# Pedidos

> Crie pedidos de clientes, registre a execução e correções, e adicione rastreamento de entrega opcional.

Compradores colocam pedidos em seu portal de comércio. Fornecedores lidam com faturamento. Eles podem oferecer opcionalmente [pagamentos de clientes hospedados pelo Stripe](/docs/guides/customer-payments) após confirmar o total final. Solicitações de pedido e crédito comercial permanecem disponíveis. Use a API para buscar pedidos em seu sistema ERP ou contábil e manter seu status atualizado.

## Status dos pedidos

| Status         | Significado                                |
| -------------- | ------------------------------------------ |
| `new`          | Colocado                                   |
| `pending`      | Em espera                                  |
| `acknowledged` | Visualizado e sendo selecionado            |
| `processed`    | Pronto para envio                          |
| `dispatched`   | Enviado pelo fornecedor                    |
| `completed`    | Fornecedor marcou como completo            |
| `cancelled`    | Cancelado com um motivo visível ao cliente |

Funcionários podem pular etapas intermediárias de seleção. Mover para trás ou reabrir um pedido concluído ou cancelado requer um `correctionReason` interno. O histórico anterior permanece. Correções nunca alteram o estado de pagamento. Leia `revision` e envie `expectedRevision` com atualizações para rejeitar edições obsoletas.

## Criar um pedido para um cliente

**POST** `/api/v1/orders/preview`

Requer `orders.create` e acesso ao cliente. Envie `customerAccountId` e `lines` contendo `productId` e inteiro `qty` (de 1 a 1.000.000). Escolha cada produto comum uma vez, no máximo 100 linhas. Uma linha pode incluir `configuration` opcional contendo as chaves de medição e escolha retornadas; diferentes configurações de um mesmo produto permanecem separadas. O `lineKey` opcional fornece uma identidade estável para cada linha de visualização. Um `additionalDiscountBp` adicional exige permissão de precificação e `discountReason`. Leituras de pedidos configurados incluem um `configuration` de instantâneo imutável com entradas, especificação, versão, arredondamento e taxas. A visualização retorna `total`, `fingerprint`, preços atuais do cliente e moeda. Contas pausadas, produtos indisponíveis e quantidades abaixo do mínimo são recusadas. Um catálogo apenas com preços ainda aceita um pedido inserido aqui; os compradores não podem enviar um por si mesmos enquanto isso estiver desativado.

**POST** `/api/v1/orders`

Envie o mesmo cliente e linhas, `expectedTotal` da pré-visualização, `expectedBasket` definido para seu fingerprint, e um UUID novo `requestKey`. O `poNumber` opcional (60 caracteres) e o `notes` visível ao cliente (2.000 caracteres) são suportados. Reutilize exatamente a mesma solicitação e chave após uma resposta incerta: um retry bem-sucedido retorna o pedido original em vez de criar outro. Preços alterados requerem outra revisão. O resultado é `placed` com `orderId` e `reference`, ou `prices_changed`, `empty` ou `unavailable`.

Os colaboradores atuam sob sua própria identidade. O carrinho do cliente permanece inalterado. Os contatos de e-mail do cliente podem receber uma cópia; isso não gera cobrança de pagamento. O Representante de Vendas tem acesso de criação por padrão; atualizações de processamento e entrega exigem a permissão separada `orders.process`.

## Entrega e rastreamento

**PATCH** `/api/v1/orders/{id}/delivery`

Requer `orders.process`. Envie `deliveryMethod`: `unspecified`, `courier`, `own_delivery` ou `collection`. Opcional `courierName` (100 caracteres), `trackingReference` (120 caracteres) e `trackingUrl` (1.000 caracteres) são visíveis ao cliente. Os links de rastreamento devem usar HTTPS sem credenciais de login embutidas. Detalhes opcionais omitidos os limparão. Envie `expectedRevision` da ordem atual. Há um registro de entrega por pedido; integrações de transportadoras e envios divididos não estão incluídos. Detalhes de transportadora em branco nunca implicam em entrega própria.

