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.Documentation Index
Fetch the complete documentation index at: https://docs.spitshake.io/llms.txt
Use this file to discover all available pages before exploring further.
Architecture
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_…).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).Signer lands at the signing URL with the token
The tenant redirects the signer to
https://your-instance.com/s/:slug?t=<jwt>.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.
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
| Claim | Type | Required | Description |
|---|---|---|---|
iss | string | yes | Account ID of the tenant (from GET /api/settings → account.id), as a string. |
iat | integer | yes | Issue time (unix seconds). |
exp | integer | yes | Expiration time. Keep short (≤ 15 min). |
verified_name | string | yes | Legal name returned by Stripe. Applied verbatim to signature/initials fields. |
stripe_verification_id | string | yes | Stripe Identity session id (e.g. iv_…). Stamped on the PDF audit footer. |
tenant_context | object | no | Free-form metadata echoed back on webhooks. |
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.