Webhook Providers Reference
Per-provider inbound webhook signature schemes — HMAC-SHA256, ECDSA P-256, HTTP Basic, shared-secret. For self-hosters and operators verifying provider events.
This page is a reference for inbound webhook verification — how each upstream provider authenticates the events it sends to CodeSpar. It is distinct from the Webhook Listener cookbook, which covers outbound webhooks (CodeSpar → integrator) signed with X-CodeSpar-Signature.
On the managed tier you do not need to implement any of this — the backend handles every inbound provider webhook for you at POST /v1/webhooks/<server_id>/<connection_id>. This page is for self-hosters running the OSS runtime, and for operators who want to understand what is happening per provider.
The four verification schemes
Across the 14+ providers integrated today, every inbound webhook falls into one of four schemes:
| Scheme | What it is | Verify by |
|---|---|---|
| HMAC-SHA256 | Provider signs the raw body with a shared secret | crypto.createHmac("sha256", secret).update(body).digest("hex") |
| ECDSA P-256 | Provider signs with an asymmetric key; you verify with their public key | Stripe SDK stripe.webhooks.constructEvent(body, sig, secret) |
| HTTP Basic | Provider sends Authorization: Basic <base64(user:pass)> | Compare against the credential you stamped at connect time |
| Shared-secret | Provider sends a static token in a header — compared with crypto.timingSafeEqual | Constant-time compare against the secret you stamped |
Per-provider table
The table below covers the providers wired into CodeSpar today. The "payload concat order" column matters for HMAC schemes where the signed string is not just the raw body — Mercado Pago is the canonical case.
| Provider | Scheme | Header(s) | Payload signed |
|---|---|---|---|
| Asaas | HMAC-SHA256 | asaas-access-token | Raw request body |
| Mercado Pago | HMAC-SHA256 | x-signature + x-request-id | id:<webhook_id>;request-id:<request_id>;ts:<ts>; |
| Stripe | ECDSA P-256 | Stripe-Signature | <timestamp>.<raw_body> |
| iugu | HMAC-SHA256 | X-Hub-Signature | Raw request body |
| Stone | HMAC-SHA256 | X-Stone-Signature | Raw request body |
| EBANX | HMAC-SHA256 | X-Ebanx-Signature | Raw request body |
| Sift | HTTP Basic | Authorization: Basic <...> | n/a — credential compared, not body |
| Konduto | HTTP Basic | Authorization: Basic <...> | n/a — credential compared, not body |
| Persona | HMAC-SHA256 | Persona-Signature (t=<ts>,v1=<sig>) | <timestamp>.<raw_body> |
| Truora | Shared-secret | Truora-Signature | n/a — token compared, not body |
| Coinbase Commerce | HMAC-SHA256 | X-CC-Webhook-Signature | Raw request body |
| Z-API | Shared-secret | Client-Token | n/a — token compared (companion header pattern) |
Always use crypto.timingSafeEqual when comparing the computed HMAC or stored shared-secret against the value the provider sent. A naive === comparison is vulnerable to timing attacks. For ECDSA verification on Stripe, use the official Stripe SDK — do not roll your own.
Mercado Pago payload concat note
Mercado Pago's HMAC scheme is the most-likely-to-trip-self-hosters case. The signed string is not the raw body — it is a structured concatenation of fields from the request:
id:<webhook_id>;request-id:<request_id>;ts:<ts>;Where:
<webhook_id>is thedata.idfield on the JSON body.<request_id>is the value of thex-request-idheader.<ts>is the timestamp embedded inside thex-signatureheader (parsets=...,v1=...).
Mercado Pago's webhook payload also does not include external_reference. The backend handles this with enrichMercadoPagoFromApi — a synchronous GET /v1/payments/:id against the Mercado Pago API to pull external_reference before normalizing. See async settlement → Mercado Pago caveat.
Self-hoster note
If you are running the OSS runtime, you handle inbound verification yourself. The mechanics are the same as the canonical handler: receive the request, look up the connection by the URL path (/<server_id>/<connection_id>), pull the secret from your vault, run the right verification, and only then enqueue the event for normalization.
The managed tier does this for you, including the Mercado Pago external_reference GET-back, the Stripe SDK verification, and the Persona timestamp-replay window.
Where to find more
- Canonical handler —
packages/api/src/routes/provider-webhooks.tsincodespar-enterprise. Single dispatch point for all providers; per-provider verifier modules underpackages/api/src/webhooks/<provider>.ts. - Outbound webhook reference —
/docs/cookbooks/webhook-listenercovers the OTHER direction: CodeSpar → your endpoint, signed withX-CodeSpar-Signature. - Async settlement —
/docs/concepts/async-settlementexplains how webhook events correlate totool_call_idviaidempotency_key↔external_reference.
Next steps
Last updated on
Webhook Listener
React to payment webhooks with a deterministic loop. On payment.confirmed, automatically issue an NF-e, create shipping, and send WhatsApp — no agent, no LLM, no surprise.
Multi-Tenant Agent
Build a SaaS where each customer runs their own commerce agent with their own providers and credentials. Sessions are scoped per tenant — isolated by design, metered per tenant for billing.