Webhooks
CardNexus sends webhook events to your registered endpoints when something happens on your account that's worth knowing about — an order comes in, your balance updates, an inventory import finishes, or a buyer messages you.
How delivery works
Webhooks are delivered through Svix, a managed delivery service. Svix handles signing, retries with exponential backoff, replay of failed deliveries, and an embedded portal where you manage endpoints and inspect delivery logs.
- Open Settings → API → Webhooks at cardnexus.com.
- Add an endpoint (your HTTPS URL).
- Pick the event types you want.
- CardNexus signs every delivery; you verify the signature server-side before trusting the payload.
Signature verification
Every delivery carries three headers:
| Header | Meaning |
|---|---|
webhook-id |
The delivery id, unique per delivery. |
webhook-timestamp |
Epoch seconds at send time. Reject deliveries older than ~5 minutes to mitigate replay. |
webhook-signature |
HMAC-SHA256 of id.timestamp.body keyed with your endpoint's signing secret. |
Svix publishes verified libraries for Node.js, Python, Go, Ruby, PHP, Rust, Java, C#, and Elixir — use them rather than rolling your own HMAC. Their docs cover each runtime: docs.svix.com.
Event envelope
Every webhook payload follows the same shape:
{
"type": "order.created",
"eventId": "OR-ASNBC-1.seller",
"timestamp": "2024-08-14T10:23:11.000Z",
"account": "65f3a2b1c8d4e9f7a6b5c4d3",
"data": {
"order": { "orderNumber": "OR-ASNBC-1" }
}
}
| Field | Meaning |
|---|---|
type |
The dotted event name (see Event types). |
eventId |
Stable across retries of the same delivery. Safe to dedupe on. |
timestamp |
When the event fired (UTC, ISO 8601). |
account |
The account the event belongs to. For your own webhooks this is your account id. |
data |
The per-event payload. Its shape is documented per event type in the API reference. |
Order events also carry a recipientRole field (seller or buyer) so you can tell which side of the order the event is about.
Event types
| Event | Fires when |
|---|---|
order.created |
A new order is placed. |
order.status.changed |
An order moves to a new status. |
message.received |
You receive a message. |
balance.updated |
Your wallet balance changes. |
inventory.quantity.changed |
An inventory line's quantity changes. |
inventory.import.completed |
A bulk inventory import finishes. |
inventory.export.completed |
A bulk inventory export finishes. |
optimizer.run.completed |
A cart-optimizer run finishes. |
optimizer.run.failed |
A cart-optimizer run fails. |
Order events are delivered to both sides of the order, with recipientRole naming which one a delivery is for. On order.status.changed, the seller's copy carries the sale's data.metadata — your own key/value pairs — so a delivery can be matched to your record without a follow-up call; the buyer's copy has data.metadata as null. See Track sales in your own system.
Idempotency
Deliveries are at-least-once and can arrive out of order. Dedupe on eventId, and process each event only once.
Acknowledging
Return 2xx to acknowledge a delivery. Anything else is treated as a failure and retried by Svix with exponential backoff. Don't return 4xx for "I don't recognise this event type" — log it and return 2xx instead, otherwise the delivery keeps retrying.
Building an integration?
If you're building an application that acts on other users' accounts, you receive every connected account's events at one endpoint, with the account field naming the account each event belongs to. See the Integrations guide for the fan-out model and how to set up your endpoint.