Skip to main content
The SpitShake Identity Engine lets a tenant who has already verified a signer’s identity (typically via Stripe Identity) hand that verification to SpitShake, so the signer lands on a one-tap Consent Modal instead of drawing a signature. SpitShake is stateless with respect to the identity check — it never calls Stripe itself.

Architecture

1

Tenant verifies

The tenant (e.g. a Customs broker, a payroll processor) runs their own Stripe Identity verification. On success, Stripe returns the verified legal name and a verification session ID (iv_…).
2

Tenant mints a handoff JWT

The tenant server-side mints an HS256 JWT signed with the identity_handoff_secret on their SpitShake account. Payload includes verified_name, stripe_verification_id, and a short exp (15 min recommended).
3

Signer lands at the signing URL with the token

The tenant redirects the signer to https://your-instance.com/s/:slug?t=<jwt>.
4

SpitShake verifies + shows Consent Modal

SpitShake decodes the JWT using the tenant’s configured secret, renders a legal-wording Consent Modal with the verified name interpolated, and waits for the single “Confirm & Sign” click.
5

Single click applies signatures to all fields

On click, SpitShake overrides every signature and initials field server-side with the verified name, writes the intent.verified audit entry, applies the per-page PDF audit footer, and dispatches the signing.complete webhook back to the tenant.

Configuring a tenant for identity-bound signing

Four one-time setup steps:
  1. Set the tenant display and legal names
    The signing page header becomes Acme SecureSign; the per-page PDF footer reads Executed for Acme LLC.
  2. Rotate (create) the handoff secret
    The response body contains identity_handoff_secretstore this immediately in your tenant’s secret store; it is not retrievable again.
  3. Subscribe to the signing.complete webhook Add a webhook URL on Settings → Webhooks and include signing.complete in the subscribed events.
  4. (Optional) Configure post-signature instructions
    Elements with a data-copy-value attribute render a click-to-copy button on the completion screen.

Handoff JWT payload

Signed with HS256 using the secret returned from POST /api/settings/identity_handoff_secret/rotate. SpitShake enforces iat_leeway: 60 and exp_leeway: 5 to handle clock skew.

Example: minting a handoff JWT (Ruby)

Standard (non-identity-bound) signing still works

SpitShake is verification-agnostic. If a signer lands at /s/:slug without a t= parameter, the standard signing flow runs unchanged — manual signature pad, no consent modal, no per-page footer, no signing.complete webhook. This is intentional dual-mode design: use identity handoff when the tenant has a verified identity, use manual signing when they don’t.

What lands on the PDF

When identity-bound:
  1. Every signature and initials field is stamped with the verified name.
  2. Each page of the signed PDF gets a footer strip: Executed by SpitShake Identity Engine. Identity verified via Stripe ID: iv_… . Executed for {legal_name}.
  3. The appended audit trail page includes an “Authentication & Intent Record” block with the Stripe ID and one-click affirmation timestamp.

The signing.complete webhook

Fired only when a submission was identity-bound. Payload shape:
Use this to reconcile the signer against your Stripe records on your side.

Security notes

  • Rotate immediately if leaked. The secret grants the tenant the power to assert identity to SpitShake. Rotate via POST /api/settings/identity_handoff_secret/rotate — the old secret is invalidated the moment the new one is stored.
  • SpitShake never returns the stored secret. GET /api/settings/white_label shows only identity_handoff_configured: true/false.
  • Client-supplied signatures are ignored in identity-bound mode. The server authoritatively substitutes the JWT’s verified_name into every signature/initials field before persistence, so a compromised client cannot forge a different name.
  • intent_verified: true is required on the identity-bound submit request. Missing or false → 422.