# Devoluções

Tratamento de devoluções (RMA) **encaminhadas ao seller** pela plataforma — aprovação, rejeição e geração de coleta reversa — e o acompanhamento do reembolso ao cliente.

> Pré-requisitos: leia primeiro [Pedidos: visão geral](/guia/pedidos). A devolução é um recurso vinculado a um pedido; ela só existe depois que o cliente solicita devolução de um pedido entregue.
>
> **Reembolso é processado pela plataforma, não pelo seller.** Não há endpoint de estorno nesta API — o seller acompanha o reembolso pelo status da devolução (`refunded`). Veja [Reembolsos](#reembolsos) ao final.

---

## Como devoluções chegam ao seller

O fluxo de devolução começa do lado do cliente, não do seller. A plataforma faz uma triagem inicial: algumas devoluções são resolvidas automaticamente (ex.: pedidos com seguro, casos óbvios de extravio). Outras são **encaminhadas ao seller** com status `forwarded_to_seller` — "encaminhar" transfere a **tomada de decisão**, não a visibilidade: a API lista todas as devoluções da loja desde a abertura, para a operação acompanhar mesmo quando a decisão ainda é da plataforma.

```mermaid
flowchart TD
    A[Cliente solicita devolução] --> B{Triagem da<br/>plataforma}
    B -->|resolvido automaticamente| Z[closed]
    B -->|encaminhado| C[forwarded_to_seller]
    C --> D{Decisão do<br/>seller}
    D -->|approve| E[approved]
    D -->|reject| F[rejected]
    E --> G{Coleta?}
    G -->|method=carrier| H[POST /reverse/generate<br/>+ carrier_id]
    H --> I[label_generated<br/>+ shipment reverso criado]
    G -->|method=manual| J[POST /reverse/generate<br/>method=manual]
    J --> K[label_generated<br/>seller organiza coleta]
    I --> L[return_in_progress<br/>coleta em trânsito]
    K --> L
    L -->|POST /mark-received| M[received<br/>produto voltou à loja]
    K -->|POST /mark-received| M
    M --> N{Desfecho<br/>plataforma decide}
    N -->|criar solicitação de estorno| O[refunded<br/>financeiro processando]
    O -->|estorno concluído| P[closed<br/>resolution=refunded]
    N -->|resolvido fora da plataforma| Q[closed<br/>resolution=resolved_externally]
```

**Status possíveis** (`OrderReturnStatusEnum`):

| Status | Significa |
|---|---|
| `pending` | Solicitação recém-criada pelo cliente, ainda em triagem. |
| `forwarded_to_seller` | A plataforma encaminhou; sua loja precisa aprovar ou rejeitar. |
| `approved` | Seller aprovou. A coleta reversa pode ser gerada. |
| `rejected` | Seller recusou. Cliente vê o motivo. |
| `cancelled` | Cliente desistiu antes de qualquer decisão. |
| `label_generated` | Coleta reversa gerada (em qualquer modo); aguardando coleta/etiqueta. Daqui você já pode confirmar o recebimento quando o produto chegar. |
| `return_in_progress` | Coleta em andamento (em qualquer modo). |
| `received` | Produto chegou de volta à loja — confirmado via `/mark-received`. É o **ponto de decisão** do desfecho. |
| `refunded` | Solicitação de estorno criada; o financeiro da plataforma está processando o reembolso. |
| `closed` | Caso encerrado. O campo `resolution` diz como: `refunded` (estorno concluído) ou `resolved_externally` (resolvido fora da plataforma, sem estorno). |

> O desfecho — quem decide, quanto é estornado em devoluções parciais, e o caso "resolvido por fora" — tem guia próprio: **[Devoluções — Resolução](/guia/devolucoes-resolucao)**.

---

## 1. Listagem

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/GET/api/v1/sellers/orders/returns"><span class="api-method api-method-get">GET</span><span class="api-path">/api/v1/sellers/orders/returns</span></a>

**Sem filtros, devolve todas as devoluções da loja** — inclusive as que ainda estão em triagem na plataforma (`pending`) e as já encerradas. Use os filtros para recortar o que interessa:

| Para… | Use |
|---|---|
| Fila aguardando **sua** decisão | `?status=forwarded_to_seller` |
| Todas as devoluções de um pedido | `?order_id=01JX8K3M9PEDIDO000000000AB` |
| Apenas as recusadas | `?status=rejected` |
| Buscar por número de pedido | `?q=ORD-0001` |
| Janela de datas | `?date_from=2026-04-01&date_to=2026-04-30` |

Resposta paginada (`?per_page=15` é o default):

```json
{
  "success": true,
  "message_code": "SUCCESS",
  "data": {
    "meta": {
      "pagination": {
        "page": 1,
        "per_page": 15,
        "last_page": 1,
        "has_prev_page": false,
        "has_next_page": false,
        "records": { "from": 1, "to": 1, "records": 1 }
      }
    },
    "data": [
      {
        "id": "01JX8K3M9DEVOLUCAO00000000",
        "order_id": "01JX8K3M9PEDIDO000000000AB",
        "order_number": "ORD-000123",
        "status": "forwarded_to_seller",
        "notes": "Produto com defeito",
        "seller_response_deadline_at": "2026-04-28T10:15:00Z",
        "sla_exceeded": false,
        "created_at": "2026-04-26T10:15:00Z"
      }
    ]
  }
}
```

> **`seller_response_deadline_at` e `sla_exceeded`** indicam o SLA configurado na plataforma (`returns.seller_response_sla_hours`, default 48h). Use para destacar visualmente devoluções com prazo estourado.

---

## 2. Detalhe da devolução

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

Devolve o registro completo: motivo informado pelo cliente (`reason`), itens devolvidos (`items[]`), endereço de coleta (`pickup_address`), janela e contato (preenchidos quando vai haver coleta), histórico de timestamps (`approved_at`, `rejected_at`, `cancelled_at`, etc.).

É o que a tela "Detalhe da devolução" consome para mostrar todas as informações antes do seller decidir.

---

## 3. Decidir: aprovar ou rejeitar

### Aprovar

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/POST/api/v1/sellers/orders/returns/{id}/approve"><span class="api-method api-method-post">POST</span><span class="api-path">/api/v1/sellers/orders/returns/{id}/approve</span></a>

```json
{
  "seller_notes": "Defeito confirmado nas fotos. Reembolso autorizado."
}
```

`seller_notes` é opcional (máx 1000 caracteres) e fica **só no histórico interno**; não é exibido ao cliente. Use para registrar a justificativa interna ("conferi com o setor X", "concordo com o atendimento que abriu o ticket").

Depois de aprovar, a próxima ação é **gerar a coleta reversa** (passo 4 abaixo).

### Rejeitar

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/POST/api/v1/sellers/orders/returns/{id}/reject"><span class="api-method api-method-post">POST</span><span class="api-path">/api/v1/sellers/orders/returns/{id}/reject</span></a>

```json
{
  "reason": "Produto fora do prazo de devolução (entregue há mais de 60 dias)."
}
```

`reason` é **obrigatório** (máx 1000 caracteres) e **é exibido ao cliente** como justificativa. Escreva pensando que o cliente vai ler: explicação clara, sem jargão interno.

---

## 4. Coleta reversa

Aprovada a devolução, falta trazer o produto de volta. **Dois modos:**

- **`carrier`**: usa um transportador parceiro da plataforma. Você escolhe da lista de elegíveis, a plataforma cria o shipment reverso (com `tracking_code` etc.) e o cliente recebe a etiqueta.
- **`manual`**: você organiza a coleta por fora (motoboy próprio, cliente leva na loja, retirada combinada). Nenhum shipment é criado; só o estado da devolução avança e suas anotações ficam no histórico.

### 4a. Listar carriers elegíveis (modo `carrier`)

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/GET/api/v1/sellers/orders/returns/{id}/reverse/eligible-carriers"><span class="api-method api-method-get">GET</span><span class="api-path">/api/v1/sellers/orders/returns/{id}/reverse/eligible-carriers</span></a>

Lista os transportadores que **cobrem o CEP do cliente** e o **frete estimado** de cada um. Use para montar a UI de escolha do transportador.

```json
{
  "data": {
    "has_coverage": true,
    "zip_code": "01310-100",
    "carriers": [
      { "id": 7, "uid": "01HM0Z6E...", "name": "Correios PAC",   "estimated_freight": 18.90 },
      { "id": 9, "uid": "01HM0X1A...", "name": "Loggi Reverso",  "estimated_freight": 22.50 }
    ]
  }
}
```

**`has_coverage=false`** significa que nenhum carrier parceiro atende o endereço do cliente. Nesse caso, o modo `carrier` não está disponível; só resta `manual` (ou negociar suporte da plataforma).

### 4b. Gerar coleta

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/POST/api/v1/sellers/orders/returns/{id}/reverse/generate"><span class="api-method api-method-post">POST</span><span class="api-path">/api/v1/sellers/orders/returns/{id}/reverse/generate</span></a>

**Modo carrier:**

```json
{
  "method": "carrier",
  "carrier_id": 7,
  "freight_cost": 18.90,
  "pickup_contact_phone": "+5511999999999",
  "pickup_window_from": "2026-04-28T09:00:00Z",
  "pickup_window_to":   "2026-04-28T18:00:00Z"
}
```

| Campo | Quando preencher |
|---|---|
| `method` | **Obrigatório.** `carrier` ou `manual`. |
| `carrier_id` | Obrigatório se `method=carrier`. Use o `id` de `eligible-carriers`. |
| `freight_cost` | Valor que vai ser cobrado/reservado para o frete. Default 0. Normalmente o `estimated_freight` retornado por `eligible-carriers`. |
| `notes` | Observações livres (máx 1000 caracteres). Útil em `manual`. |
| `pickup_window_from` / `pickup_window_to` | Janela acordada com o cliente. Opcionais, mas se enviar ambos, o "to" precisa ser ≥ "from". |
| `pickup_contact_phone` | Telefone no endereço de coleta. Opcional (máx 32 caracteres). |

Resposta em modo `carrier`:

```json
{
  "data": {
    "order_return": { "id": "01JX8K3M9DEVOLUCAO00000000", "status": "label_generated" },
    "shipment": {
      "id": 9921,
      "uid": "01HM1Z3...",
      "tracking_code": null,
      "status": "pending"
    }
  }
}
```

**Modo manual:**

```json
{
  "method": "manual",
  "notes": "Cliente vai trazer pessoalmente sexta-feira à tarde."
}
```

Em `manual` não há shipment; a resposta traz só o `order_return` atualizado.

---

## 5. Confirmar recebimento

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/POST/api/v1/sellers/orders/returns/{id}/mark-received"><span class="api-method api-method-post">POST</span><span class="api-path">/api/v1/sellers/orders/returns/{id}/mark-received</span></a>

O destino da coleta reversa é o endereço da loja — quem recebe o pacote fisicamente é você. Quando o produto chegar, confirme o recebimento: a devolução vira `received` e a plataforma assume a decisão do desfecho (estorno ou encerramento sem estorno).

Disponível a partir de `label_generated` ou `return_in_progress`, sem corpo na requisição. Na coleta `manual` não existe rastreio nenhum, então **esta confirmação é a única forma de a devolução avançar** — sem ela o caso fica parado e o cliente sem resposta.

```http
POST /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/mark-received
```

---

## 6. Ações possíveis (workflow dirigido pela API)

<a class="api-route" href="/reference/devolucoes#tag/devolucoes/GET/api/v1/sellers/orders/returns/{id}/possible-actions"><span class="api-method api-method-get">GET</span><span class="api-path">/api/v1/sellers/orders/returns/{id}/possible-actions</span></a>

Em vez de replicar a máquina de estados no seu sistema, pergunte à API o que pode ser feito agora. A resposta lista as ações válidas para o status atual (`approve`/`reject` em `forwarded_to_seller`, `generate_reverse_label` em `approved`, `mark_received` em `label_generated`/`return_in_progress`), cada uma com `endpoint`, `method` e os campos esperados em `requires_input`. `is_terminal=true` indica que a devolução encerrou e não há mais nada a fazer.

A decisão de estorno (após `received`) é exclusiva da plataforma e nunca aparece aqui — acompanhe pelos campos `status` e `resolution`.

---

## Cenários comuns

### "Recebi devolução por defeito de fabricação, vou aprovar e gerar coleta"

```http
POST /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/approve
{ "seller_notes": "Defeito visível nas fotos. Reembolso autorizado." }
```

```http
GET /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/reverse/eligible-carriers
```

Escolha um carrier baseado em `estimated_freight` e prazo, depois:

```http
POST /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/reverse/generate
{ "method": "carrier", "carrier_id": 7, "freight_cost": 18.90 }
```

### "Vou recusar, cliente está fora do prazo legal de 7 dias"

```http
POST /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/reject
{
  "reason": "O prazo legal de 7 dias para devolução sem motivo (Art. 49 CDC) já expirou. Recebimento confirmado em 12/03; solicitação aberta em 28/03."
}
```

### "Cliente mora numa cidade que nenhum carrier atende"

`GET /reverse/eligible-carriers` retorna `has_coverage=false`. Resolva manualmente:

```http
POST /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/reverse/generate
{
  "method": "manual",
  "notes": "Sem cobertura de carrier. Combinado com cliente: motoboy contratado pela loja vai retirar dia 29/04."
}
```

### "A coleta era manual e o produto chegou na loja"

```http
POST /api/v1/sellers/orders/returns/01JX8K3M9DEVOLUCAO00000000/mark-received
```

A devolução vira `received` e a plataforma decide o desfecho. Sem essa confirmação, a devolução em coleta manual não avança.

### "Cliente acha que vai dar tudo certo mas quero ver o histórico"

```http
GET /api/v1/sellers/orders/returns?order_id=01JX8K3M9PEDIDO000000000AB
```

Devolve todas as devoluções do pedido `01JX8K3M9PEDIDO000000000AB`, em qualquer status.

### "Quero ver minhas devoluções recusadas no último mês"

```http
GET /api/v1/sellers/orders/returns?status=rejected&date_from=2026-04-01&date_to=2026-04-30
```

---

## Reembolsos

Você não vai encontrar um endpoint de estorno nesta API — e isso é de propósito. O reembolso mexe com dinheiro do cliente, conciliação de pagamento e regras de repasse, então a plataforma centraliza esse passo para garantir que o estorno só aconteça quando o produto realmente voltou e o caso fechou. Do seu lado, em vez de "emitir" um reembolso, você acompanha o desfecho pelos campos `status` e `resolution` da devolução.

Pela ótica do seller, o ciclo final é:

| Status | O que significa para o reembolso |
|---|---|
| `received` | Produto chegou de volta à loja. A plataforma decide o desfecho. |
| `refunded` | Solicitação de estorno criada — o financeiro da plataforma está processando. O valor corresponde aos **itens devolvidos** (devolução parcial estorna só a parte devolvida). |
| `closed` + `resolution=refunded` | Estorno concluído. O valor (e eventuais descontos de frete reverso) entra no seu extrato/repasse. |
| `closed` + `resolution=resolved_externally` | Encerrada **sem estorno** — o caso foi resolvido fora da plataforma (ex.: você reenviou o produto direto ao cliente). A justificativa fica em `resolution_notes`. |

Pontos a ter em mente:

- **Você não inicia nem cancela um reembolso pela API.** Casos excepcionais (divergência de valor, acordo direto com o cliente) são tratados pela operação/suporte da plataforma — e refletem na devolução via `resolution`.
- **O frete reverso pode impactar o repasse.** O `freight_cost` informado na [geração da coleta](#4b-gerar-coleta) é o valor que entra no acerto financeiro daquela devolução.
- **Aprovar a devolução não é o mesmo que reembolsar.** Aprovar (`/approve`) autoriza a volta do produto; o desfecho financeiro só é decidido no fim do trilho, após o recebimento.
- **Devoluções parciais sucessivas são possíveis.** Encerrada uma devolução, o cliente pode abrir outra para o restante dos itens — cada uma com seu próprio desfecho. Detalhes e exemplos em **[Devoluções — Resolução](/guia/devolucoes-resolucao)**.

---

## Cuidados

**`reject.reason` é visto pelo cliente.** Escreva como se fosse uma resposta no SAC: profissional, clara, sem jargão interno. Para anotações internas use `approve.seller_notes` (que **não** é visto pelo cliente).

**Coleta reversa só depois de `approved`.** Tentar gerar coleta antes de aprovar retorna 422 com `A devolução precisa estar aprovada para gerar a coleta reversa.`. O fluxo é: `forwarded_to_seller` → `approve` (vira `approved`) → `reverse/generate`.

**`freight_cost` é o valor real (não estimativa).** Use o `estimated_freight` como base, mas se você negociou um valor diferente com o carrier, mande o valor negociado. Esse é o número que entra no extrato/repasse.

**`pickup_address` é montado a partir do snapshot do pedido.** Você não precisa enviar; ele vem do endereço que estava no pedido original. Se for diferente (cliente mudou), edite no shipment via operação depois de gerar.

**Aprovar é um caminho só de ida — não há "des-aprovar".** A razão é que, assim que você aprova, o cliente já é avisado e a coleta reversa passa a poder ser gerada; voltar atrás depois disso confundiria o cliente e a logística. Por isso, uma vez `approved`, a devolução segue o trilho da coleta reversa. Se aprovou por engano, não é algo que você reverte pela API — é caso de operação (contate o suporte).

**Datas inválidas em `date_from`/`date_to` são ignoradas silenciosamente.** Não há erro; o filtro simplesmente não é aplicado. Se a busca não está filtrando, cheque o formato (ISO 8601).
