Skip to main content

Webhooks to Retail

Webhooks are the one direction that runs from your integration to Retail. They are optional and make things faster. Retail already checks every open order on its own schedule (order state hourly, fulfilment every 10 minutes), so a missed webhook delays a sale but never loses one. Send one when an order is accepted, rejected or cancelled, or when a ticket becomes retrievable.

Endpoint

{vendorKey} is the supplier identifier Gomry assigns you. It identifies you. It does not authenticate you.

A webhook is a wake-up call

Retail never changes an order’s state from a webhook body. A payload that says “accepted” is a claim, and acting on it would let a forged or buggy message create a payment to you for a purchase that never happened. This is true even when the signature is valid: a signature proves who sent the bytes, not that what they say is right. So Retail reads one thing out of the body, an order reference, and then asks you through your own Search orders endpoint. Delivery follows through Fulfilment. Those endpoints are the truth. The webhook only has to say which order to look at.

Payload

Keep it small. Retail reads an event type and an order reference from the top level of the body:
string
required
Retail’s own reference, exactly as it arrived on Place order. Retail passes this value straight to GET /api/vendor/v1/orders?reference=…, so it must be something your search endpoint can find.
string
A short name for what happened. Retail stores it and filters on it. Use your own vocabulary in lowercase, for example order.accepted or delivery.available.
Do not also send order_id, orderId or id at the top level. Retail takes the first identifier it finds, in this order: order_id, orderId, reference, id. A top-level order_id holding your own order number wins over reference, gets searched as a reference, matches nothing, and the webhook is recorded as delivered while nothing happens. Put your own ids inside a nested object if you want them in the record.
Also accepted, for systems that cannot send JSON: a form-encoded body, including one where a field holds a JSON string. The same field names apply. A body larger than 64 KB is rejected.

Authentication

Choose one mode with us when you go live. Signing is strongly preferred.
Sign the exact raw bytes of the body with HMAC-SHA256, using the webhook secret Gomry issued you, and send the hex digest in a header. The default header is X-Vendor-Signature. We can configure a different name if your system already uses one.
A sha256= prefix is also accepted. Sign the bytes you send, not a re-serialized object: re-encoding JSON does not reliably reproduce them.
Node.js
Once signing is switched on for you, an unsigned delivery is rejected with 401.

Redeliveries

Send a unique id per delivery in X-Webhook-Id (X-Delivery-Id and X-Request-Id also work). Retail uses it to recognize a redelivery and process it once. Without one, Retail treats two byte-identical bodies as the same delivery.

Responses

Retail answers 200 as soon as a webhook is stored, even if its own processing then fails. That is deliberate: a 500 would ask you to send it again and amplify an outage on our side. A failed reconciliation stays recorded and is retried by Retail’s sweep. stored_only means the body had no reference Retail could read. It is kept, and nothing is done with it. If you see it for order events, check the field names above.
200