Webhooks
Receive HTTP POST notifications when envelopes are signed or declined. HMAC-SHA256 signed payloads, fire-once delivery, and best practices for integration.
Webhooks are outbound HTTP POST requests that signID sends to your endpoint when specific events happen. Instead of polling the API to check envelope status, you configure a URL and signID notifies you when things change.
Currently supported events:
envelope.signed: all signers have completed and the envelope is sealedenvelope.declined: a signer explicitly declined
Every payload is HMAC-SHA256 signed, so you can cryptographically verify the request came from signID.
Go to Settings → Integrations → Webhooks and click Configure webhook. Enter:
- URL: the HTTPS endpoint on your backend that will receive events
- Signing secret: a shared secret used for HMAC signatures (signID generates one, save it in your backend)
- Events to subscribe to: currently
envelope.signedandenvelope.declined
signID sends a test event immediately after configuration so you can verify your endpoint is reachable and correctly parses payloads.
Fired when all signers on an envelope have completed and the sealed PDF is finalized.
Payload:
{
"event": "envelope.signed",
"timestamp": "2026-11-15T14:32:11Z",
"tenant": {
"id": "tenant_abc123",
"slug": "acme-corp"
},
"envelope": {
"id": "env_xyz789",
"title": "Partnership agreement",
"status": "signed",
"documentSha256": "a1b2c3...",
"updatedAt": "2026-11-15T14:32:10Z",
"signedPdfUrl": "https://signid.brandid.app/download/...",
"signedPdfUrlExpiresAt": "2026-11-15T15:32:10Z"
}
}
The signedPdfUrl is short-lived (typically 1 hour), so download promptly or use the API to request a fresh URL later.
Fired when a signer explicitly declines to sign. The envelope moves to status declined and the flow ends, no further signing happens.
Payload structure mirrors envelope.signed but with:
status: "declined"- No
signedPdfUrl(nothing was sealed) - Optional
declineReasonif the signer provided one - The declining signer’s identifier
Use this to update your own workflow, for example, mark a deal as fallen through, notify your sales team, or retry with a different contract.
Every webhook request includes two headers:
X-SignID-Event: the event type (e.g.envelope.signed)X-SignID-Signature: sha256=<hex>: HMAC-SHA256 of the raw request body, using your signing secret
Verify the signature in your handler before parsing the payload:
const crypto = require('crypto');
const signature = req.headers['x-signid-signature'].replace('sha256=', '');
const expected = crypto
.createHmac('sha256', SIGNING_SECRET)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected))) {
return res.status(401).end();
}
Use a constant-time comparison (like timingSafeEqual) to prevent timing attacks.
signID’s webhook delivery is fire-once, no automatic retry queue. Design your endpoint accordingly:
- signID sends one HTTP POST per event
- Connection timeout is approximately 12 seconds
- If your endpoint doesn’t respond with
2xxin that window, the event is logged as failed but not retried - Failed deliveries appear in Settings → Integrations → Webhook log for troubleshooting
GET /envelopes/{id} to get current status.Respond 200 OK (or any 2xx) as quickly as possible, ideally under 10 seconds. If you need to do heavy processing:
- Receive the webhook
- Verify the HMAC signature
- Queue the payload to your own background job system
- Return
200 OK - Process asynchronously in your background worker
Don’t do database writes, external API calls, email sends, or file downloads synchronously in the webhook handler, that path should be fast, deterministic, and always succeed.
While signID’s delivery is fire-once by default, network issues can occasionally cause duplicate deliveries (rare but possible). Make your endpoint idempotent:
- Use the envelope ID + event type as an idempotency key
- Store processed webhook events in your database
- On receipt, check if you’ve already processed this event; if so, return
200 OKand skip
This is standard webhook best practice and works well regardless of signID’s specific delivery model.
The canonical use case for signID webhooks is the marketplace pattern, exemplified by Ainfluencer:
- Brand and influencer agree to a deal on the Ainfluencer platform
- Ainfluencer’s backend calls signID’s API to create an envelope with the campaign contract
- Ainfluencer embeds signID’s signer UI in an iframe (see Embedded Signing) or emails the signing link
- Influencer signs; signID seals the document
- signID fires an
envelope.signedwebhook to Ainfluencer - Ainfluencer’s backend receives the webhook, updates the deal status to “contract signed”, releases escrow, notifies both parties, and moves the workflow forward
This pattern lets signing feel native to the host platform. The signer never sees signID’s brand front-and-center, they see Ainfluencer’s brand throughout.
Ready to send your first envelope?
Create a free signID account, or book a demo to see how it fits your team or platform.