## Listar pedidos

**GET** `/api/v1/orders`

Ordens mais recentes primeiro. A lista abrange as **200 ordens mais recentes** que correspondem aos seus filtros. Para buscar novas ordens, faça o polling com `status=new`.

| Nome       | Tipo    | Obrigatório | Descrição                         |
| ---------- | ------- | ----------- | --------------------------------- |
| `status`   | string  | não         | Um dos status acima.              |
| `customer` | string  | não         | ID do cliente ou código da conta. |
| `limit`    | integer | não         | 1 a 100. Padrão 100.              |
| `offset`   | integer | não         | Linhas a pular. Padrão 0.         |

```bash
curl "https://tradecatalog.app/api/v1/orders?status=new" \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch('https://tradecatalog.app/api/v1/orders?status=new', {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: orders } = await res.json()
```

```json
{
  "data": [
    {
      "id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
      "reference": "SO-000142",
      "status": "new",
      "total": 31460,
      "currency": "GBP",
      "poNumber": "PO-88231",
      "createdAt": "2026-09-27T07:48:12.000Z",
      "customer": "Oakfield Joinery"
    }
  ],
  "nextOffset": null
}
```

| Campo       | Tipo           | Descrição                                                                            |
| ----------- | -------------- | ------------------------------------------------------------------------------------ |
| `id`        | string         | ID do pedido.                                                                        |
| `reference` | string         | Número do pedido que os compradores veem, como `SO-000142`.                          |
| `status`    | string         | Veja [estados](#order-statuses).                                                     |
| `total`     | inteiro        | Total do pedido conforme preço (veja `taxLabel`), em unidades menores de `currency`. |
| `currency`  | string         | Fixo quando o pedido foi feito.                                                      |
| `poNumber`  | string ou nulo | Número do pedido de compra do comprador.                                             |
| `createdAt` | string         | Quando foi feito.                                                                    |
| `customer`  | string         | Nome do cliente. Obtenha o código da conta de `GET /orders/{id}`.                    |

## Obter um pedido

**GET** `/api/v1/orders/{id}`

O pedido com suas linhas e histórico de status.

| Nome | Entrada | Tipo   | Obrigatório | Descrição     |
| ---- | ------- | ------ | ----------- | ------------- |
| `id` | path    | string | sim         | ID do pedido. |

```bash
curl https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY"
```

```js
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
  headers: { Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}` },
})
const { data: order } = await res.json()
```

```json
{
  "data": {
    "id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
    "customerAccountId": "7d2e4f0a-1b3c-4d5e-8f90-a1b2c3d4e5f6",
    "placedBy": "Fq3L0cZ7tN1pW9aYx2Rk",
    "reference": "SO-000142",
    "poNumber": "PO-88231",
    "notes": "Deliver to the Hebden Road site, gate 2.",
    "status": "new",
    "cancelReason": null,
    "currency": "GBP",
    "total": 31460,
    "createdAt": "2026-09-27T07:48:12.000Z",
    "updatedAt": "2026-09-27T07:48:12.000Z",
    "customer": { "name": "Oakfield Joinery", "accountCode": "OAK01" },
    "lines": [
      {
        "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
        "orderId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
        "productId": "3f8a2c1e-9b4d-4e6f-a7c8-1d2e3f4a5b6c",
        "sku": "HX-8510",
        "name": "Hex bolt M10 x 50 zinc",
        "unitLabel": "Box of 100",
        "qty": 20,
        "unitPrice": 1573,
        "listPrice": 1850,
        "priceLabel": "Brand discount −15%",
        "lineTotal": 31460
      }
    ],
    "events": [
      {
        "status": "new",
        "note": null,
        "createdAt": "2026-09-27T07:48:12.000Z",
        "actor": "Sam Patel"
      }
    ]
  }
}
```

| Campo                                     | Descrição                                                                                                                                             |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `customerAccountId`                       | O id do cliente.                                                                                                                                      |
| `placedBy`                                | Id da pessoa que o cadastrou. `source` diferencia pedidos de clientes e de funcionários.                                                              |
| `notes`                                   | Observações do cliente sobre entrega ou pedido.                                                                                                       |
| `cancelReason`                            | Motivo da cancelamento, se houver.                                                                                                                    |
| `lines`                                   | O que foi pedido e o preço. Uma captura: alterações de preço posteriores nunca o alteram. `productId` é `null` se o produto foi excluído desde então. |
| `lines[].priceLabel`                      | A regra que definiu o preço, conforme em [Preços](/docs/api/prices#the-price-object).                                                                 |
| `lines[].priceLabel` é `Price on request` | O produto não tem preço: `unitPrice` e `lineTotal` são `0` e a linha não está em `total`. O fornecedor confirma seu preço.                            |
| `events`                                  | Histórico de status, do mais antigo ao mais recente. `actor` é o nome da pessoa, ou `null`.                                                           |

## Atualizar ou corrigir um pedido

**PATCH** `/api/v1/orders/{id}`

Altera o status de um pedido. Retorna o pedido atualizado na mesma estrutura que `GET /orders/{id}`.

- Movimentos normais seguem as etapas de execução acima. Outros movimentos exigem um motivo de correção privado; um status atual repetido não causa alteração.
- Cancelar requer um `note`, que é exibido ao comprador como motivo.
- Mudanças de status podem enviar e-mails aos contatos do cliente, incluindo a nota pública. Um e-mail gerado não comprova a entrega.
- A alteração é registrada no histórico do pedido contra a pessoa que fez a alteração.

| Nome     | Entrada | Tipo   | Obrigatório | Descrição                                      |
| -------- | ------- | ------ | ----------- | ---------------------------------------------- |
| `id`     | path    | string | sim         | ID do pedido.                                  |
| `status` | corpo   | string | sim         | O novo status.                                 |
| `note`   | corpo   | string | não         | Até 1.000 caracteres. Obrigatório ao cancelar. |

```bash
curl -X PATCH https://tradecatalog.app/api/v1/orders/5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e \
  -H "Authorization: Bearer $TRADECATALOG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "dispatched", "note": "DPD tracking 1554 2210 0931" }'
