Register a new webhook
Register a new webhook to receive event notifications.
About Webhooks
Webhooks allow Formify to communicate with your system using callbacks triggered by specified Formify events. Webhooks provide real-time data updates, enabling you to integrate Formify events directly into your workflows.
What’s the use for webhooks?
With webhooks, you can be notified of specific events to automate workflows such as sending documents for signature or tracking their status in real-time.
How do I set up webhooks?
When you create a webhook, specify the events you'd like to subscribe to. To reduce unnecessary requests to your server, only subscribe to the events you plan to handle.
Entity Types
Formify uses entity types to filter webhook events based on the scope of data you want to receive. The entity type defines which object changes in Formify trigger your webhook.
| Entity Type | Description | Example |
|---|---|---|
| Document | Subscribe to events involving a specific document. | A document is sent for signature, signed, or completed (sealed). Each change triggers your webhook. |
Supported Events
- document.created: A document has been created/sent for signature.
- document.opened: A document has been opened.
- document.signed: A document has been signed by a signee.
- document.completed: A document process has been signed by all parties and is sealed.
- document.revoked: A document has been revoked.
- document.deleted: A document has been deleted.
Notes
- Other entity types (e.g., settings.x) are not yet supported.
- Do not assume ordering: Events may arrive late, more than once, or out of order
Webhook Request Payload
- See the DocumentWebhookEvent schema for the structure of the Document entity type payload.
Example for General Events (document.created, document.opened, etc.)
{
"event": {
"eventId": "123e4567-e89b-12d3-a456-426614174000",
"type": "document.created",
"date": "2024-10-25T14:23:55Z",
"data": {
"documentId": "789e4567-e89b-12d3-a456-426614174111",
"accountId": "456e4567-e89b-12d3-a456-426614174222",
"userId": "123e4567-e89b-12d3-a456-426614174333"
}
}
}
- eventId: The unique identifier for the event instance.
- type: The type of the event that triggered the webhook.
- date: The date and time the event was triggered in ISO 8601 format.
- data: Contains the documentId, userId and accountId relevant to the event.
Example for document.signed Event
{
"event": {
"eventId": "123e4567-e89b-12d3-a456-426614174000",
"type": "document.signed",
"date": "2024-10-25T14:23:55Z",
"data": {
"documentId": "789e4567-e89b-12d3-a456-426614174111",
"accountId": "456e4567-e89b-12d3-a456-426614174222",
"userId": "123e4567-e89b-12d3-a456-426614174333",
"signeeId": "789e4567-e89b-12d3-a456-426614174555"
}
}
}
Notes
- All event payloads include eventId, type, date, and a data object with event-specific details.
- The data object may differ between events and the signeeId field is only included for the document.signed event.
Expected Response for Webhooks
To acknowledge receipt of the webhook, your server should respond with an HTTP status code in the 2xx range. No specific content is required in the response body; a 200 OK status alone will indicate successful processing.
Example Response
HTTP/1.1 200 OK
Content-Type: application/json
Retry Attempts
Formify will attempt to deliver webhook events up to 5 times if the initial attempt fails. The retry intervals* are as follows:
- First retry: "immediately"
- Second retry: after ~5 minutes
- Third retry: after ~30 minutes
- Fourth retry: after ~2 hours
- Fifth retry: after ~24 hours
*The intervals are not exact and may vary slightly. After 5 retries, the event will not be sent again.
Formify must receive a success status code (2xx) from your server to acknowledge the event. If a success status code is not received, Formify will retry sending the event according to the intervals specified above.
Each event sent by Formify includes a unique eventId. You can use this eventId to ensure that your system processes each event only once and to avoid handling duplicate events.
Security and Reliability
To secure your webhook endpoint:
- Use HTTPS: Always use HTTPS URLs for your webhook endpoint to encrypt data in transit
- Verify webhook signatures: Use HMAC-SHA256 to verify that requests are authentic (see Webhook Signature Verification below)
- Use eventId for idempotency: Store processed eventId values to safely ignore duplicate deliveries
Webhook Delivery Headers
Every webhook delivery includes the following headers:
| Header | Type | Description |
|---|---|---|
| Content-Type | string | Always application/json |
| X-Formify-Timestamp | string | Unix timestamp (seconds) of when the event was sent |
| X-Formify-Signature | string | Lowercase hexadecimal HMAC-SHA256 digest of {timestamp}.{raw_body} |
Webhook Signature Verification
Formify signs all webhook payloads using HMAC-SHA256 so you can verify that requests are authentic. Each webhook endpoint is assigned a unique signing secret (prefixed with whsec_) upon creation.
How to verify:
The signature is computed over the string {timestamp}.{raw_body} using your webhook's signing secret as the HMAC key.
Example (Node.js with Express):
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.headers['x-formify-timestamp'];
const signature = req.headers['x-formify-signature'];
const secret = 'whsec_...'; // your signing secret
const signedPayload = `${timestamp}.${req.body}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
const isValid = crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(signature)
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process the event...
res.status(200).send('OK');
});
Important: Use the raw request body exactly as received when verifying the signature. Do not verify against a re-serialized JSON payload.
Signature format: The X-Formify-Signature header contains the lowercase hexadecimal HMAC-SHA256 digest of:
{timestamp}.{raw_body}
Timestamp verification: You should also verify that the X-Formify-Timestamp value is recent to reduce replay attacks.
Testing Webhooks
For local development, you can use tools such as ngrok to expose your local server to the internet and receive webhook calls from Formify.
Example:
ngrok http 3000
This gives you a public HTTPS URL that you can register as your webhook endpoint for testing.
You can also use request inspection tools such as:
- webhook.site — Inspect webhook payloads without writing code
- requestbin.com — Capture and inspect HTTP requests
These tools are useful for development and debugging before you build your own webhook handler.
Typical test flow
- Register a webhook endpoint
- Trigger an event, for example by creating or signing a document
- Verify that the webhook payload is received
- Return a 2xx response from your server
Note: These tools are useful for development and debugging. Production webhook endpoints should use your own secure, publicly accessible HTTPS endpoint.
Idempotency
Webhooks may be delivered more than once due to network issues or retries. To handle this:
// Example: Node.js/Express webhook handler
app.post('/webhook', async (req, res) => {
const { event } = req.body;
const eventId = event.eventId;
// Check if we've already processed this event
const alreadyProcessed = await db.checkEventId(eventId);
if (alreadyProcessed) {
console.log(`Event ${eventId} already processed, skipping`);
return res.status(200).send('OK');
}
// Store eventId to prevent duplicate processing
await db.storeEventId(eventId);
// Process the event
await handleEvent(event);
res.status(200).send('OK');
});
Debugging
Common Issues:
| Issue | Cause | Solution |
|---|---|---|
| Webhook not received | URL incorrect or server down | Verify URL is accessible from the internet |
| Webhook retrying repeatedly | Server not returning 2xx status | Check server logs, ensure 200/201/204 response |
| Duplicate events | Not tracking eventId | Implement idempotency using eventId |
| Events out of order | Network delays | Don't rely on event order; use timestamps |
Logging:
- Log all incoming webhook requests with timestamp, eventId, and event type
- Log your server's response status code
Monitoring:
- Track webhook delivery success rate in Formify dashboard (if available)
- Set up alerts if webhook endpoint returns errors consistently
- Monitor processing time to ensure webhooks are handled quickly
Request
- Base URL: https://docs-api.formify.eu/v1
- URL: https://docs-api.formify.eu/v1/webhooks
- Auth: OAuth 2 (authorization code, authorization URL https://docs-api.formify.eu/v1/oauth/authorize, token URL https://docs-api.formify.eu/v1/oauth/token)
Request body
Response
Webhook registered successfully.
Example response
{
"signingSecret": "whsec_dGhpcyBpcyBhIHNhbXBsZSBzZWNyZXQ="
}Changes
No recorded changes to this operation across all 1 revision of this API.