Skip to main content
Webhooks push events to your server the moment they happen — no polling. You register an HTTPS endpoint, choose which events to receive, and Crevio sends a signed JSON POST whenever one fires. Use them to sync data, trigger your own automations, fulfill purchases, and feed analytics — anything where your code needs to react to something happening in Crevio.
Webhooks notify your server so your code acts. If you instead want Crevio’s AI to act on an event, use an event-triggered Task. See How webhooks differ from event-triggered Tasks.

Creating an endpoint

Create endpoints from the dashboard, via the API, or with the TypeScript SDK.

Via dashboard

1

Open Developer settings

In your dashboard, go to Settings → Developer.
2

Create a new endpoint

Click New webhook endpoint and enter the HTTPS URL that should receive events. (HTTP is allowed only in local development.)
3

Select events

Choose which event types to subscribe to, individually or all at once.
4

Save and copy the secret

Crevio generates a signing secret (prefixed whsec_) shown once. Copy it now — you’ll use it to verify incoming requests.

Via API

Like every Crevio endpoint, params go at the top level (no {"webhook_endpoint": {...}} wrapper). Subscribe with enabled_events.
The response includes a secret — returned only on creation:
Copy your signing secret immediately. It’s shown once on creation and cannot be retrieved later — you’ll need it to verify signatures.
The url can point at a Crevio site that only signed-in visitors can open, on any path. Crevio’s deliveries to your own account’s sites pass its sign-in check, while anyone else is still asked to sign in. Verify the signature in your handler all the same, as you would on a public site. List the available event types programmatically:

Testing an endpoint

Send a synthetic event to any active endpoint to verify your handler:
event_type must be one the endpoint is subscribed to. A test delivery is signed like a real one, but it carries "test": true, "livemode": false and an empty data.object — acknowledge it as soon as the signature checks out, or your own validation will fail a test that was never going to carry an order. The response is what your server answered, so a handler that rejects the event tells you why:
delivered is true for any 2xx. If nothing answers at all — DNS failure, refused connection, timeout — the call returns 502 instead. The test event is signed like a real one, so it exercises your signature check. You can also test from the dashboard: Settings → Developer → Send test, then pick an event type. Test deliveries include "test": true in the body and are not persisted to your delivery history.
Use webhook.site or ngrok during development to inspect payloads without standing up a server.

Testing a paid order end to end

A test event proves your endpoint is reachable and your secret is right. To run your real fulfillment code on a real order without charging a card, make a test purchase:
1

Create the checkout the way your site does

POST /v1/checkouts with your line items and metadata, exactly as in production. See Checkouts.
2

Complete it as a test purchase

POST /v1/checkouts/{id}/test_purchase with a key or OAuth token of an account admin. Or open its purchase_url in a browser signed in to Crevio as an admin of the store: the card form is replaced by a Test purchase notice.
3

Watch your endpoint

No card is charged and Stripe is not called. Crevio creates the order and sends order.created and order.paid to your endpoint, plus subscription.created when the checkout starts a subscription.
Each checkout can be completed once, so create a new one for every run. An account can place 5 test purchases a minute. These are ordinary events, not test events: they have no test flag, they are signed and retried like any other, and data.object is the full order. The checkout’s metadata is copied onto the order, so the key your site saved before checkout comes back in the event. What marks the order as a test is livemode. It is on the event itself and on the order:
Branch on the event’s livemode wherever your handler acts on the outside world. Every event has it, including events whose data.object has no livemode of its own, so one check covers all of them:
The order’s customer is the signed-in owner who made the test purchase, not the email or customer on the checkout, so Crevio’s own receipt goes to you. A test order uses up no stock and doesn’t count toward sales. Its amount is what the checkout would have charged, and charge is null. The rest of the order’s life works without Stripe too, and every event it sends has livemode: false: Test subscriptions renew and end on an hourly check, so subscription.renewed and subscription.ended can arrive up to an hour after the period closes. A test renewal sends no renewal receipt, because nothing was charged. GET /v1/refunds and GET /v1/refunds/{id} return test refunds too, with livemode: false.
The event goes to the URL registered on your endpoint, nowhere else. If your site has a preview copy with its own database, an order saved there will not be found by the live site when the event arrives. Test from the site your endpoint points to.
There is no Stripe test mode for checkouts, so test card numbers like 4242 4242 4242 4242 are declined. A test purchase covers everything except the card charge itself.

Available events

These are the event types you can subscribe to via enabled_events. Each has a dedicated page under Webhook Events in the API reference showing its exact payload.

Commerce

subscription.canceled means the customer keeps access until ends_at. A subscription that is canceled immediately skips it and sends only subscription.ended. These events fire however the change was made — the API, the customer’s billing portal, or Stripe.

Subscription renewals

