Skip to main content
Firma sends webhook events to notify your application about signing lifecycle changes, document completions, and workspace activities. Webhooks enable real-time integrations without polling.

Common use cases

  • Send internal notifications when documents are signed
  • Update your database when signing requests are completed
  • Trigger downstream workflows (invoicing, provisioning, etc.)
  • Track signing request status changes in real-time

Event types

Firma sends the following event types:

Signing Request Events

  • signing_request.created - New signing request created
  • signing_request.sent - Signing request sent to recipients
  • signing_request.viewed - Recipient viewed the document
  • signing_request.completed - All recipients finished signing
  • signing_request.expired - Signing request expired
  • signing_request.cancelled - Signing request was cancelled
  • signing_request.updated - Signing request metadata updated
  • signing_request.deleted - Signing request deleted (before sending)
  • signing_request.certificate.generated - Signing certificate generated
  • signing_request.reminder.sent - Reminder sent to recipients

Recipient Events

  • signing_request.recipient.signed - Recipient completed signing
  • signing_request.recipient.declined - Recipient declined to sign
  • signing_request.recipient.identity_changed - Recipient changed their identity (name, company, etc.) during signing

Template Events

  • template.updated - Template modified
  • template.used - Template used to create a signing request

Workspace Events

  • workspace.created - New workspace created
  • workspace.updated - Workspace modified

Domain Events

  • domain.verified - Domain verified successfully
  • domain.verification.failed - Domain verification failed

Two levels of enablement

Webhook delivery requires two switches to both be on: a per-webhook switch and an account-level master switch. The two are independent, so a webhook can be created and enabled correctly while never firing a single event. At least one scope (company or workspace) must have its master switch on for any webhook under that scope to deliver. Turning on the workspace master switch also generates a signing secret for that workspace if one doesn’t already exist.
POST /webhooks/{id}/test bypasses the master switch. A test delivery can succeed even while the master switch is off and live events are not being delivered - a passing test is not confirmation that real events will fire.
When the master switch is off, Firma skips the event before logging a delivery attempt. consecutive_failures stays at 0 and the webhook shows no failure history, even though nothing is being delivered - there’s no error to alert you.

Permissions

Toggling the company-level master switch requires owner or admin access to the company; view-only members cannot enable or disable it. Attempting to toggle it without sufficient permissions returns a permission error.

Creating a webhook

Create webhooks via the API or dashboard:
Your webhook URL must use HTTPS and respond within 5 seconds. Use POST /webhooks/{id}/test after creation to verify your endpoint is receiving events correctly.

Webhook payload structure

All webhook events follow this standard structure:

Security: Signature verification (Required)

Always verify webhook signatures to prevent spoofing attacks. Do not process webhooks without signature verification.
Firma signs all webhook requests using HMAC SHA-256. Your webhook endpoint receives these headers:
  • X-Firma-Signature - HMAC signature using current signing secret
  • X-Firma-Signature-Old - HMAC signature using previous secret (during 7-day rotation grace period)
  • X-Firma-Event - Event type (e.g., signing_request.completed)
  • X-Firma-Delivery - Unique delivery attempt ID

Signature format

Firma uses a timestamped signature header:
  • Header: X-Firma-Signature: t=1707500000,v1=abc123def456...
  • t = Unix timestamp (seconds) when the signature was generated
  • v1 = HMAC-SHA256 hex digest
  • Signed payload format: {timestamp}.{json_body}
Example:

Get your signing secret

  1. Navigate to your Firma dashboard
  2. View webhook details to retrieve the signing secret
  3. Store the secret securely (environment variable or secrets manager)

Verification example - Node.js (Express)

Verification example - Python (Flask)

Handling secret rotation

When you rotate your webhook signing secret:
  1. Firma generates a new secret
  2. For 7 days, Firma sends both signatures:
  • X-Firma-Signature (new secret)
  • X-Firma-Signature-Old (previous secret)
  1. After 7 days, only X-Firma-Signature is sent
Implementation: Check X-Firma-Signature first. If verification fails and X-Firma-Signature-Old exists, verify against the old secret.

Retry behavior

Firma automatically retries failed webhook deliveries:
  • Retry schedule: Immediate, then +5 minutes, then +1 hour
  • Total attempts: Up to 3 attempts per event
  • Timeout: Your endpoint must respond within 5 seconds
  • Success: Any 2xx status code indicates success
  • Auto-disable: After 50 consecutive failures, the webhook is automatically disabled
Best practice: Respond with 200 immediately, then process events asynchronously (queue, background job, etc.) to avoid timeouts.

Idempotency

Always handle duplicate events using the event id:

Monitoring webhook health

Monitor your webhook’s health by checking the webhook details:
Response includes:
  • consecutive_failures - Number of consecutive failed deliveries
  • last_failure_at - Timestamp of most recent failure
  • enabled - Whether webhook is active (auto-disabled after 50 failures)
  • last_success_at - Timestamp of most recent successful delivery
Rate limit: All /webhooks operations (GET and write alike) share a 60 requests per minute per API key limit. POST /webhooks/{id}/test has its own separate limit of 10 requests per minute.

Troubleshooting

Common issues

Webhooks configured but events don’t fire This is the most common cause of “my webhook isn’t working” reports. Per-webhook settings can be entirely correct while the account-level master switch is off, silently skipping every event.
  • Confirm the master switch is on for the right scope: company-level webhooks need the company’s switch on, workspace-level webhooks need that workspace’s switch on.
  • Don’t trust a passing test as proof - POST /webhooks/{id}/test bypasses the master switch, so it can succeed while live events don’t fire.
  • Check consecutive_failures on the webhook - if it reads 0 and no events show up, that also points to the master switch, since skipped events are never logged as failed delivery attempts.
  • If you can’t find or toggle the master switch, you may not have owner or admin permissions on the company; ask an owner or admin to enable it from Settings > Webhooks.
401 Unauthorized / Invalid signature
  • Verify you’re using the correct signing secret
  • Check that you’re hashing the raw request body (not parsed JSON)
  • Ensure you’re using HMAC SHA-256, not other hash algorithms
Timeouts / 504 errors
  • Respond with 200 immediately, process asynchronously
  • Check your endpoint responds within 5 seconds
  • Use background jobs/queues for heavy processing
Duplicate events
  • Implement idempotency using the event id
  • Store processed event IDs in your database
Webhook auto-disabled
  • Check consecutive_failures and recent event logs
  • Fix endpoint issues, then re-enable webhook via API or dashboard

Testing webhooks locally

Use a tunnel service like ngrok for local development:

Production checklist

  • Verify HMAC signatures on all webhook requests
  • Handle signature rotation (check both X-Firma-Signature and X-Firma-Signature-Old)
  • Respond with 200 within 5 seconds
  • Process events asynchronously (queues/background jobs)
  • Implement idempotency using the event id
  • Store signing secret securely (env var or secrets manager)
  • Monitor consecutive_failures metric via GET webhook endpoint
  • Set up alerts for webhook failures
  • Log all webhook events for debugging
  • Test with all subscribed event types
  • Stay within rate limits (See Rate Limit Guide)

Next steps