Parcourir la documentation
Webhooks

Webhooks

The inbound webhook endpoints, how each verifies its sender, and how idempotency is enforced per provider.

9 webhook endpoints. All inbound: these are endpoints BoostEcom exposes to receive events, not endpoints you register to receive ours.

| Endpoint | Sender | Idempotency key | |---|---|---| | /api/webhooks/stripe | Stripe | StripeEvent by event.id | | /api/webhooks/shopify/events | Shopify | ShopifyWebhookEvent | | /api/webhooks/shopify/customer-redact | Shopify (GDPR, mandatory) | ShopifyCustomerRedaction | | /api/webhooks/shopify/redact | Shopify (GDPR, mandatory) | ShopifyComplianceRequest | | /api/webhooks/shopify/data-request | Shopify (GDPR, mandatory) | ShopifyComplianceRequest | | /api/webhooks/resend | Resend | EmailDeliveryEvent.providerEventId | | /api/webhooks/github | GitHub (dev console) | GitHubWebhookEvent by delivery id | | /api/webhooks/creative-render | GitHub Actions (Remotion renders) | the render's own row: an already-settled one is a no-op | | /api/webhooks/[platform] | Chat SDK / WhatsApp | per platform |

Creative render

Internal, and listed here because it is an endpoint we expose. A Remotion render runs on a GitHub Actions runner; when it finishes, the run calls this endpoint to settle the render's row: delivered with its file, or failed with the run's own conclusion. Signed with HMAC-SHA256 over the raw body, refused with 401 without a valid signature.

It settles, it never guesses: the endpoint does not ask GitHub to confirm what the run just told it, because a second source of truth about a run diverges from the first. A row that is already settled is left alone, so a double delivery cannot reopen it. And when no callback arrives at all, an hourly job settles the row as failed rather than leaving it "in progress" forever — a queue that never empties is a queue people stop reading.

Stripe

Every handler is idempotent on event.id, recorded in StripeEvent. A redelivery is a no-op, which matters because credit grants and plan changes run through here.

Beyond subscriptions and credits, the handler also processes the marketplace events: account.updated, charge.refunded and payout.failed for Stripe Connect.

Credit grants read metadata.amount — the pre-tax figure — never amount_total. Stripe Tax is active, so those two differ.

Shopify GDPR — the three mandatory endpoints

Shopify requires three, and they behave differently on purpose:

customers/redact raises a ShopifyCustomerRedaction barrier before answering 200. The erasure itself is drained by the gdpr-erasure cron: vector namespaces, customer links, attribution rows, archived audit payloads. completedAt is the proof of completion: a row without it is an unfulfilled obligation, and it is visible as one.

shop/redact records a ShopifyComplianceRequest that the same cron drains: credentials, vector namespaces, raw payloads archived in AuditLog, order free-text. It stamps completedAt and names in pendingSteps whatever still needs an operator.

customers/data_request is entirely manual. handledAt is only ever set by a human. Past 30 days, a row without it raises an error-level log — the system refuses to let the obligation go quiet.

Note that these three are idempotent on their own two tables, not on ShopifyWebhookEvent.

Resend

Delivery, bounces and spam complaints. Signature verified via Svix, idempotent on EmailDeliveryEvent.providerEventId.

GitHub

Internal: it reports what happened to the agent runs the operator console launched (pull request opened, reviewed, merged), and it is the only writer that advances a roadmap row on a merge. Signature is HMAC-SHA256 over the raw body (X-Hub-Signature-256), idempotent on X-GitHub-Delivery. An unset secret refuses every delivery.

Verification

Every endpoint verifies its sender before doing work: Stripe signature, Shopify HMAC, Svix for Resend. An unverified payload is rejected, never processed on the assumption that it probably came from the right place.

Outbound webhooks

One outbound surface exists, and it is not in the table above because it is not an inbound endpoint: status webhooks. Incident events are signed with HMAC-SHA256 and posted to URLs configured from the admin status panel.

Vous avez construit avec cette doc ?

Passez sur le forum si quelque chose n'est pas clair ou est faux. La doc s'améliore plus vite quand les lecteurs signalent les manques.

Ouvrir le forum