Skip to content

Webhooks and REST API Integration

Kunal Priyadarshi edited this page Oct 3, 2026 · 1 revision

Webhooks and REST API integration

The document API lets another application read or change documents. A webhook sends an HTTP callback to your receiver when an event is processed. Subscription setup and actual background delivery are separate concerns.

Configure a webhook in the application

  1. Open /settings/integrations → Webhooks.
  2. Create a subscription with a clear name and a public HTTPS receiver URL.
  3. Select only the events your receiver needs.
  4. Add filters or select payload fields if needed, using the event catalog to check field names.
  5. Save the subscription and store its one-time signing secret in an approved secret manager.
  6. Use the test action and inspect the result.
  7. Test an actual domain event and confirm delivery with the operator.

The inspected Worker entry point does not wire the outbox dispatch and retry services to automatic background processing. A successful synthetic test does not prove real events will be delivered automatically. Ask your operator to confirm those processors before relying on callbacks.

Subscription API

Prefix these paths with /api/v1/webhooks:

Method Suffix Scope Purpose
GET /events Public catalog Event definitions, sample data, and supported fields
GET /, /{id} webhooks:read List / retrieve subscriptions
POST / webhooks:manage Create a subscription; secret returned once
PATCH /{id} webhooks:manage Update configuration or active/disabled state
DELETE /{id} webhooks:manage Soft-delete a subscription
POST /{id}/rotate-secret webhooks:manage New secret, with a 24-hour previous-key grace window
POST /{id}/test webhooks:manage Synthetic delivery with optional eventType, dataOverrides
GET /{id}/metrics webhooks:read Summary and stored buckets; format=csv export
GET /{id}/dead-letters webhooks:read Terminal failed deliveries
POST /deliveries/{id}/replay webhooks:replay Create and attempt another delivery generation

Creation input:

{
  "name": "Example document callback",
  "targetUrl": "https://receiver.example.com/graphsign",
  "eventTypes": ["document.created", "document.updated"],
  "filterRules": {"and":[{"field":"data.folder","op":"eq","value":"Examples"}]},
  "payloadProjection": {"mode":"CUSTOM","includeFields":["document_id","document_name","status","folder"]},
  "rateLimitPerMinute": 10
}

name is required (1–255 characters), targetUrl is required (up to 1,024 characters), and at least one event is required. Optional properties include description (500 characters), custom headers, filters, and projection. Rate configuration accepts 1–1,000 with default 10, but current delivery code does not enforce that throttle. Protect your receiver independently.

Use public HTTPS targets for production. Do not configure private infrastructure or place credentials in the target URL. Keep custom header secrets private. The application protects its signature and delivery headers against custom-header replacement.

Event catalog

The registry includes document.created, document.updated, document.deleted, document.sent, document.viewed, document.signed, document.completed, document.declined, document.voided, document.expired, document.verified, user.created, user.updated, user.role_changed, and user.removed.

The event catalog is a schema registry, not a guarantee that every application operation emits every event. Confirm your actual producer path and deployed processing. A wildcard * is accepted, but explicit events reduce unwanted traffic and data disclosure.

Filters support and/or groups and predicates with field, op, and optional value. Operators include eq, neq, in, nin, contains, and exists; nested filters are depth-limited. CUSTOM projection selects event data fields; the standard envelope is still included by fan-out.

Callback envelope and headers

Normal callbacks contain id, event, schemaVersion, timestamp, organisationId, sequence, test, and data. Header names are case-insensitive:

Header Meaning
X-Signature sha256= followed by the lowercase HMAC-SHA256 hex signature
X-Signature-Key-ID Signing key identifier used to select the stored secret
X-Event-ID Stable event identity
X-Delivery-ID Particular delivery identity
X-Attempt-Number Attempt number for this delivery

Verify before processing

Compute HMAC-SHA256 over the exact raw request body bytes, using the secret's literal UTF-8 text as the key. Do not hex-decode the secret and do not parse/re-serialize the body before checking it. Compare signatures in constant time. Reject missing or invalid signatures.

This Node.js helper illustrates the comparison only; your HTTP framework must pass the unmodified raw body and headers. Store secrets outside source code.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyWebhook(rawBody, signatureHeader, secret) {
  if (!Buffer.isBuffer(rawBody) || typeof signatureHeader !== 'string') return false;
  if (!/^sha256=[0-9a-f]{64}$/.test(signatureHeader)) return false;
  const expected = createHmac('sha256', secret).update(rawBody).digest();
  const received = Buffer.from(signatureHeader.slice(7), 'hex');
  return received.length === expected.length && timingSafeEqual(received, expected);
}

After verification, validate the event schema, expected organization, and supported event type. Deduplicate by event ID using durable storage before applying business effects. Handle test: true without changing production records. An HMAC alone does not prevent replay, and callbacks should not be treated as ordered or exactly-once.

Return a 2xx response after durably accepting the event. Avoid a slow synchronous workflow in the receiver. Keep previous and new secrets available during the documented rotation window, selecting by key ID.

Failures and replay

The delivery service treats 2xx as success and schedules eligible retries with backoff, up to four total attempts. Background execution requires a processor. Terminal failures enter the dead-letter queue. Fix the receiver or configuration before replaying, and ensure replay cannot duplicate a business action.

Metrics summarize stored buckets; the current period parameter is echoed but does not filter those buckets. Do not interpret it as a guaranteed time-window query.

API client bindings and logs

Bindings use /api/v1/organisations/me/api-clients with api_clients:manage:

  • GET lists bindings.
  • POST registers required name, clientId, and nonempty scopes; optional description, issuer, and actingUserId are supported.
  • PATCH /{id} updates name, description, scopes, or status (active, disabled, revoked).
  • DELETE /{id} revokes and soft-deletes the binding.

Registration does not issue an OAuth token or client secret. No corresponding client-secret rotation or client-credentials token endpoint is implemented here. Get approved token provisioning instructions from the operator.

GET /api/v1/organisations/me/api-logs requires api_logs:read and supports page/limit plus route, method, statusCode, requestId, from, and to. Use request IDs for private troubleshooting; review exports before sharing.

Next: API Quick Start · REST API Reference · Feature Availability.

Clone this wiki locally