# Orders

An order represents one item submitted to LegitGrails for authentication. Use
the catalog to build a valid request, create the order, then monitor its detail
endpoint until review finishes. Examples assume the base URL and API key are
set as described in [Authentication](/authentication); complete schemas are in
the [API reference](/api).

## Create an order

Before creating an order, use [Catalog](/catalog) to select compatible brand,
category, model, answer-time, and photo-index codes. Include every required
photo index without exceeding its `photo_limit`. Photo URLs must come from the
[upload endpoint](/photos#upload-photo-files).

`POST /v1/integrations/orders` requires `orders:write`. This first example is a
Live-key request; Test-key requests are described below.

```bash
curl --request POST "$LEGITGRAILS_BASE_URL/orders" \
  --header "Authorization: Bearer $LEGITGRAILS_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "external_id": "listing-10442-line-1",
    "brand_code": "gucci",
    "category_code": "bag",
    "model_code": "gucci-bag-gg-marmont",
    "answer_time": 720,
    "photos": [
      {
        "index_code": "overall-picture",
        "url": "https://media.legitgrails.com/uploads/tusd/example-overall"
      },
      {
        "index_code": "inside-label",
        "url": "https://media.legitgrails.com/uploads/tusd/example-label"
      }
    ]
  }'
```

A successful request returns `201 Created`:

```json
{
  "id": "<order-id>",
  "external_id": "listing-10442-line-1",
  "order_number": "<order-reference>",
  "status": "queued",
  "due_date": "2026-08-05T15:30:00.000Z"
}
```

Persist `id` for API calls. Use `order_number` when communicating with support.

Key request rules:

- `brand_code` and `category_code` are required; `model_code` is optional.
- `answer_time` is a positive number of minutes selected from the available
  catalog options.
- `photos` contains managed URLs from `uploaded` `POST /photos` results with
  catalog `index_code` values. Other photo hosts are rejected.
- `external_id` is an optional identifier from your system.

### Test and Live orders

A Test API key requires `mock_outcome`. It selects the immediate simulated
state and result:

| `mock_outcome` | Initial Test-order state |
| --- | --- |
| `authentic` | `completed` with outcome `authentic` |
| `fake` | `completed` with outcome `fake` |
| `unable-to-verify` | `completed` with outcome `unable-to-verify` |
| `update-photos` | `update-photos` with a simulated photo request |

For example, add `"mock_outcome": "authentic"` to the request body when using
a Test key. Omitting it returns `mock-outcome-required` with the allowed values
in `details.allowed_mock_outcomes`. Live API keys reject `mock_outcome`, so
remove it when moving the same request to Live.

## List orders

`GET /v1/integrations/orders` requires `orders:read` and returns orders available
to the authenticated integration, newest first.

```bash
curl "$LEGITGRAILS_BASE_URL/orders?page=1&limit=10" \
  --header "Authorization: Bearer $LEGITGRAILS_API_KEY"
```

```json
{
  "data": [
    {
      "id": "<order-id>",
      "external_id": "listing-10442-line-1",
      "order_number": "<order-reference>",
      "status": "processing",
      "outcome": null,
      "due_date": "2026-08-05T15:30:00.000Z",
      "updated_at": "2026-08-04T16:12:44.000Z"
    }
  ],
  "page": 1,
  "limit": 10,
  "total": 1
}
```

`page` and `limit` default to `1` and `10`; `limit` can be at most `50`.

## Get an order

`GET /v1/integrations/orders/{id}` requires `orders:read`. Use the returned `id`
or the URL-encoded `external_id` supplied at creation.

```bash
curl "$LEGITGRAILS_BASE_URL/orders/<order-id>?locale=en" \
  --header "Authorization: Bearer $LEGITGRAILS_API_KEY"
```

```json
{
  "id": "<order-id>",
  "status": "processing",
  "outcome": null,
  "outcome_locale": null,
  "delayed": false,
  "classification": {
    "brand_code": "gucci",
    "category_code": "bag",
    "model_code": "gucci-bag-gg-marmont",
    "model_name": "GG Marmont",
    "model_name_locale": "GG Marmont"
  },
  "due_date": "2026-08-05T15:30:00.000Z",
  "updated_at": "2026-08-04T16:12:44.000Z"
}
```

The detail response is the authoritative current state. It adds
`photos_to_resubmit` while photos are requested and completed-result fields
after review. `locale` accepts `en` or `ja`; canonical fields stay in English,
while `*_locale` fields use the requested locale with English fallback.
`delayed` is informational and does not change `status` or `outcome`.

## Lifecycle and outcomes

Always branch on `status`:

| Status | Action |
| --- | --- |
| `queued` | Continue monitoring. |
| `processing` | Continue monitoring. |
| `update-photos` | Resolve every current `photos_to_resubmit` entry as described in [Photos](/photos#respond-to-an-update-photos-request). |
| `completed` | Read `outcome` and the conditional result fields. |
| `error` | Stop monitoring and investigate or contact support with the order ID and `order_number`. |

An order can enter `update-photos` more than once. After each accepted update,
continue polling or processing [webhooks](/webhooks) until the order reaches the
terminal `completed` or `error` state.

The `error` state does not produce an `order-outcome` webhook. Keep polling or
run scheduled reconciliation even when webhooks are enabled.

For `completed`, `outcome` is `authentic`, `fake`, `unable-to-verify`, or
`canceled`.

```json
{
  "id": "<order-id>",
  "status": "completed",
  "outcome": "fake",
  "outcome_locale": "Fake",
  "delayed": false,
  "classification": {
    "brand_code": "gucci",
    "category_code": "bag",
    "model_code": "gucci-bag-gg-marmont",
    "model_name": "GG Marmont",
    "model_name_locale": "GG Marmont"
  },
  "due_date": "2026-08-05T15:30:00.000Z",
  "updated_at": "2026-08-05T14:48:02.000Z",
  "certificate_url": "https://certificates.example.com/example",
  "outcome_reasons": [
    "The item does not match the expected characteristics."
  ],
  "outcome_reasons_locale": [
    "The item does not match the expected characteristics."
  ],
  "outcome_indexes": ["overall-picture"]
}
```

`outcome_reasons` and `outcome_reasons_locale` provide aligned explanatory
text. `outcome_indexes` contains machine-readable photo-index codes. Base
business decisions on `outcome`.

See [Errors](/errors) for response envelopes and retry guidance.
