Webhooks
Every change reaches your system. Signed and deduplicated.
Factuarea notifies your server when an invoice changes, gets paid or fails. If you do not answer, it retries automatically.
- HMAC-SHA256 signature
- Eight attempts over more than 4 days
- Delivery replay
Compatible integrations and formats

WhatsApp

Your server
POST /webhook
200 OK
The event leaves signed. If delivery repeats, it keeps the same identifier so you process it only once.
Signed
HMAC-SHA256
Deduplication
Stable Event-Id
8 attempts
backoff + jitter
Verifying the signature
HMAC-SHA256 verification in four steps
The Factuarea-Signature header carries the moment of the delivery and the HMAC-SHA256 of t.body computed with your secret. Verifying it is easy; verifying it badly is even easier.
- 1
Sign over the raw body
The HMAC is computed over the bytes that arrived, not over the JSON your framework already parsed and serialised again.
- 2
Compare in constant time
timingSafeEqual, hash_equals or whatever your language brings to compare without leaking where it fails.
- 3
Look at the clock
The header carries t with the UNIX moment of the delivery. Reject it if more than 300 seconds have gone by.
- 4
Accept either v1
For the 24 hours after a rotation the header carries two signatures. It is enough for one of them to match.
Node · the whole verifier
import { createHmac, timingSafeEqual } from "node:crypto"; // `cuerpo` son los BYTES que llegaron, sin parsear ni volver a serializar.export function firmaValida(cuerpo, cabecera, secreto) { const partes = cabecera.split(",").map((p) => p.split("=")); const t = Number(partes.find(([clave]) => clave === "t")?.[1]); const firmas = partes.filter(([clave]) => clave === "v1").map(([, v]) => v); // Ventana de 5 minutos: una entrega capturada no vale para mañana. if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; const esperada = createHmac("sha256", secreto) .update(`${t}.${cuerpo}`) .digest("hex"); // Durante una rotación llegan dos v1. Basta con que cuadre uno. return firmas.some( (firma) => firma.length === esperada.length && timingSafeEqual(Buffer.from(firma), Buffer.from(esperada)) );}When your server does not answer
If your server goes down, we call again
Every event arrives signed and keeps the same identifier. You can retry it without processing it twice.
- Any 2xx responseDelivery closed. We keep the status code and how long you took, and that is the end of it.
- 408, 425 and 429These are the only transient 4xx responses and they are retried. Other 4xx responses are marked as permanent failures so you can fix the request and replay it later.
- 5xx, timeout or silenceInto the queue. Eight attempts in total, with growing waits: the first retry after a minute, the last one three days later.
Signed
HMAC-SHA256
Deduplication
Stable Event-Id
8 attempts
backoff + jitter
Rotating the secret
Secret rotation with a 24-hour transition
The plaintext secret is shown once only, when you create the endpoint and when you rotate it. It is then stored encrypted for signing deliveries and also as a hash for verification. Rotation causes no interruption if you follow the order.
Previous secret
New secret
During the window every delivery goes out signed twice: the header carries t, the v1 of the new secret and the v1 of the previous one. Your verifier takes the signature as good if either of the two matches.
The order that loses no event
- 1
Make your server accept both secrets at once.
- 2
Rotate, and store the new one: it is shown only once.
- 3
Deploy the new secret to your server.
- 4
Before the 24 hours are up, drop the old one.
You cannot rotate twice within five minutes
That guard rail is deliberate. Two rotations in a row would leave out the secret your server has just learnt, and that is when deliveries really do fall.
Rotation and dual signing, in the guidePublished surface
An integration that fits your stack
The OpenAPI contract, SDKs and MCP server share the same API. These figures are checked against the published surface so you can assess scope and maintenance before integrating.
- API operations
- 413
- resources
- 37
- MCP tools
- 391
- documentation pages
- 797
Compatible technologies and clients
Stripe
TypeScript
PHP
OpenAPI
Logos identify compatible technologies or clients; they do not imply a commercial relationship or endorsement.
Before going live
Common webhook questions
Verify the Factuarea-Signature header with HMAC-SHA256, the unmodified body and a five-minute tolerance. The official SDKs include the verifier to avoid serialisation differences.
Grab a test key and send a ping
The whole circuit (create the endpoint, sign, fail, retry, replay) can be tried in sandbox without touching anything real. The test key takes a minute to get from the dashboard.