Use Webhooks as Your Source of Truth
postMessage events make the browser feel instant. Signed webhooks tell your backend what actually happened, even if the buyer closes the tab.
Create an endpoint
Register an HTTPS endpoint and choose the events your backend can process. Store the
returned signingSecret securely; it is shown once.
curl -X POST https://api.usethrottle.dev/api/v1/webhook-endpoints \
-H "x-api-key: $THROTTLE_API_KEY" \
-H "content-type: application/json" \
-d '{
"url": "https://shop.example.com/api/throttle/webhook",
"enabledEvents": ["order.created", "payment.captured", "payment.failed"]
}' enabledEvents must be exact event-type strings from
the catalog — Throttle uses payment.captured and subscription.cancelled, not Stripe-style payment.succeeded or subscription.expired. Fetch the full list from GET /api/v1/event-types. An unknown name is rejected
with 400; the error’s details[].allowedValues enumerates every accepted
event type.
/api/v1/webhook-endpoints is the canonical, signed
outbound webhook system documented on this page (enabledEvents,
a whsec_ signing secret, X-Throttle-Signature, plus /deliveries, /replay,
and /test). The older /api/v1/webhooks family (events,
no signed delivery) is deprecated and no longer delivers events — its
delivery worker has been decommissioned. Responses carry Deprecation + Sunset: 2026-10-11 headers; the routes will be removed after 2026-10-11. Migrate any integration still pointing at it to /api/v1/webhook-endpoints.
Verify signatures
Each delivery includes X-Throttle-Signature (format t=<unix_seconds>,v1=<hex>). Compute
HMAC-SHA256 over <timestamp>.<raw body>. The
verifier below also rejects deliveries whose timestamp falls outside a tolerance window
(default 300s) so a captured delivery cannot be replayed indefinitely, and guards against a
malformed v1 so a bad header returns false instead of throwing in your handler.
import { createHmac, timingSafeEqual } from 'node:crypto';
// Verify a Throttle webhook signature (header: "t=<unix_seconds>,v1=<hex>").
// For 24 hours after you rotate the endpoint's secret the header carries TWO
// v1 digests — outgoing secret first, new secret last — and any match
// verifies, so you can swap the secret in your config without a failed
// delivery. Rejects (returns false) when no digest matches OR the timestamp
// is outside the tolerance window — a replayed delivery is not accepted forever.
export function verifyThrottleWebhook(
rawBody: string,
header: string,
secret: string,
toleranceSeconds = 300,
): boolean {
if (!header || typeof header !== 'string') return false;
let ts = NaN;
const digests: string[] = [];
for (const kv of header.split(',')) {
const idx = kv.indexOf('=');
if (idx <= 0) continue;
const k = kv.slice(0, idx).trim();
const v = kv.slice(idx + 1).trim();
if (k === 't') ts = Number.parseInt(v, 10);
// Require hex — a malformed v1 would make a short Buffer and
// timingSafeEqual would throw a RangeError, crashing your handler.
else if (k === 'v1' && /^[0-9a-f]+$/i.test(v) && digests.length < 4) digests.push(v);
}
if (!Number.isFinite(ts) || digests.length === 0) return false;
// Reject deliveries whose timestamp is too old (or too far in the future) to
// stop indefinite replay.
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - ts) > toleranceSeconds) return false;
const expected = Buffer.from(
createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex'),
'hex',
);
// Equal-length only, then a constant-time compare of every digest.
let ok = false;
for (const v1 of digests) {
const received = Buffer.from(v1, 'hex');
if (received.length === expected.length && timingSafeEqual(received, expected)) ok = true;
}
return ok;
} environmentKind shipped. Throttle retries a 401
on the normal ladder; a 4xx that names a schema problem is far easier to find in the
delivery log.
Rotate the signing secret
POST /api/v1/webhook-endpoints/:id/rotate-secret
mints a new whsec_ and returns it once. For graceSeconds (default 86400, max 604800) every
delivery is signed with both secrets — the header reads t=…,v1=<outgoing>,v1=<new> — so you can
swap the secret in your config on your own schedule without a failed delivery. Send { "graceSeconds": 0 } to revoke the old secret
immediately when it has leaked. The response carries previousSecretExpiresAt.
v1 still works — it verifies from the moment you
deploy the new secret, and rejects until then, exactly as before the window existed.
Process idempotently
Webhook delivery is at-least-once. Store each event id before applying side effects so retries do not double-fulfill, double-email, or double-book revenue.
Deduping on event id covers retries. It does not cover a genuine repeat: order.created fires exactly once per order, but every
other order lifecycle event is at-least-once by design, and a repeat carries a new event id. Moving an order backwards is a supported merchant
action, so fulfilled → processing → fulfilled sends order.fulfilled twice. That is deliberate — a merchant
who rewinds and re-fulfils has usually re-shipped, and suppressing the second event
would be the one case where a shipping notification must not go missing. Make handlers
that must not act twice idempotent on the entity and target state, not on the event id
alone.
fulfillmentType: 'digital' or fulfillmentType: 'access_grant', Throttle
creates and completes those fulfillment rows after the order enters processing. Listen for
fulfillment.completed and order.fulfilled in addition to payment events when
you need to mirror delivery state.
fulfillment.shipment.delivered now fires when the
arrival is recorded — a call to POST /fulfillments/{id}/delivered — and no longer
when the shipment fulfillment is completed. Completing a fulfillment is carrier
handoff, so the old timing announced a delivery while the parcel was still moving.
Merchants who complete a fulfillment and never call the new endpoint will not see this
event at all; listen for fulfillment.completed if what you want is handover.
The payload gained the shipment object, so data.shipment.actualDelivery carries the arrival
timestamp. The buyer-facing customer-order-delivered email follows this event, and
so moved with it.
Recommended fields
- event_id unique identifier from the delivery envelope.
- event_type for routing and debugging.
- event_version from the delivery envelope for payload migrations.
- environment_id from the delivery envelope for environment-scoped processing.
- processed_at timestamp after side effects finish.
- payload raw JSON for audit and replay.
Event families
- Cart (14 events)
- Order (13 events)
- Quote (9 events)
- Payment (14 events)
- Fulfillment (6 events)
- Subscription (15 events)
- Discount (2 events)
- Customer (9 events)
- AR / Net30 (1 events)
- Imports (1 events)
- Buyer-facing scripts (3 events)
- Shipping & Tax (1 events)
- Workspace payment methods (5 events)
- Platform billing charges (2 events)
- Workspace lifecycle (1 events)
- Extension lifecycle (2 events)
Abandoned carts & cart lifecycle
Carts are authoritative in Throttle. When your storefront creates a native cart
(POST /api/v1/carts), Throttle tracks its whole
lifecycle and detects abandonment server-side — you don't build your own timer.
-
cart.abandoned— a nightly sweep marks an open cart abandoned once it sits idle past the application's configured threshold (or you can react immediately on delivery). Throttle also sends the buyer acustomer.cart_abandonedrecovery email when it can resolve an email and the app has recovery enabled — subscribe to the event only if you want to run your own follow-up. -
cart.converted— fires when a cart becomes an order (the successful-checkout signal, alongsideorder.created/payment.captured). A cart that lapses past itsexpires_atwindow is swept intocart.abandoned— there is no separate cart.expired webhook.
cart.abandoned and cart.converted — not checkout.abandoned / checkout.expired / checkout.completed (which don't exist).
Subscribing to any cart event requires the carts:read scope on your API key — a key without it
won't see cart events in the subscribable list at all.
The cart.abandoned delivery carries the full cart so
your follow-up needs no extra fetch — buyer, line items, totals, and a recoveryUrl that reopens the checkout:
{
"id": "evt_2b8f...",
"type": "cart.abandoned",
"workspaceId": "9c1e...",
"createdAt": "2026-07-04T18:22:05.000Z",
"data": {
"cartId": "cart_9f2a...",
"sequence": 7,
"currency": "USD",
"total": 8900,
"itemCount": 2,
"customer": { "id": "cus_51...", "email": "buyer@example.com", "firstName": "Bea" },
"lineItems": [
{ "id": "li_1", "name": "Premium Widget", "quantity": 2, "unitPrice": 2999, "total": 5998 }
],
"recoveryUrl": "https://checkout.usethrottle.dev/c/cart_9f2a..."
}
} Payload envelope
Every delivery is a JSON object with stable top-level fields and event-specific fields
under data. The signed body is capped at 256 KiB; larger deliveries are marked failed
before Throttle POSTs them.
{
"id": "evt_01HZX...",
"type": "payment.captured",
"version": "1",
"workspaceId": "6659b411-9cd7-40ac-9a73-1e7801d89f55",
"environmentId": "b7e5a40e-8c0f-4d85-a726-2ff2967f4b52",
"environmentKind": "production",
"createdAt": "2026-05-06T10:00:00.000Z",
"data": {
"paymentId": "pay_01HZX...",
"orderId": "ord_01HZX...",
"amount": 12900,
"capturedAmount": 12900,
"authorizedAmount": 12900,
"currency": "USD",
"customerId": "cus_01HZX...",
"subscriptionId": "sub_01HZX...",
"customer": {
"id": "cus_01HZX...",
"email": "buyer@example.com",
"firstName": "Ada",
"lastName": "Lovelace",
"phone": null,
"externalId": "your-user-42",
"externalCustomerId": null
}
}
} - Headers:
X-Throttle-Signature,X-Throttle-Event-Id,X-Throttle-Event-Type,Content-Type: application/json. - Envelope:
id,type,version,workspaceId,environmentId,environmentKind,createdAt, anddata.environmentKindisproductionornon_production: if your destination is a live system, dropnon_productiondeliveries in one check. A shared test environment delivers to the same endpoint, correctly signed, so nothing else distinguishes synthetic traffic from a real buyer's. - Additive fields are not a version bump. Throttle adds top-level
envelope fields and fields under
datawithout changingversion. Each addition is announced in the changelog, but never in advance of the first delivery that carries it. Parse the envelope with a schema that ignores or strips unknown keys; a strict schema that rejects them will fail every delivery the moment a field is added.versionchanges only when an existing field is removed, renamed, or changes type, and never without notice. - Replays: a replayed delivery re-sends the same event —
same
id, samecreatedAt, samedata. The send time is thet=in the signature header. Treat a repeatedidas already handled; never re-date the event. - Amounts: all money fields are integer minor units, for example cents for USD.
- Customer identity: every
subscription.*event, and everypayment.*event that resolves an order, carries a nestedcustomerobject so you can map the delivery to your own user without a follow-upGET /customers/{id}. Readcustomer.externalIdfor the id you set yourself —externalCustomerIdis a separate per-connection mapping and isnullfor most integrations. Payment events also carrycustomerIdand, for subscription-driven charges,subscriptionId. Treatcustomeras optional: it is omitted when the record cannot be resolved.
Event payload reference
Each event below uses the common envelope above. Route by type and read the event fields from data.
data follows two shapes — check the exact fields for
each event in the table below rather than assuming one form:
- Entity lifecycle events nest the full entity under its name:
data.order,data.payment,data.subscription,data.customer,data.fulfillment,data.paymentMethod, anddata.workspace(for platform-billing events). Onorder.created, the order includesclientContextwhen attribution was captured at checkout — UTMs, click ids (gclid/fbclid/msclkid/ttclid),gaClientId/gaSessionId,fbp/fbc,landingPage,referrer,consent, and the server-stampedipAddress/userAgent. See Track conversions . - Cart activity events (
cart.*) are intentionally flat and lightweight —data.cartId,data.sequence, plus the changed fields — because they fire at high frequency and do not ship the full cart. - Non-entity signals carry flat scalars, not a nested entity:
order.sync_failed,order.sync_conflict,payment.disputed,payment.dispute_cleared,payment.recorded,payment.expired,subscription.trial_blocked, andshipping_tax.provider_connection.unhealthy. For examplepayment.disputedsendsdata.paymentId(notdata.payment).
Every order.* event embeds data.order.paymentStatus, one of pending, authorized, captured, partially_paid, partially_refunded, refunded, failed, voided, processing, disputed, or expired. The
last three arrived with the in-flight payment work: processing is a payment the provider has not
resolved yet (it used to read failed), disputed is derived from the payment's disputed boolean and outranks captured because a chargeback has a deadline, and expired is an authorization that lapsed before
capture. partially_paid
means money has been captured for this order, but less than the order total. Most
commonly a deposit on a quote with deposit-and-balance terms, where the balance has not
yet been collected. A comped order reads captured as well: order.comped waives the remaining balance, so nothing
is owed even though no money arrived. Read data.compAmount to tell a waived order from a paid
one.
Every event below carries an example delivery: the exact envelope your
endpoint receives, with fixed ids and timestamps. It is the same bytes GET /api/v1/event-types returns under example and the same fixture POST /api/v1/webhook-endpoints/:id/test sends, so a
mapper written against it is written against what ships.
Cart
-
cart.created— A native cart is created.{ cartId, sequence, applicationId, customerId, currency }Example delivery
cart.created{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.created", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 1, "applicationId": "test_store_1a2b3c08", "customerId": "test_cust_1a2b3c02", "currency": "USD" } } -
cart.updated— Cart metadata, customer, notes, or addresses change.{ cartId, sequence, changedFields }Example delivery
cart.updated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.updated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 2, "changedFields": [ "shippingAddress" ] } } -
cart.item_added— A line item is added to the cart.{ cartId, sequence, itemId, name, quantity, unitPrice }Example delivery
cart.item_added{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.item_added", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 3, "itemId": "test_item_1", "name": "Test item", "quantity": 2, "unitPrice": 500 } } -
cart.item_updated— A line item quantity or price changes.{ cartId, sequence, itemId, quantity, unitPrice }Example delivery
cart.item_updated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.item_updated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 4, "itemId": "test_item_1", "quantity": 3, "unitPrice": 500 } } -
cart.item_removed— A line item is removed from the cart.{ cartId, sequence, itemId }Example delivery
cart.item_removed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.item_removed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 5, "itemId": "test_item_1" } } -
cart.shipping_selected— A buyer selects a shipping method.{ cartId, sequence, methodId, displayName, rateAmount, currency }Example delivery
cart.shipping_selected{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.shipping_selected", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 6, "methodId": "ground", "displayName": "Ground", "rateAmount": 799, "currency": "USD" } } -
cart.shipping_cleared— A selected shipping method is cleared.{ cartId, sequence }Example delivery
cart.shipping_cleared{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.shipping_cleared", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 7 } } -
cart.discount_applied— A cart discount code is applied.{ cartId, sequence, code, amount, type, discountTotal }Example delivery
cart.discount_applied{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.discount_applied", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 8, "code": "TESTCODE", "amount": 500, "type": "fixed_amount", "discountTotal": 500 } } -
cart.discount_removed— A cart discount code is removed.{ cartId, sequence }Example delivery
cart.discount_removed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.discount_removed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 9 } } -
cart.tax_recomputed— Cart tax lines are recomputed or cleared.{ cartId, sequence, lineCount }Example delivery
cart.tax_recomputed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.tax_recomputed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 10, "lineCount": 2 } } -
cart.checkout_started— A cart moves into checkout.{ cartId, sequence }Example delivery
cart.checkout_started{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.checkout_started", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 11 } } -
cart.converted— A cart is converted into an order.{ cartId, sequence }Example delivery
cart.converted{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.converted", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 12 } } -
cart.abandoned— A cart is marked abandoned.{ cartId, sequence, currency, subtotal, taxTotal, shippingTotal, discountTotal, total, itemCount, customer: { id: string | null, email, firstName, lastName, phone, externalId, externalCustomerId } | null, lineItems: [{ id, type, referenceId, name, quantity, unitPrice, subtotal, total, imageUrl }], shippingAddress: CartAddress | null, billingAddress: CartAddress | null, recoveryUrl: string | null }Example delivery
cart.abandoned{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.abandoned", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 13, "currency": "USD", "subtotal": 5000, "taxTotal": 0, "shippingTotal": 0, "discountTotal": 0, "total": 5000, "itemCount": 1, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": "+1 555 555 0123", "externalId": "ext_cust_demo", "externalCustomerId": "ext_cust_demo" }, "lineItems": [ { "id": "test_item_1", "type": "product", "referenceId": "SKU-TEST-1", "name": "Test item", "quantity": 2, "unitPrice": 2500, "subtotal": 5000, "total": 5000, "imageUrl": null } ], "shippingAddress": { "firstName": "Test", "addressLine1": "1 Market St", "city": "San Francisco", "stateProvince": "CA", "postalCode": "94105", "countryCode": "US" }, "billingAddress": null, "recoveryUrl": "https://shop.example.com/cart?c=test_cart_1a2b3c05" } } -
cart.merged— An anonymous cart is merged into a customer cart.{ cartId, sequence, fromCartId, toCustomerId }Example delivery
cart.merged{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "cart.merged", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cartId": "test_cart_1a2b3c05", "sequence": 15, "fromCartId": "test_cart_1a2b3c0b", "toCustomerId": "test_cust_1a2b3c02" } }
Order
-
order.created— An order is persisted.{ order, fromCart?, sessionId?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
order.created{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.created", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "pending", "paymentStatus": "pending", "source": "throttle-test", "clientContext": { "utmSource": "newsletter", "utmMedium": "email", "gclid": "test_gclid", "gaClientId": "1234567890.1720000000", "consent": { "analytics": true, "marketing": true } } }, "sessionId": "test_sess_1a2b3c0b", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.updated— An order status changes without reaching a terminal state.{ order, status, previousStatus, action, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
order.updated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.updated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "processing", "paymentStatus": "partially_paid", "source": "throttle-test" }, "status": "processing", "previousStatus": "pending", "action": "PAYMENT_CAPTURED", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.fulfilled— Every deliverable line item on the order has been fulfilled. Fires on entering `fulfilled`, whether the engine derived it from fulfillment rows or a merchant set it by hand.{ order, status, previousStatus, action, manual?, actor?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
order.fulfilled{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.fulfilled", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "fulfilled", "paymentStatus": "pending", "source": "throttle-test" }, "status": "fulfilled", "previousStatus": "processing", "action": "FULFILLMENT_COMPLETE", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.cancelled— An order is cancelled.{ order, status, previousStatus, action, reason?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
order.cancelled{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.cancelled", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "cancelled", "paymentStatus": "pending", "source": "throttle-test" }, "status": "cancelled", "previousStatus": "pending", "action": "CANCEL", "reason": "Synthetic cancellation", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.closed— A fulfilled order is closed.{ order, status, previousStatus, action, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
order.closed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.closed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "closed", "paymentStatus": "pending", "source": "throttle-test" }, "status": "closed", "previousStatus": "fulfilled", "action": "CLOSE", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.comped— A merchant wrote off the order's remaining balance. Money already captured stays captured; no processor is contacted.{ order, status, paymentStatus, previousPaymentStatus, compAmount, netCaptured }Example delivery
order.comped{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.comped", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 0, "currency": "USD", "status": "pending", "paymentStatus": "captured", "source": "throttle-test", "compAmount": 100 }, "status": "pending", "paymentStatus": "captured", "previousPaymentStatus": "pending", "compAmount": 100, "netCaptured": 0, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.comp_reversed— A comp is undone — the order owes what it owed again.{ order, status, paymentStatus, previousPaymentStatus, compAmount, netCaptured }Example delivery
order.comp_reversed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.comp_reversed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "pending", "paymentStatus": "pending", "source": "throttle-test", "compAmount": 0 }, "status": "pending", "paymentStatus": "pending", "previousPaymentStatus": "captured", "compAmount": 0, "netCaptured": 0, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.held— An order is frozen operationally. Nothing about money or delivery changed — branch on onHold, not on the order status.{ order, onHold, holdReason, previousHoldReason, heldBy, releasedBy }Example delivery
order.held{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.held", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "pending", "paymentStatus": "pending", "source": "throttle-test", "onHold": true, "holdReason": "Fraud review", "heldBy": "user_synthetic" }, "onHold": true, "holdReason": "Fraud review", "previousHoldReason": null, "heldBy": "user_synthetic", "releasedBy": null, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.hold_released— A hold is lifted. previousHoldReason carries the reason the hold existed.{ order, onHold, holdReason, previousHoldReason, heldBy, releasedBy }Example delivery
order.hold_released{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.hold_released", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "pending", "paymentStatus": "pending", "source": "throttle-test", "onHold": false, "holdReason": null, "heldBy": null }, "onHold": false, "holdReason": null, "previousHoldReason": "Fraud review", "heldBy": null, "releasedBy": "user_synthetic", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.sync_failed— Provider order writeback retries are exhausted.{ mappingId, connectionId, externalId, attempts, error }Example delivery
order.sync_failed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.sync_failed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "mappingId": "test_map_1a2b3c0b", "connectionId": "test_conn_1a2b3c0c", "externalId": "bc_order_1001", "attempts": 5, "error": "Synthetic provider writeback failure", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
order.sync_conflict— An inbound provider update conflicts with Throttle payment state.{ externalStatus, throttleStatus, dimension }Example delivery
order.sync_conflict{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "order.sync_conflict", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "externalStatus": { "paymentStatus": "captured" }, "throttleStatus": "authorized", "dimension": "payment", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
return.created— A return / RMA is opened against an order.{ returnId, orderId, status, reason?, restock, refundAmount, items }Example delivery
return.created{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "return.created", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "returnId": "test_ret_1a2b3c0b", "orderId": "test_ord_1a2b3c00", "status": "requested", "reason": "no_longer_needed", "restock": true, "refundAmount": 100, "items": [ { "lineItemId": "test_li_1a2b3c0c", "quantity": 1 } ] } } -
return.updated— A return transitions (approved / rejected / received / completed / cancelled). On completion refundPaymentId is set.{ returnId, orderId, status, previousStatus, restock, refundAmount, refundPaymentId? }Example delivery
return.updated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "return.updated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "returnId": "test_ret_1a2b3c0b", "orderId": "test_ord_1a2b3c00", "status": "approved", "previousStatus": "requested", "restock": true, "refundAmount": 100 } }
Quote
-
quote.requested— A buyer or integration submits a quote request (RFQ).{ quote, revision? }Example delivery
quote.requested{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.requested", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "requested", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "api", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": null } } -
quote.proposed— A quote revision is issued and proposed to the buyer.{ quote, revision }Example delivery
quote.proposed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.proposed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "proposed", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null } } } -
quote.viewed— The buyer opens the quote link. Throttled to one delivery per revision per 6 hours.{ quote, revision }Example delivery
quote.viewed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.viewed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "proposed", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null } } } -
quote.comment_added— A shared (non-internal) comment is added by either party.{ quote, revision?, comment }Example delivery
quote.comment_added{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.comment_added", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "proposed", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null }, "comment": { "id": "test_qc_1a2b3c0d", "authorType": "buyer", "body": "Can you do Net-45 instead of Net-30?", "createdAt": "2026-09-10T12:00:00.000Z" } } } -
quote.revision_requested— The buyer asks for changes instead of accepting the current proposal.{ quote, revision }Example delivery
quote.revision_requested{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.revision_requested", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "revision_requested", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null } } } -
quote.declined— The buyer (or a rep, recording a decline) declines the quote.{ quote, revision?, declinedReason? }Example delivery
quote.declined{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.declined", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "declined", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null }, "declinedReason": "Chose another vendor" } } -
quote.accepted— The buyer accepts a quote (or a rep records a phone/email acceptance) and checkout begins.{ quote, revision }Example delivery
quote.accepted{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.accepted", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "accepted", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null } } } -
quote.converted— The accepted quote completes checkout and its order now exists.{ quote, orderId }Example delivery
quote.converted{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.converted", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "converted", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null }, "orderId": "test_order_1a2b3c0d" } } -
quote.expired— The hourly expiry cron flips a proposed quote past its revision expiry; it can no longer be accepted.{ quote, revision: null }Example delivery
quote.expired{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "quote.expired", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "quote": { "id": "test_quote_1a2b3c0c", "quoteNumber": "Q-1042", "status": "proposed", "customerId": null, "customerEmail": "buyer@example.com", "buyerReference": "PO-2211", "source": "dashboard", "currency": "USD", "assignedMemberId": null, "expiresAt": "2026-09-24T12:00:00.000Z", "createdAt": "2026-09-10T12:00:00.000Z" }, "revision": { "id": "test_qrev_1a2b3c0b", "revisionNumber": 1, "state": "issued", "issuedAt": "2026-09-10T12:00:00.000Z", "expiresAt": "2026-09-24T12:00:00.000Z", "subtotal": 250000, "taxTotal": 0, "discountTotal": 0, "shippingTotal": 0, "total": 250000, "paymentTerms": { "mode": "net_terms", "netN": 30 }, "depositAmount": null } } }
Payment
-
payment.pending— A payment is created and waiting for authorization.{ payment }Example delivery
payment.pending{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.pending", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "payment": { "id": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 100, "currency": "USD", "status": "pending", "processor": "card-simulator" } } } -
payment.processing— The provider has neither approved nor declined yet — a 3DS challenge, a redirect method, a bank debit still settling. A resolution event (payment.authorized, payment.captured or payment.failed) follows.{ payment, processorStatus }Example delivery
payment.processing{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.processing", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "payment": { "id": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 100, "currency": "USD", "status": "processing", "processor": "card-simulator" }, "processorStatus": "buyer_approval_pending" } } -
payment.captured— A payment capture succeeds (full or partial).{ paymentId, orderId, amount, capturedAmount?, authorizedAmount?, currency, processor?, processorTransactionId?, metadata?, customerId, subscriptionId?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
payment.captured{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.captured", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 100, "currency": "USD", "processor": "card-simulator", "processorTransactionId": "test_txn_1a2b3c0b", "metadata": { "orderRef": "TEST-PO-1" }, "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.failed— A processor reports a terminal payment failure or decline.{ paymentId, orderId, amount?, currency?, code?, message?, errorCode?, errorMessage?, customerId, subscriptionId?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
payment.failed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.failed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "code": "card_declined_test", "message": "Synthetic test failure (Card Simulator)", "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.voided— A payment authorization is voided.{ payment }Example delivery
payment.voided{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.voided", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "payment": { "id": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 100, "currency": "USD", "status": "voided", "processor": "card-simulator" } } } -
payment.expired— An authorization lapsed before it was captured. Emitted by our own hourly sweep, not by the provider — the money was never taken and the buyer must be charged again.{ paymentId, orderId, authorizedAt }Example delivery
payment.expired{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.expired", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "authorizedAt": "2026-09-10T12:00:00.000Z", "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.recorded— Money that arrived outside Throttle (cash, bank transfer, cheque) was recorded by the merchant. Not a processor capture: excluded from billable GMV.{ paymentId, orderId, amount, currency, method, reference, receivedAt }Example delivery
payment.recorded{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.recorded", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 100, "currency": "USD", "method": "bank_transfer", "reference": "TEST-WIRE-1", "receivedAt": "2026-09-10T12:00:00.000Z", "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.refunded— A full refund succeeds.{ paymentId, orderId, refundedAmount, paymentAmount, currency, status?, processor?, gr4vyRefundId?, customerId, subscriptionId?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
payment.refunded{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.refunded", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "refundedAmount": 100, "paymentAmount": 100, "currency": "USD", "status": "refunded", "processor": "card-simulator", "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.partially_refunded— A partial refund succeeds.{ paymentId, orderId, refundedAmount, paymentAmount, currency, status?, processor?, gr4vyRefundId?, customerId, subscriptionId?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
payment.partially_refunded{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.partially_refunded", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "refundedAmount": 40, "paymentAmount": 100, "currency": "USD", "status": "partially_refunded", "processor": "card-simulator", "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.refund_failed— A refund attempt fails.{ paymentId, orderId, amount, currency, gr4vyRefundId?, errorCode?, errorMessage?, customerId, subscriptionId?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
payment.refund_failed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.refund_failed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 50, "currency": "USD", "gr4vyRefundId": "test_ref_1a2b3c0b", "errorCode": "refund_failed_test", "errorMessage": "Synthetic refund failure", "customerId": "test_cust_1a2b3c02", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
payment.vaulted— A checkout stores a reusable payment method.{ customerId, paymentMethodId, gr4vyBuyerId, processor, checkoutSessionId, recurring? }Example delivery
payment.vaulted{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.vaulted", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customerId": "test_cust_1a2b3c02", "paymentMethodId": "test_pm_1a2b3c03", "gr4vyBuyerId": "test_buyer_1a2b3c0b", "processor": "embedded", "checkoutSessionId": "test_sess_1a2b3c0c", "recurring": { "plan": "test-plan", "interval": "monthly" } } } -
payment.disputed— A payment is marked disputed — a Net30 payment flagged in the dashboard, or an ingested Gr4vy card chargeback.{ paymentId, reason, openedAt }Example delivery
payment.disputed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.disputed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "reason": "product_not_received", "openedAt": "2026-09-10T12:00:00.000Z" } } -
payment.dispute_cleared— A payment dispute is cleared — a Net30 dispute cleared in the dashboard, or a Gr4vy chargeback won/reversed.{ paymentId }Example delivery
payment.dispute_cleared{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "payment.dispute_cleared", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01" } }
Fulfillment
-
fulfillment.created— A fulfillment is created for an order.{ fulfillment, order: { id, customerId? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
fulfillment.created{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "fulfillment.created", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "fulfillment": { "id": "test_ful_1a2b3c06", "orderId": "test_ord_1a2b3c00", "type": "shipment", "status": "pending" }, "order": { "id": "test_ord_1a2b3c00" } } } -
fulfillment.completed— A fulfillment is completed.{ fulfillment, order: { id, customerId? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
fulfillment.completed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "fulfillment.completed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "fulfillment": { "id": "test_ful_1a2b3c06", "orderId": "test_ord_1a2b3c00", "type": "shipment", "status": "completed" }, "order": { "id": "test_ord_1a2b3c00" } } } -
fulfillment.cancelled— A fulfillment is cancelled.{ fulfillment, order: { id, customerId? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
fulfillment.cancelled{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "fulfillment.cancelled", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "fulfillment": { "id": "test_ful_1a2b3c06", "orderId": "test_ord_1a2b3c00", "type": "shipment", "status": "cancelled" }, "order": { "id": "test_ord_1a2b3c00" } } } -
fulfillment.shipment.shipped— A shipment fulfillment receives its first tracking number — supplied at creation via POST /orders/{orderId}/fulfillments `shipment.trackingNumber`, or later via PATCH .../fulfillments/{id}/shipment. Fires once per shipment; later tracking edits do not re-fire it.{ fulfillment, shipment: { trackingNumber, carrier, trackingUrl, serviceLevel, shippedAt, estimatedDelivery, ... }, order: { id, customerId? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
fulfillment.shipment.shipped{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "fulfillment.shipment.shipped", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "fulfillment": { "id": "test_ful_1a2b3c06", "orderId": "test_ord_1a2b3c00", "type": "shipment", "status": "processing" }, "shipment": { "fulfillmentId": "test_ful_1a2b3c06", "carrier": "ups", "trackingNumber": "TEST-TRACKING-123", "trackingUrl": "https://www.ups.com/track?tracknum=TEST-TRACKING-123" }, "order": { "id": "test_ord_1a2b3c00", "customerId": "test_cust_1a2b3c02" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
fulfillment.shipment.delivered— A shipment is recorded as arrived via POST /fulfillments/{id}/delivered. Since 2026-08-24 this is the arrival itself, not the completion of the fulfillment.{ fulfillment, shipment, order: { id, customerId? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
fulfillment.shipment.delivered{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "fulfillment.shipment.delivered", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "fulfillment": { "id": "test_ful_1a2b3c06", "orderId": "test_ord_1a2b3c00", "type": "shipment", "status": "completed" }, "shipment": { "fulfillmentId": "test_ful_1a2b3c06", "carrier": "ups", "trackingNumber": "TEST-TRACKING-123", "trackingUrl": "https://www.ups.com/track?tracknum=TEST-TRACKING-123", "shippedAt": "2026-05-20T00:00:00Z", "actualDelivery": "2026-05-23T00:00:00Z" }, "order": { "id": "test_ord_1a2b3c00" } } } -
fulfillment.digital.delivered— A digital fulfillment is marked completed (download/access ready).{ fulfillment, digitalDelivery?, order: { id, customerId? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
fulfillment.digital.delivered{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "fulfillment.digital.delivered", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "fulfillment": { "id": "test_ful_1a2b3c06", "orderId": "test_ord_1a2b3c00", "type": "digital", "status": "completed" }, "digitalDelivery": { "downloadUrl": "https://example.com/download/test", "expiresAt": null }, "order": { "id": "test_ord_1a2b3c00" } } }
Subscription
-
subscription.created— A subscription is created.{ subscription, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.created{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.created", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "created", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.activated— A trialing subscription becomes active.{ subscription, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.activated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.activated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "activated", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.updated— Subscription plan, metadata, or cancel-at-period-end changes.{ subscription, cancelAtPeriodEnd?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.updated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.updated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "updated", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.plan_changed— A subscription plan change is applied immediately (upgrade).{ subscription, previous, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.plan_changed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.plan_changed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "active", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "previous": { "planReference": "starter", "amount": 2900 }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.plan_change_scheduled— A downgrade is scheduled for the next interval.{ subscription, pending, effectiveAt, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.plan_change_scheduled{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.plan_change_scheduled", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "active", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "pending": { "planReference": "starter", "planName": "Starter Monthly", "interval": "monthly", "amount": 2900 }, "effectiveAt": "2026-10-10T12:00:00.000Z", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.paused— A subscription is paused.{ subscription, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.paused{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.paused", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "paused", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.resumed— A paused subscription is resumed.{ subscription, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.resumed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.resumed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "resumed", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.cancelled— A subscription is cancelled.{ subscription, reason?, atPeriodEnd?, lastError?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.cancelled{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.cancelled", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "cancelled", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "reason": "merchant_action", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.invoice_refunded— One of a subscription's billing cycles is refunded. `intent` says whether the subscription was also cancelled.{ subscription, invoiceId, amountRefunded, fullyRefunded, intent: 'money_only' | 'refund_and_cancel' }Example delivery
subscription.invoice_refunded{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.invoice_refunded", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "active", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "invoiceId": "test_subinv_1a2b3c0b", "amountRefunded": 2000, "totalRefunded": 2000, "fullyRefunded": true, "intent": "money_only", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.renewed— A renewal succeeds.{ subscription, payment?, order?, freeRenewal?, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.renewed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.renewed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "active", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD", "currentPeriodStart": "2026-09-10T12:00:00.000Z", "currentPeriodEnd": "2026-10-10T12:00:00.000Z" }, "payment": { "id": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "amount": 100, "currency": "USD", "status": "authorized", "processor": "card-simulator" }, "order": { "id": "test_ord_1a2b3c00", "orderNumber": "TEST-1001", "total": 100, "currency": "USD", "status": "pending", "paymentStatus": "pending", "source": "throttle-test" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.past_due— The first renewal failure moves a subscription to past_due.{ subscription, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.past_due{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.past_due", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "past_due", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD", "failureCount": 1 }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.payment_failed— A renewal payment attempt fails.{ subscription, attempt, nextRetryAt, lastError, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.payment_failed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.payment_failed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "past_due", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD", "failureCount": 1 }, "attempt": 1, "nextRetryAt": "2026-09-11T12:00:00.000Z", "lastError": "Synthetic renewal failure", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.backup_pm_used— A renewal's primary card declined and the buyer's backup card was charged instead.{ subscription, payment?, backupPaymentMethod: { cardLastFour?, cardBrand? }, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.backup_pm_used{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.backup_pm_used", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscription": { "id": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "status": "active", "planReference": "test-plan", "interval": "monthly", "amount": 2000, "currency": "USD" }, "backupPaymentMethod": { "id": "test_pm_1a2b3c0b", "cardBrand": "visa", "cardLastFour": "4242" }, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.create_failed— Auto-create from a recurring checkout intent fails after vaulting.{ checkoutSessionId, customerId, paymentMethodId, gr4vyBuyerId, recurring, reason, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.create_failed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.create_failed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "checkoutSessionId": "test_sess_1a2b3c0b", "customerId": "test_cust_1a2b3c02", "paymentMethodId": "test_pm_1a2b3c03", "gr4vyBuyerId": "test_buyer_1a2b3c0c", "recurring": { "plan": "test-plan", "interval": "monthly" }, "reason": "Synthetic subscription create failure", "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
subscription.trial_blocked— Trial-fraud protection downgrades a requested trial.{ subscriptionId, customerId, paymentMethodId, reason, requestedTrialDays, customer: { id, email, firstName, lastName, phone, externalId, externalCustomerId } | null }Example delivery
subscription.trial_blocked{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "subscription.trial_blocked", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "subscriptionId": "test_sub_1a2b3c04", "customerId": "test_cust_1a2b3c02", "paymentMethodId": "test_pm_1a2b3c03", "reason": "card_fingerprint_seen", "requestedTrialDays": 14, "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } }
Discount
-
discount.applied— A discount is applied to a cart.{ cart, code, cartId }Example delivery
discount.applied{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "discount.applied", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cart": { "id": "test_cart_1a2b3c05", "discountTotal": 500, "total": 1500, "currency": "USD" }, "cartId": "test_cart_1a2b3c05", "code": "TESTCODE" } } -
discount.removed— A discount is removed from a cart.{ cart, cartId }Example delivery
discount.removed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "discount.removed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "cart": { "id": "test_cart_1a2b3c05", "discountTotal": 0, "total": 2000, "currency": "USD" }, "cartId": "test_cart_1a2b3c05" } }
Customer
-
customer.created— A customer is created.{ customer }Example delivery
customer.created{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.created", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
customer.updated— A customer profile is updated.{ customer }Example delivery
customer.updated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.updated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
customer.deleted— A customer is deleted.{ customer }Example delivery
customer.deleted{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.deleted", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customer": { "id": "test_cust_1a2b3c02", "email": "buyer@example.com", "firstName": "Test", "lastName": "Buyer", "phone": null, "externalId": "test_ext_1a2b3c09", "externalCustomerId": "test_conn_1a2b3c0a" } } } -
customer.payment_method_added— A customer payment method is added.{ paymentMethod }Example delivery
customer.payment_method_added{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.payment_method_added", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentMethod": { "id": "test_pm_1a2b3c03", "customerId": "test_cust_1a2b3c02", "processor": "embedded", "methodType": "card", "cardBrand": "visa", "cardLastFour": "1111" } } } -
customer.payment_method_removed— A customer payment method is removed.{ paymentMethod }Example delivery
customer.payment_method_removed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.payment_method_removed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentMethod": { "id": "test_pm_1a2b3c03", "customerId": "test_cust_1a2b3c02", "processor": "embedded", "methodType": "card", "cardBrand": "visa", "cardLastFour": "1111" } } } -
customer.registered— A buyer registers a storefront account.{ customerId }Example delivery
customer.registered{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.registered", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customerId": "test_cust_1a2b3c02" } } -
customer.email_verified— A buyer verifies their storefront account's email address.{ customerId }Example delivery
customer.email_verified{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.email_verified", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customerId": "test_cust_1a2b3c02" } } -
customer.password_changed— A buyer changes their storefront account password, via reset or account settings.{ customerId, via: 'reset' | 'change' }Example delivery
customer.password_changed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.password_changed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customerId": "test_cust_1a2b3c02", "via": "reset" } } -
customer.sessions_revoked— All of a buyer's storefront sessions are revoked at once (logout-all, a password reset, or a merchant-initiated sign-out).{ customerId, reason }Example delivery
customer.sessions_revoked{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "customer.sessions_revoked", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "customerId": "test_cust_1a2b3c02", "reason": "logout_all" } }
AR / Net30
-
invoice.past_due— A Net30 invoice passes a dunning tick.{ paymentId, orderId, invoiceNumber, daysPastDue, tickFired, dueDate, totalAmount, currency }Example delivery
invoice.past_due{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "invoice.past_due", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "paymentId": "test_pay_1a2b3c01", "orderId": "test_ord_1a2b3c00", "invoiceNumber": "INV-TEST-1001", "daysPastDue": 7, "tickFired": 7, "dueDate": "2026-09-10T12:00:00.000Z", "totalAmount": 100, "currency": "USD" } }
Imports
-
import.completed— A bulk data import run (validate or commit) finishes.{ importId, entityType, status, mode, rowsProcessed, rowsCreated, rowsUpdated, rowsSkipped, rowsFailed, startedAt, finishedAt }. When rowsFailed > 0, GET /api/v1/imports/{importId}/errors for the row-level report.Example delivery
import.completed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "import.completed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "importId": "test_imp_1a2b3c0b", "entityType": "order", "status": "completed", "mode": "commit", "rowsProcessed": 100, "rowsCreated": 96, "rowsUpdated": 0, "rowsSkipped": 0, "rowsFailed": 4, "startedAt": "2026-09-10T11:59:00.000Z", "finishedAt": "2026-09-10T12:00:00.000Z" } }
Buyer-facing scripts
-
script.loaded— A script mounted successfully on a buyer-facing surface. HIGH VOLUME by nature — one per script per page load. Subscribe only if you actually want the firehose; script.blocked and script.error are the monitoring signals.{ scriptId, sha256, surface, outcome, detail, sessionId }. `sha256` is the exact bytes that ran, so it can be matched against the script inventory.Example delivery
script.loaded{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "script.loaded", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "scriptId": "1a2b3c0b-0000-4000-8000-000000000000", "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "surface": "checkout", "outcome": "loaded", "detail": {}, "sessionId": "sess_1a2b3c0c000040008000000000000000" } } -
script.blocked— A script resolved and was delivered, then was refused at mount. Read `detail.reason` — today the only value is `consent`, meaning the buyer had not granted the script's category.{ scriptId, sha256, surface, outcome, detail: { reason }, sessionId }.Example delivery
script.blocked{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "script.blocked", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "scriptId": "1a2b3c0b-0000-4000-8000-000000000000", "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "surface": "checkout", "outcome": "blocked", "detail": { "reason": "consent" }, "sessionId": "sess_1a2b3c0c000040008000000000000000" } } -
script.error— A script failed: its source would not load, its bytes did not match the pinned hash, it threw while mounting, or it tried to navigate its own frame. `detail.message` says which.{ scriptId, sha256, surface, outcome, detail: { message }, sessionId }.Example delivery
script.error{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "script.error", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "scriptId": "1a2b3c0b-0000-4000-8000-000000000000", "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "surface": "checkout", "outcome": "error", "detail": { "message": "sha256 mismatch: fetched bytes do not match the pinned hash" }, "sessionId": "sess_1a2b3c0c000040008000000000000000" } }
Shipping & Tax
-
shipping_tax.provider_connection.unhealthy— A shipping or tax provider connection auto-disables after repeated failures (consecutive_failures reaches threshold).{ connection_id, axis, provider, environment, environment_id, application_id, workspace_id, consecutive_failures, last_error_reason, last_error_detail, last_error_at, fallback_policy }Example delivery
shipping_tax.provider_connection.unhealthy{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "shipping_tax.provider_connection.unhealthy", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "connection_id": "test_conn_1a2b3c0b", "axis": "shipping", "provider": "easypost", "environment": "production", "environment_id": "test_env_1a2b3c0c", "application_id": "test_app_1a2b3c0d", "workspace_id": "ws_example", "consecutive_failures": 5, "last_error_reason": "rate_limited", "last_error_detail": "API rate limit exceeded; connection auto-disabled", "last_error_at": "2026-09-10T12:00:00.000Z", "fallback_policy": "strict" } }
Workspace payment methods
-
workspace.payment_method.added— Internal-only — fired when a platform-billing payment method is attached to a workspace.{ workspace, paymentMethod }Example delivery
workspace.payment_method.added{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.payment_method.added", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "paymentMethod": { "id": "pm_sample", "brand": "visa", "lastFour": "4242", "expMonth": 12, "expYear": 2028 } } } -
workspace.payment_method.removed— Internal-only — fired when a platform-billing payment method is hard-deleted from the workspace.{ workspace, paymentMethod }Example delivery
workspace.payment_method.removed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.payment_method.removed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "paymentMethod": { "id": "pm_sample", "brand": "visa", "lastFour": "4242", "expMonth": 12, "expYear": 2028 } } } -
workspace.payment_method.default_changed— Internal-only — fired when the workspace primary (default) payment method changes.{ workspace, paymentMethod, previousPaymentMethod? }Example delivery
workspace.payment_method.default_changed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.payment_method.default_changed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "paymentMethod": { "id": "pm_sample", "brand": "visa", "lastFour": "4242", "expMonth": 12, "expYear": 2028 } } } -
workspace.payment_method.backup_set— Internal-only — fired when the workspace backup payment method is set or cleared.{ workspace, paymentMethod }Example delivery
workspace.payment_method.backup_set{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.payment_method.backup_set", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "paymentMethod": { "id": "pm_sample", "brand": "visa", "lastFour": "4242", "expMonth": 12, "expYear": 2028 } } } -
workspace.payment_method.backup_used— Internal-only — fired when the renewal or dunning charge succeeded on the backup PM after the primary failed.{ workspace, primaryPaymentMethod, backupPaymentMethod, charge }Example delivery
workspace.payment_method.backup_used{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.payment_method.backup_used", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "primaryPaymentMethod": { "id": "pm_primary_sample", "brand": "visa", "lastFour": "4242" }, "backupPaymentMethod": { "id": "pm_backup_sample", "brand": "mastercard", "lastFour": "5454" }, "charge": { "amountCents": 12500, "currency": "USD" } } }
Platform billing charges
-
workspace.platform_charge.succeeded— Internal-only — fired after a successful platform-billing charge (initial subscribe or renewal).{ workspace, invoice, charge }Example delivery
workspace.platform_charge.succeeded{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.platform_charge.succeeded", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "invoice": { "number": "INV-sample-001", "periodStart": "2026-04-23T00:00:00Z", "periodEnd": "2026-05-23T00:00:00Z" }, "charge": { "id": "charge_sample", "amountCents": 10000, "currency": "USD" } } } -
workspace.platform_charge.failed— Internal-only — fired after a platform-billing charge fails (initial or renewal).{ workspace, charge, error }Example delivery
workspace.platform_charge.failed{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.platform_charge.failed", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace" }, "charge": { "id": "charge_sample", "amountCents": 10000, "currency": "USD" }, "error": "Card was declined." } }
Workspace lifecycle
-
workspace.trial_expired— Internal-only — fired by trial-expiry-runner when a workspace trial lapses without conversion.{ workspace, expiredAt }Example delivery
workspace.trial_expired{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "workspace.trial_expired", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "workspace": { "id": "ws_sample", "name": "Sample Workspace", "slug": "sample" }, "expiredAt": "2026-09-10T12:00:00.000Z" } }
Extension lifecycle
-
extension.uninstalled— An extension installation ends — a merchant uninstalls it, or staff take the extension down. Delivered to the uninstalled installation's own endpoint without a subscription, and to any subscribed endpoint.{ installationId, extensionId, applicationId, uninstalledAt, reason }Example delivery
extension.uninstalled{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "extension.uninstalled", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "installationId": "test_inst_1a2b3c0b", "extensionId": "test_ext_1a2b3c0c", "applicationId": "test_app_sample", "uninstalledAt": "2026-09-10T12:00:00.000Z", "reason": "merchant" } } -
extension.webhook_secret_rotated— The signing secret of an installation's webhook endpoint was rotated. Delivered to that endpoint, signed with BOTH the outgoing and the new secret until previousSecretExpiresAt, so the extension can fetch the new secret from GET /api/v1/installations/:id/webhook-secret before the old one stops verifying. Never carries a secret.{ installationId, extensionId, endpointId, rotatedAt, previousSecretExpiresAt }Example delivery
extension.webhook_secret_rotated{ "id": "1a2b3c07-0000-4000-8000-000000000000", "type": "extension.webhook_secret_rotated", "version": "1", "createdAt": "2026-09-10T12:00:00.000Z", "environmentId": "11111111-1111-4111-8111-111111111111", "environmentKind": "production", "workspaceId": "22222222-2222-4222-8222-222222222222", "data": { "installationId": "test_inst_1a2b3c0b", "extensionId": "test_ext_1a2b3c0c", "endpointId": "test_wep_1a2b3c0d", "rotatedAt": "2026-09-10T12:00:00.000Z", "previousSecretExpiresAt": "2026-09-11T12:00:00.000Z" } }
Event scope reference
Subscribing to an event requires holding the matching read scope. When you
create or update a webhook endpoint (or register an extension), every event in the
requested list is checked against the registering caller's scopes; any event you are
not allowed to read returns 403 with error.code = "insufficient_scopes" naming the
missing scope. See the
API key scopes reference
for the full scope catalog and grant rules.
The full mapping is available as JSON at GET /api/v1/event-types — each entry is { type, readScope, trigger, dataShape, requiredDataKeys, example } — plus a top-level
, so a build step can check its handlers against the live catalogue without
scraping this page. Payload shapes for every event are also typed in the conventions object stating the units every payload
uses (money is integer minor units, timestamps are ISO 8601 UTC)
@usethrottle/webhook-types
package: import the ThrottleEvent discriminated union
of ThrottleEventEnvelope variants and narrow on type to get the exact data shape per event.
application_scripts:read
-
script.blocked -
script.error -
script.loaded
billing:read
-
workspace.payment_method.added -
workspace.payment_method.backup_set -
workspace.payment_method.backup_used -
workspace.payment_method.default_changed -
workspace.payment_method.removed -
workspace.platform_charge.failed -
workspace.platform_charge.succeeded -
workspace.trial_expired
carts:read
-
cart.abandoned -
cart.checkout_started -
cart.converted -
cart.created -
cart.discount_applied -
cart.discount_removed -
cart.item_added -
cart.item_removed -
cart.item_updated -
cart.merged -
cart.shipping_cleared -
cart.shipping_selected -
cart.tax_recomputed -
cart.updated
customers:read
-
customer.created -
customer.deleted -
customer.email_verified -
customer.password_changed -
customer.payment_method_added -
customer.payment_method_removed -
customer.registered -
customer.sessions_revoked -
customer.updated
discounts:read
-
discount.applied -
discount.removed
extensions:read
-
extension.uninstalled -
extension.webhook_secret_rotated
fulfillment_digital:read
-
fulfillment.digital.delivered
fulfillments:read
-
fulfillment.cancelled -
fulfillment.completed -
fulfillment.created -
fulfillment.shipment.delivered -
fulfillment.shipment.shipped
imports:read
-
import.completed
invoices:read
-
invoice.past_due
order_returns:read
-
return.created -
return.updated
orders:read
-
order.cancelled -
order.closed -
order.comp_reversed -
order.comped -
order.created -
order.fulfilled -
order.held -
order.hold_released -
order.sync_conflict -
order.sync_failed -
order.updated
payment_disputes:read
-
payment.dispute_cleared -
payment.disputed
payment_refunds:read
-
payment.partially_refunded -
payment.refund_failed -
payment.refunded -
subscription.invoice_refunded
payments:read
-
payment.authorized -
payment.captured -
payment.expired -
payment.failed -
payment.pending -
payment.processing -
payment.recorded -
payment.vaulted -
payment.voided
quotes:read
-
quote.accepted -
quote.comment_added -
quote.converted -
quote.declined -
quote.expired -
quote.proposed -
quote.requested -
quote.revision_requested -
quote.viewed
shipping_tax:read
-
shipping_tax.provider_connection.unhealthy
subscriptions:read
-
subscription.activated -
subscription.backup_pm_used -
subscription.cancelled -
subscription.create_failed -
subscription.created -
subscription.past_due -
subscription.paused -
subscription.payment_failed -
subscription.plan_change_scheduled -
subscription.plan_changed -
subscription.renewed -
subscription.resumed -
subscription.trial_blocked -
subscription.updated
Retries
Non-2xx responses and network failures are retried with delayed backoff and are visible in webhook delivery logs. Design handlers to return 2xx only after the event is durably stored or safely ignored as a duplicate.
The retry curve is fixed at [5m, 15m, 1h, 6h, 24h]
(max five attempts). After the final attempt, deliveries are marked dead_letter and stop retrying automatically; an
operator can replay them via
POST /api/v1/webhook-deliveries/{deliveryId}/replay
.
Failures the destination can never recover from skip the curve entirely and are marked dead_letter on the first attempt: a malformed URL, a
non-HTTPS scheme, a hostname that does not resolve, a hostname that resolves to a private
or link-local address, and a payload over the size limit. The reason is recorded on the
delivery’s lastError; fix the endpoint URL and
replay. A temporary DNS failure is not in this set and is still retried.
sequenceDiagram
participant T as Throttle
participant Q as Delivery Queue
participant E as Your Endpoint
participant Op as Operator
T->>Q: emit event (e.g. payment.captured)
Q->>E: attempt 1
E-->>Q: 5xx / timeout
Note over Q: wait 5m
Q->>E: attempt 2
E-->>Q: 5xx / timeout
Note over Q: wait 15m
Q->>E: attempt 3
E-->>Q: 5xx / timeout
Note over Q: wait 1h
Q->>E: attempt 4
E-->>Q: 5xx / timeout
Note over Q: wait 6h
Q->>E: attempt 5
E-->>Q: 5xx / timeout
Note over Q: wait 24h, max attempts reached
Q->>Q: mark delivery dead_letter
Op->>T: POST /api/v1/webhook-deliveries/{deliveryId}/replay
T->>Q: requeue
Q->>E: manual retry attempt
E-->>Q: 2xx
Q->>Q: mark delivered Manage endpoints
Endpoints have three states: active, paused, and deleted. Pause stops dispatch without losing the subscription; delete is a soft remove that excludes the endpoint from the list and from future dispatch.
-
PATCH /api/v1/webhook-endpoints/{id}updatesurl,enabledEvents, orisActive. Send{ "isActive": false }to pause and{ "isActive": true }to resume. -
DELETE /api/v1/webhook-endpoints/{id}soft-deletes the endpoint. Deleted endpoints cannot be recovered through the API; create a new one if you need to resubscribe. -
GET /api/v1/webhook-endpointsreturns both active and paused endpoints; deleted are excluded. -
GET /api/v1/webhook-endpoints/coveragereports subscribable event types your application emitted that no active endpoint is subscribed to. A handler written for an event you never subscribed to simply never runs, and the delivery log cannot show that — it only records what was sent, so the gap is indistinguishable from the event never happening. Defaults to a 30-day window; pass?days=(1–90) to widen it. Paused endpoints do not count as coverage. The same list appears on the webhooks page in the dashboard.Coverage response{ "data": { "windowDays": 30, "unsubscribed": [ { "type": "subscription.paused", "count": 12, "lastEmittedAt": "2026-07-27T09:24:11.000Z" } ] } }
order.completed was renamed to order.fulfilled when an order's lifecycle status absorbed its fulfillment state — nothing emits
the old type any more. Endpoints that were subscribed to it have been migrated to order.fulfilled in place, so no action is needed and no
deliveries were lost. The old name is accepted on write and dropped from what gets
saved, like any other retired type (below), so an integration that still sends it in an enabledEvents array will not 400 — but it will not
receive anything either. Subscribe to order.fulfilled.
enabledEvents replaces the whole list, so the usual
way to add one subscription is to read the endpoint and send its current array back with
the new type appended. If we have retired an event type since your endpoint was created,
that stored value is still in the array we hand you — and write requests accept it rather
than rejecting the round-trip. Retired types are dropped before anything is saved, and the
response lists them under retiredEventsIgnored so the removal is visible rather
than silent:
{
"data": {
"id": "wep_01HZX...",
"enabledEvents": ["payment.captured", "subscription.updated"],
"retiredEventsIgnored": ["cart.expired"]
}
} 400 no_deliverable_events — saving it would leave the
endpoint subscribed to nothing at all.
Inspect deliveries
GET /api/v1/webhook-deliveries returns recent
delivery attempts. Paginate via limit (1–200, default
50) and offset (default 0). Response shape is { data: { items, total, limit, offset, hasMore } }
; keep paging until hasMore is false.
Narrow the list with endpointId, status (one of pending, delivered, failed, dead_letter) and eventType (an exact event name, e.g. payment.failed). Filtering happens server-side, so status=failed returns every failed delivery across
the whole result set rather than only those on the page you happened to load.
GET /api/v1/webhook-deliveries/{id} returns one
delivery in full: the exact payload envelope that was
signed and POSTed, its status, lastStatus/lastError,
and a signatureScheme descriptor (header name, the t=…,v1=… format, and the signed {t}.{rawBody} string). Use it to debug signature
verification without reverse-engineering the headers from live traffic.
This is the first place to look when something is not happening and nothing is erroring: it separates “never sent” from “sent, and your handler swallowed it”, which have identical symptoms and completely different fixes. It is also the right source for test fixtures — a mock that returns a friendlier shape than production makes tests pass while the integration is broken, so replay a captured payload rather than writing what you think the shape is. See Failures with no error.
Send a test event
POST /api/v1/webhook-endpoints/{id}/test fires a
synthetic, signed delivery so you can verify reachability and signature handling. Omit the
body to send a default payment.captured sample, or pass { "eventType": "subscription.renewed" } for a
realistic per-event stub. To exercise your own handler logic (org lookup, subscription
status updates), pass an eventType plus a data object — it is delivered verbatim as the envelope data instead of the built-in stub.