Every Merchant Callback event follows the same rules for how requests arrive, how responses must be shaped, what happens when your endpoint fails, and how to prove a request came from Bolt. Those rules are on this page. The individual event pages cover only the fields specific to each event.

The request envelope

Bolt sends every event as an envelope of event and data, and the payload for that event sits inside data:
This is the same whether you registered one Universal Merchant API URL or individual endpoints per event. There is no unwrapped variant. The event names are exact. A common mistake is discount.code.apply, which is not a real event:

The response envelope

Your response uses the same envelope. Echo the event you are answering, set a status, and put the body inside data:
Three rules govern whether Bolt accepts it:
A flat response such as {"status": "success", "display_id": "..."} is rejected. HTTP 200 does not make it succeed, because Bolt looks for the body inside data and finds nothing there.

When your endpoint fails

A rejected or failing response does not always stop the checkout. What happens depends on your division, and the custom cart default is the quiet one: For a custom cart this means a broken callback produces successful payments with no matching orders, and nothing in the shopper’s experience indicates a problem. Alert on your own callback error rate rather than expecting checkout to surface it. To fail on purpose, set status to failure and return a code. The field differs by event, so check the event’s reference page:
See error codes for the codes to use.

Amounts and currency

The same field name can be a number or an object depending on which event you are handling. The rule is that events carrying a full cart snapshot use the expanded form, and events carrying a working cart use plain integers: Amounts are always in the currency’s minor units, so 41200 is $412.00.
Type your inbound carts per event rather than sharing one cart model across all of them. Reusing a single strict model is the most common cause of a callback returning an error on one event while working on the others.

Delivery option signatures

Shipping options, pickup options, and ship to store options all carry a signature field. Despite the name it is not a cryptographic value and Bolt does not verify it. It forms part of the cache key for the tax result attached to an option. Give each option a value that is stable for as long as the option means the same thing, and change it whenever the option changes in a way that should invalidate its tax. Omitting it disables tax caching for that option rather than failing the request.

Verifying the request came from Bolt

Every callback is signed. Compute the base64 encoded HMAC SHA-256 of the raw request body using your signing secret, then compare it to the X-Bolt-Hmac-Sha256 header:
TypeScript
Sign the raw body exactly as received. Parsing and re-serializing the JSON first changes the bytes and the signature will not match. Two cases differ:
  • GET endpoints, such as the OAuth login callback, sign the query string rather than a body.
  • While a signing secret is being rotated, Bolt also sends X-Bolt-Hmac-Sha256-Pending, holding the same body signed with the incoming secret. Accept the request if either header matches, so that verification keeps working across the rotation.