Overview
Webhooks let your application receive real-time notifications when events happen in SpitShake. Instead of polling the API, SpitShake sends an HTTP POST request to your configured endpoint whenever a relevant event fires. Each webhook delivery includes a JSON payload describing the event, a cryptographic signature for verification, and a unique delivery ID for deduplication.Supported Events
SpitShake emits 14 event types across four resource categories:Form Events
Triggered when a submitter interacts with a signing form.Field Events
Submission Events
Triggered when the overall submission (containing one or more submitters) changes state.Template Events
Event Payloads
Every webhook delivery is an HTTP POST withContent-Type: application/json. The top-level structure is always:
Form Event Payload
All form events (form.created, form.started, form.viewed, form.completed, form.declined, form.expired) share this structure:
For
form.declined events, the declined_at field will contain a timestamp and completed_at will be null. For form.expired events, both completed_at and declined_at will be null.Submission Event Payload
Submission events include the full list of submitters and their statuses:metadata echoes the caller-provided submission metadata. Keys reserved for
SpitShake internals, currently flattening_failed, are omitted. Submitter metadata remains
excluded; use each submitter’s external_id for caller-owned correlation.
Document download URLs in payloads
Thesubmission.completed and form.completed webhook payloads include a documents array with download URLs, so you can retrieve signed PDFs directly from the event without a follow-up API call:
download_url requires authentication (Bearer token or X-Auth-Token with submissions:read scope).
Public document links
download_url needs your API token. If your integration expects a link it can fetch
without credentials (DocuSeal’s documents[].url), an account admin can turn on
Public document links in Settings → Webhooks, or via PUT /api/settings/document_links.
Each document then also carries:
- Anyone with
urlcan download the PDF, so treat it like a password. - Links expire after the account’s setting (30 days by default); set
expires_in_daystonullfor links that never expire.url_expires_atisnullfor those. - Turning links off, or
POST /api/settings/document_links/revoke, stops every link already sent. Changing the expiry only affects new links. - To mint a link on demand (with its own expiry),
POST /api/submissions/{id}/documents/{document_id}/link. - A redelivery replays the original body, including the link it carried.
Template Event Payload
Template events include metadata about the template’s structure:Field Updated Payload
Fired each time a submitter changes a field value. Includes the old and new values for change tracking:Webhook Headers
Every webhook delivery includes the following HTTP headers:HMAC Signature Verification
Every webhook includes anX-Webhook-Signature header containing an HMAC-SHA256 digest of the raw request body, signed with your webhook secret key:
Managing Webhooks via API
Create a Webhook Endpoint
Update a Webhook Endpoint
List All Webhook Endpoints
Delete a Webhook Endpoint
204 No Content on success.
Webhook Delivery Log
Every attempt to reach this endpoint, newest first: real events, retries, and sends from the test endpoint (flagged"test": true). Each entry carries the
HTTP status the endpoint answered, how long it took, and the error text when it failed.
Response:
A delivery that is missing entirely was never dispatched: check the subscription rules under
Which events actually reach you.
Redelivering a failed webhook
If a delivery failed or was exhausted, you can trigger a redeliver:delivery_uuid.
Test a Webhook Endpoint
Send a sample delivery of any single event to your registered URL, so you can exercise the handler you actually wrote before a real signer ever touches the document.200 OK
"test": true, so
GET /api/webhooks/{id}/deliveries shows what your endpoint
answered. A failed test send is logged as failed with the error text and is not
retried; the response is 422 with the same delivery_id.
-
eventmay be any event in Supported Events, including one this endpoint is not subscribed to — which is what makes it useful behind a “Test” button next to an event picker. Omit it and you get the first event the endpoint subscribes to, orform.completedif it subscribes to all. -
An unrecognised event is rejected with
422and nothing is sent:422 Unprocessable Entity -
The body is the real payload shape for that event, filled with obviously-fake
sample data (
sample.signer@example.com, template “Sample Agreement”, ids in the 900000 range). Documentdownload_urls (and publicurls, when links are on) point at your own signing domain but resolve to nothing — they are for checking your URL handling, not for downloading. - The delivery carries every standard header, so it exercises your signature verification exactly like a real one.
Sample body
Test deliveries are sent synchronously and are not recorded in the
delivery log — the HTTP response above is the result. Use
the log for real events.
Testing your integration
A path from “nothing wired up” to “confident in production”, without emailing anyone.1
Exercise your handler with a sample of each event
POST /api/webhooks/{id}/test for every event you
subscribe to. Confirm your signature check passes, your parser copes with the
shape, and you skip anything carrying "test": true.2
Run a real submission that emails nobody
Create a submission with Each submitter in the response carries a
"send_email": false. No invitation is sent, but the
submission is real and webhooks still fire normally — so you can open the
signing link yourself, complete it, and watch genuine form.* and
submission.completed deliveries arrive.signing_url you can open directly.3
Read the delivery log when something does not arrive
GET /api/webhooks/{id}/deliveries shows every attempt,
test sends included ("test": true), with the status your endpoint answered and the
error text when it did not. A delivery that is missing entirely was never
dispatched — check the subscription rules below rather than your handler.Which events actually reach you
Retry Policy
If your endpoint does not return an HTTP2xx response, SpitShake retries the delivery up to 5 times with increasing delays:
After 5 failed attempts, the delivery is marked as permanently failed. You can view failed deliveries in the webhook logs section of your account settings.
Timeout thresholds:
- Connect timeout: 5 seconds. If SpitShake cannot establish a TCP connection within 5 seconds, the attempt fails.
- Read timeout: 10 seconds. If your server does not send a complete response within 10 seconds of connection, the attempt fails.
X-Webhook-Delivery-Id, so you can use it to deduplicate deliveries on your end.
SSRF Protection
SpitShake validates all webhook URLs before delivering events. The following restrictions apply to protect against Server-Side Request Forgery (SSRF) attacks: Blocked IP ranges:
Additional restrictions:
- HTTPS required in production. HTTP URLs are only accepted in development/test environments.
- Localhost blocked. URLs containing
localhost,127.0.0.1, or[::1]are rejected. - Cloud metadata endpoints blocked. URLs targeting
169.254.169.254and similar metadata service IPs are rejected.
Best Practices
Verify signatures on every request
Verify signatures on every request
Always validate the
X-Webhook-Signature header before processing any webhook payload. Never skip verification, even in development.Use the delivery ID for deduplication
Use the delivery ID for deduplication
Store the
X-Webhook-Delivery-Id header value and check for duplicates before processing. Retries will send the same delivery ID.Respond quickly, process asynchronously
Respond quickly, process asynchronously
Return a
200 status immediately and enqueue the payload for background processing. Long-running synchronous handlers risk timeouts and unnecessary retries.Handle events idempotently
Handle events idempotently
Your webhook handler may receive the same event more than once due to retries. Design your processing logic to be safe for repeated execution.
Subscribe only to events you need
Subscribe only to events you need
Selecting specific events reduces traffic to your endpoint and simplifies your handler logic. You can always add more events later.
Monitor your endpoint health
Monitor your endpoint health
Set up alerting for elevated error rates or slow response times on your webhook handler. SpitShake will stop retrying after 5 failures.

