API reference · Webhooks
Events to sync your system
Receive status changes in your backend without relying only on polling. Configure endpoints from the Enterprise panel.
Signed
HMAC SHA-256
Retryable
Up to 8 attempts
Persistent
History per endpoint
Available events
signer.enrolledsigning_session.createdsigning_session.signedEnvelope
Events always use this shape. Persist `id` before processing so a retry does not execute your business logic twice.
jsonFirmARDigital API
{
"id": "evt_01J...",
"type": "signing_session.signed",
"api_version": "2026-07-01",
"created_at": "2026-08-01T12:00:00.000Z",
"data": { "document_id": "doc_123", "status": "SIGNED" }
}Request headers
| Header | Uso |
|---|---|
| x-firmar-event-id | Idempotent event identifier. |
| x-firmar-event-type | Type of the delivered event. |
| x-firmar-timestamp | Unix timestamp in seconds used in the signature. |
| x-firmar-signature | Format `t=<timestamp>,v1=<hmac-sha256>`. |
Verify the signature
Use the raw body, not serialized JSON, and compare with a constant-time function.
javascriptFirmARDigital API
const rawBody = await request.text();
const signature = request.headers.get("x-firmar-signature") ?? "";
const timestamp = request.headers.get("x-firmar-timestamp") ?? "";
const signed = `${timestamp}.${rawBody}`;
const expected = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(signed)
.digest("hex");
const [, version] = signature.split(",");
const received = version?.replace("v1=", "") ?? "";
if (
received.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))
) {
throw new Error("Firma de webhook inválida");
}200
Signing completed event
jsonFirmARDigital API
{
"id": "7d920246-1d14-4f43-a194-2f0c2dc7018c",
"type": "signing_session.signed",
"apiVersion": "2026-07-31",
"createdAt": "2026-08-01T12:00:00.000Z",
"data": {
"documentId": "cme0tc1tw0003s6a1c4g2n8fj",
"status": "SIGNED"
}
}