```

```js
const res = await fetch(`https://tradecatalog.app/api/v1/orders/${orderId}`, {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.TRADECATALOG_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ status: 'acknowledged' }),
})
const { data: order } = await res.json()
```

| Status | Código               | Quando                                                                                                                        |
| ------ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| 400    | `invalid_request`    | O movimento não é permitido, por exemplo, "Um pedido recebido não pode ser marcado como concluído", ou cancelar sem uma nota. |
| 403    | `insufficient_scope` | A chave é somente leitura.                                                                                                    |
| 404    | `not_found`          | Não há esse pedido neste espaço de trabalho.                                                                                  |

Tentar novamente um `PATCH` que já foi bem-sucedido retorna `400 invalid_request`, pois o pedido avançou. Verifique o status do pedido antes de tratá-lo como um erro.

## Não disponível

A API não pode colocar pedidos para um comprador, ou alterar linhas, quantidades ou preços de um pedido. Os compradores colocam pedidos em seu portal comercial; alterações em um pedido são acordadas com o cliente e faturadas no seu próprio sistema.

Listas de pedidos também incluem `paymentStatus`: nulo para termos de conta usuais, ou o status de pagamento opcional do Stripe. O status de entrega é separado e nunca comprova o pagamento. A página de detalhes do pedido é o local para revisar o total final confirmado e aprovar o pagamento. A API não pode criar ou aprovar um pagamento.

`internalNote` (2.000 caracteres) e `correctionReason` (1.000 caracteres) são campos de histórico exclusivos para o pessoal. `note` permanece público, incluindo notas antigas. Envie `expectedRevision` para rejeitar uma edição simultânea com uma solicitação inválida. Leituras da API de pessoal incluem histórico privado; ferramentas de comprador e leituras do portal excluem identidades de pessoal e campos privados. Cancelar ainda requer um motivo público e a retirada ou reembolso total de um pagamento pendente.
