Documentation
Webhooks
Get a signed HTTP call when a document is sent, viewed, signed, declined or expires, and verify it.
Register an endpoint in Developers → Webhooks, choose the events, and Docustay posts a JSON event to it each time one happens. Each endpoint has a secret that starts whsec_, shown once.
Verify every delivery
Deliveries carry the Standard Webhooks headers webhook-id, webhook-timestamp and webhook-signature (v1,<base64 HMAC-SHA256 of "id.timestamp.body">). Verify against the raw body, not re-serialised JSON.
import { verifyWebhook } from "@docustay/sdk";
const event = await verifyWebhook(process.env.DOCUSTAY_WEBHOOK_SECRET, req.headers, rawBody);
from docustay import verify_webhook
event = verify_webhook(secret, headers, raw_body)
Both refuse a changed body, a wrong secret and a timestamp more than five minutes old.
A receiver using any Standard Webhooks library passes whsec_ plus the secret with - swapped for + and _ for /.
Trying it from your laptop
Docustay can only send to a public https:// address: localhost, 127.0.0.1 and office or home network addresses are refused when you add the endpoint, and the message says so. While you build the receiver, give your laptop a public address with a tunnel tool, add that address as the endpoint, and use Send again in the delivery log to replay an event as often as you like. When the receiver is deployed, replace the address.
Delivery
Answer with any 2xx quickly. A delivery that fails is retried, and every attempt is in the delivery log under the endpoint, where a delivery can be replayed. Deliveries can arrive more than once and out of order: use the webhook-id to ignore a repeat, and fetch the document (GET /api/v1/documents/{id}) rather than trusting the event as the final word.
The events carry facts about the document, never a signing link or a code.
Which events
You choose per endpoint. The ones most receivers want are documents.document.sent, documents.document.opened, documents.document.signed (one person signed), documents.document.executed (everyone signed and the sealed copy exists), documents.document.declined, documents.document.expired and documents.document.voided. A receiver that only cares about finished documents subscribes to documents.document.executed and nothing else.
A receiver, step by step
- Read the raw request body as bytes before any JSON parser touches it.
- Verify the three headers against that body with the SDK, as above. If verification throws, answer
400and stop. - Look at
webhook-id. If you have processed it, answer200and stop; this is a repeat. - Queue the work (fetch the document, save the signed copy) and answer
200at once. Do slow work after you have answered.
curl https://docustay.app/api/v1/documents/DOCUMENT_ID/signed.pdf \
-H "Authorization: Bearer $DOCUSTAY_KEY" -o signed.pdf
The delivery log
Each endpoint's page shows a delivery log with the event, the result your server gave and which try it was. Send again replays a delivery, so you can fix your receiver and try again without making another document. Repeats carry the same webhook-id, which is why your receiver should ignore ids it has already handled.