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:-
Set the tenant display and legal names
The signing page header becomes
Acme SecureSign; the per-page PDF footer readsExecuted for Acme LLC. -
Rotate (create) the handoff secret
The response body contains
identity_handoff_secret— store this immediately in your tenant’s secret store; it is not retrievable again. -
Subscribe to the
signing.completewebhook Add a webhook URL on Settings → Webhooks and includesigning.completein the subscribed events. -
(Optional) Configure post-signature instructions
Elements with a
data-copy-valueattribute 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:- Every
signatureandinitialsfield is stamped with the verified name. - 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}. - 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:
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_labelshows onlyidentity_handoff_configured: true/false. - Client-supplied signatures are ignored in identity-bound mode. The server authoritatively substitutes the JWT’s
verified_nameinto every signature/initials field before persistence, so a compromised client cannot forge a different name. intent_verified: trueis required on the identity-bound submit request. Missing or false → 422.
Related
POST /api/settings/identity_handoff_secret/rotate— rotate the secret.GET /api/settings/white_label— read current configuration.PUT /api/settings/white_label— update tenant branding and post-sign copy.- Webhooks guide — subscribe to
signing.complete.