Every paid renewal of a subscription or payment plan is recorded as a new order. Each one sends order.created and order.paid, then subscription.renewed. Tell a renewal apart from a purchase by the order’s billing_reason, named as Stripe names the invoice: Both subscription values come with subscription, the id of the subscription (sub_…) the order started or renewed. A renewal order has the purchase’s line_items and customer, its own charge and amount, checkout: null, and empty metadata: the metadata you set on the checkout stays on the purchase order, so your own order numbers are never reused. A handler that fulfils purchases on order.paid should branch on it, or every renewal will look like a new sale:
A renewal paid in full by a discount charges nothing and creates no order, but still sends subscription.renewed. A subscription that starts with a free trial has no payment to tie it to an order, so its order is null. customer is always present.

Leads & customers

Filter on data.tag.id to react to one specific tag. Bulk CSV imports and deleting a tag outright do not send per-customer tag events.

Catalog

AI Tasks & Jobs

Email

Social

Payload format

Every delivery is an HTTP POST with a JSON body in this shape:
data.object is the resource exactly as the API returns it — an order.paid event carries the same object as GET /v1/orders/{id}, so one parser handles both. References are ids you can fetch (checkout, order), and the ones you almost always need arrive already expanded: customer on checkout, order, refund, invoice and subscription events, plus line_items on orders and order.line_items on subscriptions. Refund events carry the refunded order as data.object — its amount_refunded and status reflect the refund. Dispute events carry the disputed order. On order.disputed its status is disputed. On order.dispute_closed the status tells you the outcome: back to succeeded (or partially_refunded) when the dispute was won or the inquiry closed, still disputed when it was lost. A lost dispute also removes the buyer’s access, as a full refund does, except on a renewal order: access there comes from the subscription, which keeps running. Tag events carry the customer as data.object and the tag that was added or removed as data.tag.

Delivery history

Each delivered event is persisted as a webhook event you can inspect. List your delivery history:
Narrow the list to one endpoint with ?webhook_endpoint=wh_abc123, or by status and event_type. Fetch a single delivery with GET /webhook_events/{id}; add ?expand[]=request to see the response code, response body and attempt count.

Redelivering an event

Send a delivered or failed event again, unchanged:
The redelivery carries the same id and the same payload, so a handler that deduplicates on id treats it as the event it already saw. Events are kept for 7 days. The call returns 409 while the event is still being delivered, or when its endpoint is disabled.

Versioning

Every endpoint is pinned to a payload version — api_version on the endpoint, repeated in each event. A new endpoint is pinned to the current version, and that never changes for you: when a payload changes in a way that could break a parser, it ships as a new version, and existing endpoints keep receiving the one they were built against. Opt in when you are ready:
New fields and new event types are not breaking changes and reach every version, so write parsers that ignore what they don’t recognise. The current version is v1.

Retry behavior

Crevio expects a 2xx response within 5 seconds. Redirects are not followed. Anything else — a timeout, a 4xx, a 5xx — counts as a failure, and the event is retried on this schedule: That is about three days in all. After the last attempt the event is marked failed; you can still redeliver it for 7 days. Delivery is at least once, and order is not guaranteed — a retry of an older event can land after a newer one. Deduplicate on id, and fetch the resource from the API when order matters.

When an endpoint keeps failing

  • After 24 hours in which every delivery failed, the account owner gets a warning email.
  • After 72 hours, the endpoint is switched to inactive and the owner is told. Events still waiting to retry are marked failed.
One successful delivery at any point resets both clocks. Once the receiving end is fixed, set the endpoint back to active and redeliver what it missed.

Endpoint statuses

Deactivate an endpoint without deleting it; reactivating resumes delivery for new events. Crevio sets inactive itself only after 72 hours of failures.

Security

Verifying signatures

Crevio signs deliveries to the Standard Webhooks specification, so you verify them with an existing library instead of writing HMAC code. Every request carries three headers:
The libraries also reject a webhook-timestamp more than five minutes old, which is what stops a captured request being replayed.
The signature is the base64 HMAC-SHA256 of <webhook-id>.<webhook-timestamp>.<raw body>. The key is the bytes your secret encodes: strip the whsec_ prefix and base64-decode the rest — do not use the secret’s text as the key. Compare in constant time against the part of webhook-signature after v1,, and reject a timestamp more than five minutes old.
Always verify the signature before processing a payload. Without it, anyone could forge events to your endpoint.

Best practices

  • Respond fast — return 200 immediately, then process asynchronously to avoid timeouts.
  • Deduplicate — use the event id (whev_…); the same event may arrive more than once.
  • Use HTTPS — required in production.
  • Protect your secret — store it in env vars or a secrets manager; never commit it.

Output formats

Beyond the default JSON, Crevio can format deliveries for chat platforms: Select the format when creating or editing the endpoint.

How webhooks differ from event-triggered Tasks

Both react to platform events, but they do different jobs: The subscribable internal events (and any third-party triggers) are documented under Events.

Next steps

Events

Internal and third-party triggers for event Tasks.

Tasks

Let Crevio’s AI react to events on your behalf.

Autonomous outreach

A worked event-triggered Task on order.paid.

API overview

The full set of primitives.