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. As etiquetas só fazem sentido depois que o pedido tem um documento fiscal; veja 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.

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:

{
  "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:

{
  "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

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

POST/api/v1/sellers/orders/{id}/shipment-labels/generate

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

{
  "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

GET/api/v1/sellers/orders/{id}/shipments

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

{
  "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:

GET/api/seller/logistics/shipments/{id}/labels/{labelId}/download

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

{
  "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):

GET/api/seller/logistics/shipments/{id}/labels

É 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_shipmentsvazio 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 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.".