Send orders from another platform
Report completed orders to AISA with one HTTP request per order. The contract of the public orders endpoint, what it ignores and what it never tells you.
If your store is not on Shopify, or you do not want to use the Shopify order webhooks, you can send AISA each completed order yourself. One POST per order, from your server or from your storefront.
On Shopify, the AISA pixel already sends here
The Shopify pixel posts each completed order to this endpoint. On a Shopify store you do not need a sender of your own: install the pixel, the webhooks, or both.
Send an order
curl -X POST "https://iadvize.ai/api/public/sites/pk_site_YOUR_PUBLIC_KEY/orders" \
-H "Content-Type: application/json" \
-d '{
"type": "purchase",
"orderRef": "order-10432",
"amount": "129.90",
"currency": "EUR",
"occurredAt": "2026-10-23T09:15:00Z",
"attributionId": "atr_01kvnwmagvf27rc9cze5rm9e1r"
}'The answer is 204 No Content with an empty body. That says nothing about whether the order was recorded, see What you get back.
Authentication
The URL is the only credential. pk_site_… is the site's public key, the same one as in your install snippet. It is not secret and ships in page source, so anyone who reads it can post an order for your site. A forged order only changes your own organization's figures. Do not treat a recorded order as proof that a sale happened.
There is no API key, no session and no origin check.
The body
JSON, at most 4096 bytes.
| Field | Required | What it is |
|---|---|---|
type | yes | Always "purchase". Any other value writes nothing. |
orderRef | yes | Your own order id, 1 to 200 characters after trimming. AISA treats it as opaque and deduplicates on it. |
amount | yes | The order total in the currency's major units, as a number or a string ("129.90", 129.9). Not negative. A string is digits with an optional decimal part, 32 characters at most. |
currency | yes | A three-letter ISO 4217 code, case does not matter (eur is stored as EUR). A code the platform's currency data does not know writes nothing. |
occurredAt | no | When the order was placed, as a date string. If it is missing, does not parse, is in the future or is more than 24 hours old, AISA uses the time it received the request. |
sessionId | no | The AISA tag session key, 1 to 200 characters, if your sender can read it. It authorizes nothing. |
attributionId | no | The conversation key (atr_…) the shopper's cart carried. See Link an order to a conversation. |
objected | no | A boolean. true writes nothing. See Shoppers who objected. |
A body that does not match this table (a missing field, a wrong type, a string where objected expects a boolean, an unknown type) writes nothing. Extra fields are ignored.
Amounts
You send major units. AISA converts to minor units with the currency's own number of decimals: 129.90 in EUR is stored as 12990, 1500 in JPY as 1500. A converted amount that does not fit in a 32-bit signed integer is refused.
Content type and CORS
AISA reads the body as text and parses it as JSON whatever Content-Type says. Send text/plain from a browser and no CORS preflight happens. The endpoint answers every origin (Access-Control-Allow-Origin: *) and also answers an OPTIONS preflight with 204, so a sender that sets application/json works too.
What you get back
Always 204 with an empty body, for every POST. A recorded order, a malformed body, an unknown key, a refused amount and a rate-limited request look the same. This is deliberate: the response never reveals whether a key exists, whether a site has counting switched off, or whether a shopper objected.
The work happens after the response is sent. To check that orders arrive, look at your reports, not at the status code. Or read lastStorefrontOrderAt from Get a site's storefront activity (or the MCP tool get_storefront_activity): the time AISA received the latest order through this endpoint, null before the first. A request that stored nothing, a repeated orderRef included, leaves it unchanged.
What writes nothing
The request still gets its 204, and AISA stores no order, when:
- the body is empty, over 4096 bytes (a
Content-Lengthover the cap is refused without reading the body), not JSON, or does not match the table above; objectedistrue;- the key in the URL matches no site;
- the site's counting is switched off: Site settings > Site access > Count add-to-carts on your pages controls the add-to-cart pixel and this endpoint together, and it is on by default;
amountis negative, malformed or too large once converted, orcurrencyis not a known code;- the request is over a rate limit.
Idempotence
An order sent here is unique by orderRef within your organization. Sending the same orderRef again changes nothing, so a retry is safe. The first one received wins: a later request with the same reference is dropped, including when its amount or key differs. The Shopify pixel posts to this same endpoint, so a reference it already sent counts as received.
The Shopify order webhooks are a separate source. If they report an order with the same reference, AISA keeps both reports and counts the order once, with the webhook's figures. Yours are used only when no webhook reported the order. See If the Shopify pixel also reports orders.
Link an order to a conversation
Send attributionId when you have it. An order with no attributionId, or one that does not have the shape of a conversation key (atr_ followed by 26 lowercase letters or digits), is stored with no key. It is read as not assisted, never inferred. AISA does not check that the key exists when it receives the order. The order read joins only on an exact match with a conversation key of the same organization, so a key from another organization credits nothing.
How the key gets onto a cart is described in The conversation join key. It is only put on a cart on Shopify stores, so on another platform you have no key to send unless you obtained it by other means. ⚠ AISA documents no way for a non-Shopify storefront to read a conversation key.
Shoppers who objected
If the shopper objected to measurement, your sender must not send anything for that order. Sending it with objected: true is not the way to comply: the endpoint drops that request, but the order should never leave your system in the first place.
AISA checks the flag too, and drops a request that says objected: true before it even looks up the site. It is a second safety net, not a replacement for your sender's own check. There is no way to say a shopper consented: the flag can only lower what AISA writes.
Rate limits
Two limits apply, shared with the add-to-cart endpoint, so both draw from the same budget:
| Limit | Window | Requests |
|---|---|---|
| Per IP address | 10 seconds | 20 |
| Per organization | 60 seconds | 1200 |
The IP limit is checked first. A request over a limit is dropped, still with a 204. If the limiter itself fails, the request goes through. Counters are per region, so the effective ceiling can be higher.
⚠ These two values were copied from another endpoint and have not been calibrated on real traffic of this one. They can change. Send one request per order and do not batch retries in a tight loop.
Limits to know
- Source and lines. An order recorded here has the source
pixel, no order lines, no test flag and no sales channel. It cannot tell a test order from a real one, so do not send test orders. - Device. AISA derives it from the request's
User-Agent, so a server-side sender's device type is whatever that header says. - No refunds or cancellations.
purchaseis the only type. A refund or cancellation made on another platform cannot be sent. - Your store's key is public. See Authentication.