Skip to main content
Version: Latest
User ManualNeed configuration, code or deployment details? In the Technical Manual: Commerce Webhooks

Webhooks

A webhook tells another system the moment something happens in your store. When an order is paid, for example, your store posts a short JSON message about it to an address you give, such as your accounting system's or your warehouse's. That system doesn't have to keep asking your store what's new.

Use webhooks to:

  • send paid orders to a warehouse or a fulfillment service;
  • record orders and refunds in your accounting system;
  • keep your stock count in step on a marketplace;
  • email or message your team when stock runs low;
  • add new customers to your mailing or CRM system.
MenuCommerce > Webhooks
PermissionManage commerce webhooks
FeatureCommerce Webhooks
Can't find it?

What you see depends on your role. If the menu, a button or a setting on this page is missing, ask your administrator to give you the permission listed above or to turn on the feature. Only administrators can turn features on and change site settings.

The system that receives the messages is called the receiver. Whoever builds it needs the address it listens on, and the webhook's signing secret, to check that each message really came from your store.

Add a webhook​

  1. Open Commerce > Webhooks and click Add webhook.
  2. Fill in the fields (below).
  3. Click Save.

The webhook was added. Copy its signing secret into the system that receives it. confirms it, and the webhook's page opens with its Signing secret. Copy it into the receiver.

FieldWhat it does
NameWhat you call it, such as the system it feeds: Warehouse or Accounting. Required.
URLThe receiver's full address, starting with https://, such as https://hooks.contoso.com/store. An address on your own network, such as https://10.0.0.5/ or https://192.168.1.20/, isn't accepted. A plain http:// address works only for a receiver on the same machine while the site runs in development.
OnTurn it off to stop sending without losing the webhook. Messages waiting to be tried again aren't sent either: they're marked Failed, with The webhook was turned off.
EventsThe events to send (below). Tick Every event, those added later too to send them all, including events added to the store later. Choose at least one.

Commerce > Webhooks lists your webhooks, each with whether it's On or Off and how many events it sends. Click a webhook's name, or Edit, to change it. Delete, on its page, removes it and its deliveries log: nothing more is sent to it.

The events​

Each event has a name, which the receiver reads, and a label you see when choosing events. The events you can choose depend on the commerce features your store has turned on.

EventLabelSent whendata holds
order.createdOrder placedAn order is made, such as when a buyer checks out their cart, before it's paid. It's sent once the order is saved and its stock is held: an order refused because something ran out sends nothing. Checking out the same cart again makes a new order and quietly cancels the earlier one, which sends no event.order
order.paidOrder paidThe order's checkout is completed. An order placed to pay offline, such as by bank transfer, is sent when it's placed, before the money arrives.order
order.canceledOrder canceledThe order is canceled, by your store or because its checkout didn't finish.order, and reason
order.refundedOrder refundedMoney is given back on the order, once for each refund, in full or in part.order, and refund
customer.createdCustomer createdA customer record is made: for a guest who buys, the first time a signed-in buyer needs one (their first checkout, or opening My account), when you add one under Commerce > Customers, or when a recipe adds one.customer
shipment.shippedShipment shippedA shipment is marked shipped, with its carrier and tracking.shipment, orderNumber and customerEmail
shipment.deliveredShipment deliveredA shipment is marked delivered.shipment, orderNumber and customerEmail
stock.changedStock changedWhat's available of a product or variant changes: a sale, units held while a buyer pays and let go again, units received, an adjustment or a count. Several changes saved together send one event. A move between locations doesn't change what's available, so it sends nothing.stock, and previousAvailable
stock.lowStock running lowWhat's available drops to the item's low-stock level.stock, and previousAvailable
stock.sold-outStock sold outNothing is left, where there was before. It's sent instead of stock.low.stock, and previousAvailable

