Event Notifications

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 sealed
  • envelope.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.signed and envelope.declined

signID sends a test event immediately after configuration so you can verify your endpoint is reachable and correctly parses payloads.

Note. Only one webhook URL per workspace is supported currently. If you need to fan out to multiple internal systems, do so from your own endpoint.

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 declineReason if 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 2xx in that window, the event is logged as failed but not retried
  • Failed deliveries appear in Settings → Integrations → Webhook log for troubleshooting
Note. Because there’s no automatic retry, treat webhooks as a notification, not the only source of truth. If a webhook is missed, your backend can always poll 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:

  1. Receive the webhook
  2. Verify the HMAC signature
  3. Queue the payload to your own background job system
  4. Return 200 OK
  5. 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 OK and 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:

  1. Brand and influencer agree to a deal on the Ainfluencer platform
  2. Ainfluencer’s backend calls signID’s API to create an envelope with the campaign contract
  3. Ainfluencer embeds signID’s signer UI in an iframe (see Embedded Signing) or emails the signing link
  4. Influencer signs; signID seals the document
  5. signID fires an envelope.signed webhook to Ainfluencer
  6. 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.

Back to Knowledge Base