Webhooks
Signed outbound webhooks from Links and Sign, and email delivery for Forms submissions.
View as MarkdownLinks and Sign can POST JSON to your HTTPS endpoint when events happen — link
clicks and conversions, document signatures and completions. Every delivery is
signed with HMAC-SHA256 in an X-Easerix-Signature header so you can verify
it came from Easerix. Forms delivers submissions to email destinations today.
Registering a webhook
Links and Sign share the same registration API:
curl -s https://api.easerix.com/links/v1/webhooks \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/hooks/easerix", "events": ["link.created", "link.clicked"]}'The response includes the webhook and a signing secret (whsec_...) —
shown once, so store it immediately. The URL must be HTTPS. Manage
webhooks with GET /v1/webhooks, PATCH /v1/webhooks/:id (change url,
events, or active), and DELETE /v1/webhooks/:id.
| Links | Sign | |
|---|---|---|
| Base URL | https://api.easerix.com/links | https://api.easerix.com/sign |
| Default events | link.created, link.updated, link.deleted | * (all events) |
Wildcard * subscription | No | Yes |
| Test delivery | POST /v1/webhooks/:id/test sends a ping | Not available |
| Webhooks per account | Unlimited | 10 |
Verifying signatures
Every delivery carries these headers:
| Header | Value |
|---|---|
X-Easerix-Event | The event name, e.g. link.clicked |
X-Easerix-Signature | sha256=<hex HMAC-SHA256 of the raw request body> |
X-Easerix-Delivery | Unique delivery ID, stable across retries (Links only) |
Compute the HMAC of the raw request body with your whsec_... secret and
compare:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}Reject anything that doesn't verify. On Links, use X-Easerix-Delivery to
deduplicate retried deliveries.
Links events
The payload envelope is always:
{ "event": "link.clicked", "at": "2026-08-09T17:30:00Z", "data": { ... } }| Event | data contains |
|---|---|
link.created / link.updated / link.deleted | {id, shortCode, destinationUrl, title, active, archived} |
link.clicked | {link: {id, shortCode}, clicks, totalClicks} |
link.converted | {link: {id, shortCode}, conversion: {id, name, amountCents, currency}, clickId, clickedAt, convertedAt} |
link.clicked is aggregated: rapid clicks on the same link are batched
into one event with a clicks count, so don't assume one event per click —
totalClicks is the link's running total.
Sign events
Sign payloads are flat (no data wrapper):
{
"event": "document.signed",
"documentId": "doc_...",
"document": { "id": "doc_...", "name": "MSA — Acme", "status": "in_progress" },
"occurredAt": "2026-08-09T17:30:00Z",
"recipient": { "id": "...", "name": "...", "email": "...", "role": "signer", "status": "signed" }
}| Event | Fires when |
|---|---|
document.sent | A document is sent for signature |
document.viewed | A recipient opens the document |
document.signed | A recipient signs |
document.declined | A recipient declines |
document.completed | Everyone has signed |
document.voided | The sender voids the document |
document.expired | The document passes its expiry |
recipient is present only on the recipient-scoped events (viewed,
signed, declined).
Delivery and retries
Both tools deliver with a 10-second timeout and treat any 2xx as success. A
failed delivery is retried twice (Links waits ~1s then ~5s; Sign waits 5s then
20s). After 20 consecutive failures a webhook is automatically disabled —
re-enable it with PATCH /v1/webhooks/:id {"active": true}, which also resets
the failure count. Respond quickly (queue the work, return 200 immediately)
to stay under the timeout.
Forms: email destinations
Forms doesn't send outbound webhooks yet — email is the delivery channel.
Each form has destinations managed at
https://f.easerix.com/v1/forms/:id/destinations; an email destination with
config {"to": ["[email protected]"]} receives a branded notification for every
non-spam submission, with Reply-To set to the submitter when their email is
detected. Forms can also send an autoresponder to the submitter if you enable
it on the form.
To get form submissions into your own systems today, poll
GET /v1/forms/:id/submissions on the Forms API.
Frequently asked questions
Are deliveries replayed in order?
No ordering guarantee — treat each event independently and use the payload's
timestamp (at / occurredAt) rather than arrival order.
My endpoint was down — did I lose events?
Each delivery is retried twice, then dropped. After 20 consecutive failures the webhook is disabled entirely, so re-enable it and reconcile via the REST API after an outage.
Can I rotate the signing secret?
Delete the webhook and create a new one — a fresh secret is generated and returned once on creation.
Last updated on