Docs

Orders and refunds

Sending orders and refunds from your server

Ad platforms count sales with cookies; when most visitors decline cookies, Google Analytics and Google Ads see only a small part of the sales your ads bring. When your server sends orders to adall, adall counts every sale, decides with real revenue and profit, and uploads the sales that have consent to Google itself. This page lets your developer do the whole setup; it takes about half a day.

1. Get the key

In the panel, open Settings › Connections › Send orders straight to adall › Setup details for your developer and press Create key. You get a key (X-Adall-Key) and a secret; the secret is shown once.

  • Put both in your server's environment variables (e.g. ADALL_KEY, ADALL_SECRET). Not in code, not in the repository, not in an admin setting.
  • If the key leaks, revoke it in the same place and create a new one; requests with the old one are refused at once.

2. When to send what

  • When a payment is final: one order event. Not a cart or a payment attempt: when the money is collected.
  • When a card refund is made: one refund event; for a partial refund only the refunded amount. Also send refunds made in your payment provider's dashboard (e.g. Stripe's charge.refunded webhook).
  • Parts paid with gift cards, balance or points are not a sale; value is what the customer was actually charged.

3. Event fields

  • event_id (required, up to 128): the event's permanent id. Sent twice, it counts once; e.g. order-<order no>, refund-<refund id>.
  • type (required): order or refund.
  • occurred_at (required): when the payment or refund happened, ISO 8601, within the last 400 days.
  • reference (required): your order number. A refund carries its order's number.
  • value.amount_minor and value.currency (required): amount in minor units (699.00 = 69900) and the three-letter uppercase currency.
  • profit.amount_minor (optional, recommended): the order's profit in the same currency (sale − cost of goods). If you do not know the cost, leave it out; adall then decides with revenue.
  • country: the order's country (billing or shipping, two letters). Country decisions use it.
  • click_ids: ids of the click the visitor came from: gclid, gbraid, wbraid (Google), fbclid, fbc, fbp (Meta), msclkid, ttclid. Read them from the landing URL and keep them until the order.
  • utm: source, medium, campaign, term, content. On Google campaigns adall builds, utm_campaign is the campaign id; adall puts the sale on its campaign with it. Like the click id, keep it with the order even without consent: it is not personal data. If most orders come without a campaign, adall cannot decide on its own count.
  • customer.email_sha256: SHA-256 of the lowercased, trimmed e-mail. customer.external_id: your customer number. Plain e-mails or phone numbers are refused.
  • consent: step 5 below.
  • session.landing_url, session.first_touch_at: the first visit's address and time (optional).

4. Signature and request

POST https://live.adall.io/api/v1/events, body {"schema_version":1,"events":[…]}; up to 100 events per request.

  • X-Adall-Key: your key.
  • X-Adall-Timestamp: current Unix seconds. Requests more than five minutes off are refused; keep your server clock right.
  • X-Adall-Signature: sha256= + hex HMAC-SHA256(secret, "{timestamp}.{body}"). The signed body must be the exact bytes you send: encode the JSON first, sign that text, send that text.

Ready code: Code examples.

5. Consent: if you do not know it, do not send it

adall uploads an order to Google or Meta only when consent.ad_user_data is granted. With no consent block, there is no consent: the order counts in adall's decisions but does not reach the platform, and the panel says so.

  • At payment, store your cookie banner's (consent platform's) advertising/marketing choice on the order and send it: granted or denied for ad_user_data, ad_personalization, ad_storage, the source (e.g. veribenim), and proof_id and given_at when you have them.
  • Never send consent you do not know as granted. When unknown, leave consent out.
  • If consent is withdrawn later, report it with the endpoint on Consent.

6. Queue, retries, never block the order

  • Send from a background job (a queue), not inside the payment request. adall being unreachable must never affect the order, the payment or the delivery.
  • The answer is 202 with accepted, duplicate or rejected (+ errors) per event. duplicate is success.
  • 429, 5xx or a network error: retry a few minutes later with the same event_id (e.g. 3 tries, 1 and 10 minutes apart). Make a new timestamp and signature on each try.
  • 409 ERR_REPLAYED_REQUEST: the same signed request was already handled; treat it as delivered.
  • 401: wrong, revoked key or signature, or a skewed clock. Retrying does not fix it; alert your team, because every order after it is lost.
  • 422 or a per-event rejected: a field breaks a rule; log errors.

7. Send the past once

After setup, send the last 90 days of paid orders once in the same format (batches of 100, at most 600 requests a minute). The event_id stays the same, so running it again is safe.

8. Turn on the upload to Google

Events count in adall's decisions as soon as they arrive; sending them on to Google needs a one-time setup. In the panel press Settings › Conversion settings on Google › Set up adall's conversions on Google and pick "Sale". Google Ads gets an "Adall · Purchase" conversion that is first checked in test mode; after validated uploads adall takes it live by itself (in "Advice" mode, you approve going live).

9. Check it works

  1. Place a real order on the live site (or a small test order).
  2. In Settings › Connections › Send orders straight to adall, "N events in the last 24 hours" goes up and the "last event" time updates.
  3. The key list shows the key's "last used" time.
  4. With step 8 done, an order with an ad click and consent reaches the "Adall · Purchase" conversion in Google Ads (first as a test; Google's report can lag up to 3 hours). An order without consent never goes to Google; adall still counts it.
  5. If the count does not move, look at adall's answer in your server logs: 401 means the key, rejected means a field.