webhooks

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 TypeDescriptionExample
DocumentSubscribe 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:

HeaderTypeDescription
Content-TypestringAlways application/json
X-Formify-TimestampstringUnix timestamp (seconds) of when the event was sent
X-Formify-SignaturestringLowercase 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:

These tools are useful for development and debugging before you build your own webhook handler.

Typical test flow

  1. Register a webhook endpoint
  2. Trigger an event, for example by creating or signing a document
  3. Verify that the webhook payload is received
  4. 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:

IssueCauseSolution
Webhook not receivedURL incorrect or server downVerify URL is accessible from the internet
Webhook retrying repeatedlyServer not returning 2xx statusCheck server logs, ensure 200/201/204 response
Duplicate eventsNot tracking eventIdImplement idempotency using eventId
Events out of orderNetwork delaysDon'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
post/webhooks

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

webhookUrlstring uri

The URL where webhook events will be sent.

namestring

The name/alias of the webhook for you to identify it.

eventTypesstring[]

A list of event types that the webhook will be subscribed to.

Response

Webhook registered successfully.

webhookIdstring uuid

The unique identifier of the registered webhook.

eventTypesstring[]

A list of event types the webhook is subscribed to.

signingSecretstring

HMAC signing secret for this webhook. Store this securely — it is used to verify webhook payloads. Prefixed with whsec_.

Example response

{
  "signingSecret": "whsec_dGhpcyBpcyBhIHNhbXBsZSBzZWNyZXQ="
}

Changes

No recorded changes to this operation across all 1 revision of this API.