# Logística

Shipments do pedido, geração e download de etiquetas de envio. **Operação assíncrona** com polling.

> Pré-requisitos: leia primeiro [Pedidos: visão geral](/guia/pedidos). As etiquetas só fazem sentido depois que o pedido tem um documento fiscal; veja [Pedidos: fiscal](/guia/pedidos-fiscal).

---

## O modelo mental

Um **pedido** pode ter **um ou mais shipments**. Cada shipment representa um pacote físico que sai da loja e tem um destino, transportador e (quando aplicável) código de rastreio. Para a maioria dos pedidos é 1 shipment, mas pode ter mais (ex.: produto de um SKU vem de uma origem e outro de outra; ou um pedido com retirada parcial e envio parcial).

Cada **shipment** tem zero, uma ou mais **etiquetas (`labels`)**. A etiqueta é o PDF que vai colado na caixa. Pode ter mais de uma quando o transportador exige formatos diferentes (ex: A4 + impressão térmica) ou para shipments com múltiplos volumes.

```mermaid
flowchart LR
    O[Order] --- S1[Shipment 1]
    O --- S2[Shipment 2]
    S1 --- L1[Label A4]
    S1 --- L2[Label térmica]
    S2 --- L3[Label A4]
```

Você como seller **dispara a geração** (fila um job em background), faz **polling** até as etiquetas ficarem prontas, e baixa cada uma via URL S3 assinada.

`GET /orders/{id}/shipments` devolve `{ "shipments": [ … ] }`. Cada **shipment**:

```jsonc
{
  "id": 4421,
  "uid": "shp_a1b2c3",
  "type": "sales_order",         // tipo: sales_order | return | transfer | pickup
  "status": "ready_to_ship",     // ← status do shipment (valores abaixo)
  "tracking_code": "BR123…BR",   // rastreio fica aqui, não na etiqueta
  "carrier": {…},                // transportadora (id, name)
  "labels": [{…}],               // etiquetas deste shipment
  "labels_ready": 1,             // quantas já prontas
  "labels_total": 1              // total esperado
}
```

Os valores de `status` do shipment: `pending`, `processing`, `ready_to_ship`, `in_transit`, `delivered`, `cancelled`, `return_in_progress`, `returned`, `awaiting_return`, `failed`, `error`.

E cada **label** dentro de `labels`:

```jsonc
{
  "id": "lbl_xyz",
  "format": "a4",                // formato: a4 | zebra | thermal (+ format_label em pt-BR)
  "status": "completed",         // ← pending | processing | completed | error
  "is_ready": true,              // já pode baixar? (pronto = completed)
  "completed_at": "2026-04-26T16:02:00Z"
}
```

---

## Fluxo completo

```mermaid
sequenceDiagram
    autonumber
    participant I as Integrador
    participant A as API
    participant W as Worker
    I->>A: POST /orders/{id}/shipment-labels/generate
    A-->>I: 202 { queued_shipments: [4421], total: 1 }
    A->>W: dispara job (1 por shipment)
    loop polling (a cada N segundos)
      I->>A: GET /orders/{id}/shipments
      A-->>I: shipments com labels[] (is_ready: false)
    end
    W-->>A: labels prontas
    I->>A: GET /orders/{id}/shipments
    A-->>I: labels com is_ready: true
    I->>A: GET /logistics/shipments/{id}/labels/{labelId}/download
    A-->>I: { url, expires_in: "15 minutos" }
```

### 1. Disparar geração

<a class="api-route" href="/reference/pedidos#tag/etiquetas/POST/api/v1/sellers/orders/{id}/shipment-labels/generate"><span class="api-method api-method-post">POST</span><span class="api-path">/api/v1/sellers/orders/{id}/shipment-labels/generate</span></a>

Sem body. Enfileira um job para **cada shipment do pedido**. Resposta **202**:

```json
{
  "success": true,
  "code": 202,
  "data": {
    "queued_shipments": [4421, 4422],
    "total": 2
  }
}
```

`queued_shipments` lista os IDs efetivamente enfileirados. Pode ser menor que o número total de shipments do pedido quando algum tem `label_generation_blocked=true` (ver "Cuidados" abaixo).

> **Chamar de novo regenera.** Se a primeira geração falhou ou se você corrigiu algo no shipment e quer outra rodada, basta chamar `/generate` de novo: as etiquetas anteriores são descartadas e novos jobs são disparados.

### 2. Polling

<a class="api-route" href="/reference/pedidos#tag/etiquetas/GET/api/v1/sellers/orders/{id}/shipments"><span class="api-method api-method-get">GET</span><span class="api-path">/api/v1/sellers/orders/{id}/shipments</span></a>

Consulta a lista de shipments com etiquetas embutidas. O que você procura em cada shipment é `labels_ready === labels_total`:

