## Overview

The Orders registry accepts sales that happen outside the platform. You get an ingest URL of your
own and `POST` each order to it — from your own checkout, an ERP, an automation, or any gateway
Conversa Labs does not integrate natively yet.

The URL carries an opaque token that identifies the account. There is no other authentication
header: whoever holds the URL can register orders in your account, so treat it like a password.

## Prerequisites

- The **Orders** module must be enabled for the account. With it off, the URL answers `404`.
- Administrator (or CRM management permission) to open and rotate the token.

## Step by step

1. Open **Orders** and click **Ingest endpoint**.
2. Copy the **ingest URL**. The **Token** field is masked — use the eye to reveal it and the button
   next to it to copy the token alone.
3. Paste the URL into your source system and send the order as shown below.
4. Confirm the order shows up in the list. Resending the same `external_id` updates the order
   instead of creating another one.

```
curl -X POST 'https://YOUR-INSTALLATION/public/api/v1/orders/ingest/YOUR-TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "gateway": "my_checkout",
    "external_id": "ORD-10231",
    "email": "customer@example.com",
    "title": "Pro annual plan",
    "amount": 149.9,
    "currency": "USD",
    "status": "paid",
    "ordered_at": "2026-08-11T10:00:00-03:00",
    "line_items": [
      { "name": "Pro annual plan", "quantity": 1, "unit_price": 149.9 }
    ]
  }'
```

## Body fields

| Field | Required | What it is |
|---|---|---|
| `gateway` | Yes | Identifies where the order came from (e.g. `my_checkout`, `hotmart`). |
| `contact_id` / `email` / `phone_number` | Yes (one of them) | A reference to find or create the contact. Send `name` too to label a new one. |
| `external_id` | No, but recommended | The order's id in your system. It is what makes a redelivery idempotent. |
| `status` | No | One of `pending`, `partially_paid`, `paid`, `overdue`, `failed`, `canceled`, `refunded`. |
| `amount` and `currency` | No | Total in major units (`149.9` = $149.90) and the ISO-4217 currency. |
| `ordered_at` and `paid_at` | No | ISO-8601 timestamps with offset. Without them, the arrival time is used. |
| `line_items` | No | Items with `name`, `quantity`, `unit_price` and, when available, `catalog_product_id`, `catalog_variant_id`, `discount` and `metadata`. |
| `title`, `crm_item_id`, `affiliate_id`, `metadata`, `raw` | No | Extras. The affiliate is only credited when it belongs to this account and is active. |

## Responses

- `201` — `{"status": "ok"}`. Order registered or updated.
- `422` — `invalid_payload` (missing `gateway`), `contact_reference_required` (no contact
  reference), `invalid_order_status` (status outside the list) or `contact_unresolvable` (the contact
  could neither be found nor created).
- `404` — token missing, already rotated, or the Orders module is off for the account.

## Tips, limits and best practices

- **Always send `external_id`.** Without it, a redelivery from your gateway becomes a duplicate order.
- The endpoint is rate-limited per token. For large loads, send serially and back off on `429`.
- Rotating the token invalidates the old URL **immediately**. Do it only with the integration ready
  to take the new URL — and update it right away.
- Amounts go in major units, with a decimal point. Do not send cents as an integer.

## Troubleshooting

- **Everything answers `404`:** the URL was rotated, or the Orders module is off for the account.
- **`422 contact_reference_required`:** the body carried no `contact_id`, `email` or `phone_number`.
- **The same order appears twice:** it was sent without `external_id`, or with a different value on
  each attempt.
- **The affiliate got no commission:** the `affiliate_id` does not belong to this account, or it is
  inactive.

## See also

- [Sales reconciliation hub](/hc/ajuda/articles/catalog-commerce-reconciliacao-de-vendas-en)
- [Commerce lifecycle](/hc/ajuda/articles/catalog-commerce-commerce-lifecycle-en)