Report the order
We append an identifier to the cart link. If a purchase follows, your shop reports the order to our endpoint and the referral is attributed.
What to do
- Tell us which parameter names your shop or your affiliate system expects, and carry the values from the link over into your order. Done when the identifier shows up in your order data after a purchase.
- Point the webhook at
https://choicera.com/api/webhooks/shopify, for the three events paid, cancelled and refunded. Done when a test order arrives on our side and we confirm receipt to you. - Agree the signing secret with us and sign every request with it. Done when your deliveries get HTTP 200 and no 401.
Identifiers in the link
We pass an affiliate identifier and a sub identifier so a purchase can be attributed both to our account and to the referring party, a gym for example. We agree the names with you.
| Purpose | Example | Note |
|---|---|---|
| Affiliate identifier | ?ref=choicera | Parameter name and value come from you. |
| Sub identifier | &subref=studio-123 | Differs per cart. Contractually usually to be reported back per line item. |
| Coupon | &coupon=CHOICERA | Only if your shop redeems codes via the URL. |
| Transaction ID | &attributes[choicera_attribution]=… | On Shopify. The value lands on the order and comes back in the webhook. |
If you work with an affiliate network, we send that network's parameters, for example
?clickref=…&clickref2=… for Awin or the program's own reference for UpPromote.
Tell us which system counts.
Without a transaction ID we attribute an order via the email address. It must then match the address in the Choicera account exactly, including case.
Set up the webhook
POST https://choicera.com/api/webhooks/shopify
The field set follows the Shopify order format. Shops without Shopify call the same endpoint with the same contract: same headers, same field names, same signature. In full as a file: order-webhook.openapi.yaml (OpenAPI 3.1).
Events
The reported event is in the x-shopify-topic header. We process three. Others we
answer with HTTP 200 and discard.
| Topic | Meaning |
|---|---|
orders/paid | Paid order with the order data |
orders/cancelled | Cancellation, reverses the attribution |
refunds/create | Refund, reports the refunded amount |
Headers
| Header | Meaning |
|---|---|
x-shopify-topic | Event name, for example orders/paid |
x-shopify-hmac-sha256 | Base64-encoded HMAC-SHA256 signature over the unmodified request body. If it is missing or invalid we answer HTTP 401. |
x-shopify-webhook-id | Unique delivery ID. The same value on every retry of the same delivery, that is what keeps processing idempotent. A value reused for a different event is treated as a retry. Without the ID we accept the delivery, but it is not protected against double processing. |
x-shopify-shop-domain | Shop domain, optional, helps when tracing errors |
The signing secret is agreed during onboarding and applies to your shop only. We verify every delivery against the secret of the shop it came from, identified by the x-shopify-shop-domain header. Without that header, or with a shop domain unknown to us, we answer HTTP 401. We enable the integration before it goes live.
Shopify shops create the three events in the Shopify admin under Settings, Notifications,
Webhooks, pointing at our URL.
Which fields we evaluate
| Field | Meaning |
|---|---|
id | Order ID in the shop system |
order_number | Human-readable order number |
created_at | Date and time per ISO 8601, alternatively processed_at |
currency | Currency, for example EUR |
total_price, subtotal_price, total_discounts | Gross total, subtotal before shipping, sum of discounts |
discount_codes | Redeemed coupon codes with amount and type |
email | Customer email, alternatively contact_email |
note_attributes | Carries the transaction ID choicera_attribution from the cart link, if set up. Evaluated for attribution only, not stored. |
line_items | Line items with product_id, variant_id, sku, title, variant_title, quantity as a number and price |
For orders/cancelled the order id is enough. For
refunds/create we evaluate id, order_id and
transactions. We sum transactions with kind = refund as well as entries
without kind. So please do not include other transaction kinds.
What we store
We store an order only if it can be attributed to an open Choicera purchase: via the transaction ID from the cart link, valid for 48 hours from handoff, or via the email address of a Choicera account. Each attribution is used once. A second order with the same identifier and no new Choicera cart is discarded.
An HTTP 200 response confirms receipt, not storage. Orders without attribution are discarded. Only the fields listed above are stored. The order data remains with Choicera permanently as the customer's purchase history, there is no time-based deletion. On an erasure request under Art. 17 GDPR, Choicera removes the email address and product details. Invoicing data without product reference remains stored for commission accounting and statutory retention obligations.
For naming this in your privacy policy there is a wording template.
Responses and retries
| Response | Meaning |
|---|---|
| 200 | Receipt confirmed |
| 400 | JSON body not readable |
| 401 | Signature missing or invalid |
| 5xx | Error on our side. Retry the delivery later with the same x-shopify-webhook-id. |
If the endpoint keeps answering webhook_not_configured, the integration is not
enabled on our side yet. Contact us via the
partner contact instead of retrying further.
