Webhooks

Deliver io.sign event notifications to URLs you control

The webhooks service delivers event notifications to URLs you control. It runs as its own process, separate from the four signing-service components described in Configuring io.sign, and listens on port 3010 by default. When a subscribed event fires, the service sends a POST with a JSON event payload to your configured HTTPS URL.

Manage subscriptions through the REST API. See Create a webhook subscription, List webhook subscriptions, and Delete a webhook subscription. A subscription belongs to an organisation and specifies its events and destination URL. Set vaultId to restrict delivery to a particular vault, or omit it for organisation-wide delivery.

Supported events

Event familySupported event types
Signaturessignature.created, signature.presigning, signature.signing, signature.completed, signature.rejected, signature.failed, signature.expired, signature.cancelled
Resharesreshare.created, reshare.presigning, reshare.signing, reshare.completed, reshare.rejected, reshare.failed, reshare.expired, reshare.cancelled
Vaultsvault.created, vault.activated, vault.archived, vault.unarchived, vault.deleted
Signerssigner.created

Set events to ["*"] to receive every event type.

Authenticating deliveries

When signing is configured for your deployment, io.sign signs every delivery with a hash-based message authentication code using SHA-256 (HMAC-SHA256), following Standard Webhooks v1. Your receiver uses the subscription's secret to verify the request body and signed headers before processing the event.

Start with Set up webhook signing to enable signing, create a subscription, retrieve its secret, and check your receiver. Use Verify webhook signatures for the Node.js example, replay protection, and secret rotation.

Confirm with your operator whether signing is active, and record that requirement in your receiver's configuration. Once enabled, reject requests that lack any signature header. A deployment without a signing root key sends unsigned deliveries; the presence or absence of headers must never decide whether your receiver verifies a request.

Keep the endpoint on HTTPS and store subscription secrets in your secret manager.

Delivery retries

The built-in delivery worker makes up to six attempts per delivery by default: the initial attempt and five retries. A successful HTTP 2xx response completes the delivery. Non-2xx responses, including 4xx and 5xx, and network failures or timeouts use the retry budget. Redirects are not followed and count as failed attempts.

The default delays before retries are 10 seconds, 30 seconds, 120 seconds, 600 seconds, and 3600 seconds. Each delay starts after the failed attempt and includes an additional random delay of up to 20% to spread retries over time. Worker polling and load can delay the next attempt further. The worker does not use the receiver's Retry-After header to schedule retries.

Configure retries

For self-hosted deployments, set these environment variables on the webhooks service and restart or redeploy it. They apply to the built-in worker in WEBHOOKS_DELIVERY_MODE=direct, which is the default mode. An external delivery system used with intent mode has its own retry policy. For io.finnet-managed deployments, contact io.finnet about the effective settings for your environment.

VariableDefaultBehaviour
WEBHOOKS_MAX_ATTEMPTS6Total attempt budget, including the initial attempt. Set to 1 to disable automatic retries after a failed attempt.
WEBHOOKS_BACKOFF_SECONDS10,30,120,600,3600Comma-separated positive integers giving the delay before each retry, in seconds. If further retries remain after the list ends, the worker reuses the final delay. Each delay receives up to 20% additional random delay.
WEBHOOKS_REQUEST_TIMEOUT_MS10000Timeout for an HTTP delivery attempt, in milliseconds.
WEBHOOKS_POLL_INTERVAL_MS1000Delay between worker polls when the previous batch was not full, in milliseconds.
WEBHOOKS_BATCH_SIZE25Maximum deliveries claimed and processed concurrently in one batch per worker.
WEBHOOKS_LEASE_TTL_MS60000Time before an attempt left in progress can be reclaimed, in milliseconds.

For example, this configuration allows four attempts total, with retry delays of 30 seconds, 120 seconds, and 600 seconds, plus the additional random delay:

WEBHOOKS_MAX_ATTEMPTS=4
WEBHOOKS_BACKOFF_SECONDS=30,120,600
WEBHOOKS_REQUEST_TIMEOUT_MS=10000

These are deployment settings. The subscription API does not expose per-subscription retry overrides. The service stores the attempt budget when it creates each delivery, so changing WEBHOOKS_MAX_ATTEMPTS affects new deliveries. Existing deliveries retain their stored limit, including when manually redelivered.

Keep both WEBHOOKS_REQUEST_TIMEOUT_MS and WEBHOOKS_POLL_INTERVAL_MS below WEBHOOKS_LEASE_TTL_MS; invalid combinations fail configuration validation. If a worker stops during an attempt, recovery counts that attempt against the budget and waits at least another lease interval before retrying.

Inspect and redeliver failed deliveries

Once the attempt budget is exhausted, the delivery has status dead and automatic retries stop. Inspect delivery results and individual attempts to diagnose the failure.

After correcting the problem, use Re-queue a delivery for immediate retry. Manual redelivery keeps the delivery identifier and attempt history, resets the attempt counter, and queues the delivery again. It is unavailable while that delivery is already in progress.

Your receiver must still deduplicate on webhook-id. Signed retries keep the same delivery identifier but receive a fresh timestamp and signature for each attempt. See Prevent replay and duplicate processing.

Deployment

Route /v1/webhooks to the webhooks service. The web console proxies this path through WEBHOOKS_UPSTREAM (see Web console). The upstream on-premise guide does not yet cover the webhooks service's image, migrations, or database setup; obtain those deployment details from io.finnet.

Configure signing on the webhooks service itself using the variables in Webhook signing configuration.


Did this page help you?