```json
{
  "data": {
    "shipments": [
      {
        "id": 4421,
        "uid": "shp_a1b2c3",
        "type": "sales_order",
        "status": "ready_to_ship",
        "tracking_code": "BR123456789BR",
        "carrier": { "id": 12, "name": "Correios" },
        "labels": [
          {
            "id": "lbl_xyz",
            "format": "a4",
            "format_label": "A4 (PDF)",
            "status": "completed",
            "is_ready": true,
            "completed_at": "2026-04-26T16:02:00Z"
          }
        ],
        "labels_ready": 1,
        "labels_total": 1
      }
    ]
  }
}
```

> **Cadência recomendada:** primeiros 15 segundos a cada 2s, depois a cada 5s, com timeout em 2 minutos. A geração típica leva 5–20 segundos por shipment dependendo do transportador.

### 3. Baixar a etiqueta

Quando `is_ready=true`, baixe via URL S3:

<a class="api-route" href="/reference/pedidos#tag/etiquetas/GET/api/seller/logistics/shipments/{id}/labels/{labelId}/download"><span class="api-method api-method-get">GET</span><span class="api-path">/api/seller/logistics/shipments/{id}/labels/{labelId}/download</span></a>

`{id}` é o `shipment.id` (numérico). `{labelId}` é o `label.id`.

```json
{
  "success": true,
  "code": 200,
  "data": {
    "label_id": "lbl_xyz",
    "format": "a4",
    "url": "https://s3.amazonaws.com/.../label_4421.pdf?X-Amz-Signature=...",
    "expires_in": "15 minutos"
  }
}
```

A `url` é S3 assinada, válida por **15 minutos**. Não armazene a URL; chame a rota de novo se precisar de outra cópia.

Se a etiqueta ainda não estiver pronta no momento do download, retorna **422 `UNPROCESSABLE_ENTITY`** com `Status: Em processamento`. Volte para o polling antes de tentar de novo.

---

## Rota alternativa (só labels)

Quando você **já tem o `shipment.id` em mãos** e só quer a lista de etiquetas (sem trazer o resto do shipment):

<a class="api-route" href="/reference/pedidos#tag/etiquetas/GET/api/seller/logistics/shipments/{id}/labels"><span class="api-method api-method-get">GET</span><span class="api-path">/api/seller/logistics/shipments/{id}/labels</span></a>

É a mesma informação que vem dentro do shipment em `/orders/{id}/shipments`, mas servida isoladamente. Útil quando você está numa tela "detalhe do shipment" e não quer recarregar todos os shipments do pedido.

A resposta traz também o `shipment_id` e a lista `formats_pending[]` (formatos ainda em processamento), e cada label vem com `source`/`source_label` (de onde a etiqueta veio) e `status_label` em pt-BR — bom para exibir direto na tela.

---

## Quando gerar (e quando não)

**Antes de gerar, o documento fiscal precisa existir** (NF-e registrada ou Declaração emitida). O motivo é prático: nenhum transportador aceita despachar uma encomenda sem nota acompanhando. A API não vai te impedir de gerar a etiqueta antes do fiscal — não é um erro técnico — mas o shipment vai ficar travado na hora do despacho. Por isso a ordem importa: resolva o fiscal primeiro e a etiqueta depois.

**Ordem recomendada no fluxo padrão:**

```
paid → preparing → [emite fiscal] → /shipment-labels/generate → ready_to_ship → shipped
```

Gere as etiquetas em **`preparing`** (depois do fiscal), imprima, cole na caixa, marque `ready_to_ship` quando o pacote estiver na transportadora.

---

## Cuidados

**Shipment com `label_generation_blocked=true` é pulado.** Esse flag fica em `true` quando o sistema sabe que a etiqueta tem um problema impossível de resolver automaticamente (ex.: dados de endereço inválidos, conta com o transportador suspensa, peso fora do contrato). Você vai ver o shipment em `queued_shipments` **vazio** ou com `total` menor que o número real de shipments. Resolva o problema no shipment via operação antes de tentar gerar de novo.

**Chamar `/generate` várias vezes regenera tudo.** Não é incremental: uma chamada apaga as etiquetas anteriores e dispara jobs novos para **todos** os shipments não bloqueados. Use com consciência: se 1 de 3 shipments deu errado, chamar `/generate` vai regerar os outros 2 também.

**A URL S3 expira em 15 minutos.** Se você está montando uma tela de impressão em massa, gere as URLs sob demanda (no momento em que o usuário clica em "Imprimir"), não antes.

**Tracking code aparece no shipment, não na label.** Para ver o `tracking_code`, leia `shipments[].tracking_code` em `/orders/{id}/shipments` ou use [`GET /orders/{id}/tracking`](/guia/pedidos#tracking-de-entrega) para a versão consolidada por pedido.

**Pedido com workflow `in_store_pickup` pode não ter shipment de saída.** Faz sentido: se o cliente vai retirar na loja, não há nada para despachar, então também não há etiqueta a imprimir. Se você chamar `/generate` num pedido sem nenhum shipment, a resposta é **404** com a mensagem "Nenhum shipment encontrado para este pedido.".
