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 createdsigning_request.sent- Signing request sent to recipientssigning_request.viewed- Recipient viewed the documentsigning_request.completed- All recipients finished signingsigning_request.expired- Signing request expiredsigning_request.cancelled- Signing request was cancelledsigning_request.updated- Signing request metadata updatedsigning_request.deleted- Signing request deleted (before sending)signing_request.certificate.generated- Signing certificate generatedsigning_request.reminder.sent- Reminder sent to recipients
Recipient Events
signing_request.recipient.signed- Recipient completed signingsigning_request.recipient.declined- Recipient declined to signsigning_request.recipient.identity_changed- Recipient changed their identity (name, company, etc.) during signing
Template Events
template.updated- Template modifiedtemplate.used- Template used to create a signing request
Workspace Events
workspace.created- New workspace createdworkspace.updated- Workspace modified
Domain Events
domain.verified- Domain verified successfullydomain.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.
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)
Firma signs all webhook requests using HMAC SHA-256. Your webhook endpoint receives these headers:X-Firma-Signature- HMAC signature using current signing secretX-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 generatedv1= HMAC-SHA256 hex digest- Signed payload format:
{timestamp}.{json_body}
Get your signing secret
- Navigate to your Firma dashboard
- View webhook details to retrieve the signing secret
- 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:- Firma generates a new secret
- For 7 days, Firma sends both signatures:
X-Firma-Signature(new secret)X-Firma-Signature-Old(previous secret)
- After 7 days, only
X-Firma-Signatureis sent
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
Idempotency
Always handle duplicate events using the eventid:
Monitoring webhook health
Monitor your webhook’s health by checking the webhook details:consecutive_failures- Number of consecutive failed deliverieslast_failure_at- Timestamp of most recent failureenabled- 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}/testbypasses the master switch, so it can succeed while live events don’t fire. - Check
consecutive_failureson the webhook - if it reads0and 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.
- 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
- Respond with 200 immediately, process asynchronously
- Check your endpoint responds within 5 seconds
- Use background jobs/queues for heavy processing
- Implement idempotency using the event
id - Store processed event IDs in your database
- Check
consecutive_failuresand 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-SignatureandX-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_failuresmetric 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
- Create a webhook via API
- Test your webhook before going live
- Update webhook settings as needed