What's in data:

  • order is the order as the commerce API gives it: its id, number, status, the customer's name, email, phone and addresses, its lines, discounts, shipping, what paid toward it, its refunds and totals.
  • reason says why an order was canceled: canceledByStore, checkoutCanceled, checkoutExpired, paymentFailed, abandoned, or unavailable (something it needed couldn't be held).
  • refund is the refund just made: its id, amount, reason and createdUtc.
  • customer is the customer as the API gives them: their id, kind, name, email, phone and addresses.
  • shipment is the shipment as the API gives it: its number, status, carrier, tracking number and link, its lines and tracking events. orderNumber and customerEmail say whose it is.
  • stock is the stock item as the API gives it: its sku, title, what's onHand, reserved and available in all and at each location. previousAvailable is what was available before.

What the receiver gets​

Each event is a POST with a short JSON message. It says which event happened (event, such as order.paid), gives the event an id and the time it happened (createdUtc), and puts what the event is about in data, such as the whole order. Names and amounts are written the same way as in the commerce API, so a developer who knows the API reads them the same way. The Technical Manual shows a whole message.

Every message has these headers:

HeaderWhat it holds
X-Webhook-IdThe event's id, the same as id in the body. It's the same on every try, so the receiver can drop a message it has already handled. order.created and customer.created always have the same id for the same order or customer, such as evt_order-created_4d1k8s0p2w, so even if one is raised twice, the receiver can drop the repeat.
X-Webhook-EventThe event's name, such as order.paid.
X-Webhook-TimestampWhen this try was signed, in seconds since 1 January 1970 (UTC).
X-Webhook-Signaturev1= and the signature (below).

Check the signature​

Anybody can post to the receiver's address, so the receiver checks each message's signature before trusting it. The signature is an HMAC-SHA256, keyed with the webhook's signing secret, of the timestamp, a dot and the body, written in lowercase hexadecimal.

  1. Read the body exactly as it arrived, before parsing the JSON.
  2. Join the X-Webhook-Timestamp value, a dot (.) and the body.
  3. Work out its HMAC-SHA256 with the whole signing secret (whsec_ included) as the key, in lowercase hex, and put v1= in front.
  4. Compare it with X-Webhook-Signature. Refuse the message if they differ, or if the timestamp is more than a few minutes old: an old message may be a recording someone sends again.

Each try is signed again when it's sent, so a retry has a fresh timestamp.

Your developers find this check written out in C# and Node.js in Checking the signature, in the Technical Manual.

Keep the secret secret

Keep the signing secret in the receiver's settings or secret store, never in a web page or an app. If it may have leaked, click New secret on the webhook's page (below).

Deliveries and retries​

A message is sent right after the change that raised it is saved, so a slow or broken receiver never slows down your buyers.

  • The receiver has 15 seconds to answer. A 2xx status, such as 200, means it took the message: it's Delivered. A redirect isn't followed: it counts as not taken.
  • Any other answer, no answer in time, or no connection, and the message is tried again: after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours. That's 8 tries over nearly two days. If the last one fails too, the message is Failed, and isn't tried again unless you send it again yourself.
  • Every try sends the same body, with the same id.

So the receiver should:

  • answer quickly, with 200, and do slow work, such as calling other systems, afterwards;
  • drop repeats: a message can arrive more than once, for example when its answer was lost on the way back. Keep the ids it has handled, and skip one it has seen;
  • not depend on order: a retried message can arrive after a later one. Read the status and times in data, or get the latest from the commerce API, rather than assuming events come in order.

The deliveries log​

Click Deliveries, on the list or on a webhook's page, to see every message sent to the webhook, newest first. Each shows:

ShownWhat it means
EventThe event's name, such as order.paid. Point at it to see the event's id.
Pending, Delivered or FailedWaiting to be sent or sent again, taken by the receiver, or given up on: after the last try, or because the webhook was turned off or its secret can't be read.
The answerThe status code of the receiver's last answer, such as 200 or 500.
RaisedWhen the event happened.
AttemptsHow many times it has been tried.
Tried nextFor a pending message, when it's tried again.
The errorFor a message that wasn't taken, the receiver's answer (cut short) or why it couldn't be reached.

The status filter in the list header (Filter by status) shows Any status, Pending, Delivered or Failed messages.

Click Send again to send a message now, for example once you've fixed the receiver. It starts over: if it fails, it's tried again on the same schedule. Sent again; the receiver answered 200. confirms it was taken.

If a message failed with The webhook's signing secret can't be read. Give it a new secret, then send it again., the site can no longer read the secret it stored, for example after its encryption keys were lost. Click New secret, put it in the receiver, and send the failed messages again.

Delivered and failed messages are kept for 30 days, then deleted.

Send a test event​

On the deliveries log, click Send a test event to check the receiver is set up. It sends a webhook.ping event to this webhook only, signed like any other, whatever events the webhook takes. Its data holds the webhook's id and its Name, such as Warehouse.

The receiver answered 200. means it was taken. Otherwise the message says why, such as The receiver didn't take it: 401 Unauthorized, which usually means the receiver has a different secret. The test is listed in the log like any other message.

Change the signing secret​

On the webhook's page, click New secret next to Signing secret. The webhook has a new signing secret. Deliveries are signed with it from now on, so put it in the receiver. From that moment, every message, including retries of earlier ones, is signed with the new secret. Put it in the receiver straight away; messages it refuses in between are tried again later.