> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pilotstatus.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Referência da API de Flows

> Endpoints REST para criar, editar, publicar, clonar e depreciar WhatsApp Flows, ler as respostas dos formulários e configurar a chave do endpoint data_exchange.

Todo endpoint abaixo autentica com uma **chave de API com escopo de número** (`x-api-key`) cujo número seja **Meta (API Oficial)**. A WABA é DERIVADA desse número — não existe parâmetro para nomear uma, de propósito.

| Permissão      | Cobre                                                                   |
| -------------- | ----------------------------------------------------------------------- |
| `flows:read`   | Listar Flows e ler as respostas dos formulários                         |
| `flows:manage` | Toda escrita: criar, atualizar, subir JSON, publicar, depreciar, apagar |

<Note>
  `flows:read` é um slug próprio e **não** é `templates:read`. A listagem de templates devolve texto que o seu workspace escreveu; a resposta de um Flow devolve o que um **cliente** digitou num formulário — um nome, um telefone, às vezes um documento. As duas coisas são gateadas separadamente de propósito.
</Note>

## Listar Flows

```bash theme={null}
curl "https://pilotstatus.com.br/v1/flows?page=1&pageSize=30" \
  -H "x-api-key: ps_sua_chave_aqui"
```

```json theme={null}
{
  "flows": [
    {
      "id": "flw_local_1",
      "metaFlowId": "1122334455",
      "name": "Agendamento",
      "metaStatus": "PUBLISHED",
      "categories": ["APPOINTMENT_BOOKING"],
      "jsonVersion": "7.1",
      "healthStatus": "AVAILABLE",
      "clonedFromFlowId": null,
      "supersededByFlowId": null,
      "createdAt": "2026-08-30T12:00:00.000Z",
      "updatedAt": "2026-09-01T09:12:00.000Z"
    }
  ],
  "total": 1, "page": 1, "pageSize": 30
}
```

<Note>
  `id` é o **id local** e é o único id que você manda de volta para nós. `metaFlowId` é o da Meta e é somente leitura — vem `null` enquanto o Flow existe aqui e ainda não chegou à Meta.
</Note>

## Criar ou clonar

```bash theme={null}
curl -X POST "https://pilotstatus.com.br/v1/flows" \
  -H "x-api-key: ps_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Agendamento v2",
    "categories": ["APPOINTMENT_BOOKING"],
    "cloneFromFlowId": "flw_local_1"
  }'
```

Devolve `201` com o Flow novo em `DRAFT`. Omita `cloneFromFlowId` para começar do zero.

<Warning>
  `cloneFromFlowId` é o **id local** do Flow de origem — o campo `id`, não o `metaFlowId`. Clonar é a única forma de mudar um Flow que já foi publicado.
</Warning>

## Renomear e recategorizar

```bash theme={null}
curl -X PATCH "https://pilotstatus.com.br/v1/flows/flw_local_1" \
  -H "x-api-key: ps_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Agendamento (matriz)", "categories": ["APPOINTMENT_BOOKING"] }'
```

Aceita **apenas** `name` e `categories`. Nunca publica e nunca deprecia — essas têm endpoint próprio, para um engano num formulário não alcançar uma operação irreversível.

## Subir o Flow JSON

```bash theme={null}
curl -X POST "https://pilotstatus.com.br/v1/flows/flw_local_1/json" \
  -H "x-api-key: ps_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{ "flowJson": "{\"version\":\"7.1\",\"screens\":[...]}" }'
```

Um `200` significa que o documento compilou, e aí `validationErrors` vem sempre vazio:

```json theme={null}
{ "validationErrors": [] }
```

Documento que a Meta recusa volta como **`422` `FLOW_JSON_INVALID`**, com a lista dela anexada:

```json theme={null}
{
  "error": "A Meta recusou este Flow JSON. Veja validationErrors.",
  "errorEN": "Meta rejected this Flow JSON. See validationErrors.",
  "code": "FLOW_JSON_INVALID",
  "validationErrors": [ { "error": "...", "line_start": 12, "message": "..." } ]
}
```

<Warning>
  **Isto NÃO é como a API da Meta se comporta, de propósito — e a diferença é o ponto.**

  A Meta responde `200` com os problemas listados *dentro* do payload. Ou seja: lá, sucesso aparente sobre documento inválido é o caso normal, e um cliente que olha só o status publica um Flow que nunca compilou.

  Esta rota lê a LISTA em vez do status da Graph, e responde `422` sempre que ela não está vazia. Um `200` nosso significa, então, o que um `200` deve significar. Leia `validationErrors` no `422` — é a resposta da Meta, verbatim.

  `flowJson` é uma **string**, nunca um objeto aninhado: a Meta compila os bytes exatos, e re-serializar reordenaria em silêncio o que você escreveu.
</Warning>

## Publicar

```bash theme={null}
curl -X POST "https://pilotstatus.com.br/v1/flows/flw_local_1/publish" \
  -H "x-api-key: ps_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{ "confirm": true }'
```

<Warning>
  **Irreversível na Meta.** Um Flow publicado nunca mais pode ser editado nem despublicado — só clonado num novo. `"confirm": true` é obrigatório; sem ele a requisição é recusada com `400`.
</Warning>

## Depreciar

```bash theme={null}
curl -X POST "https://pilotstatus.com.br/v1/flows/flw_local_1/deprecate" \
  -H "x-api-key: ps_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{ "supersededByFlowId": "flw_local_2" }'
```

Aposenta o Flow sem apagar, e sem apagar as respostas já enviadas. `supersededByFlowId` é opcional — mas, se você mandar, tem de nomear um Flow real seu: string vazia é recusada em vez de ignorada em silêncio, porque cadeia de versões rompida é invisível depois.

## Apagar

```bash theme={null}
curl -X DELETE "https://pilotstatus.com.br/v1/flows/flw_local_1" \
  -H "x-api-key: ps_sua_chave_aqui"
```

Só um `DRAFT` pode ser apagado. Qualquer outro estado devolve `409`.

## Ler as respostas dos formulários

```bash theme={null}
curl "https://pilotstatus.com.br/v1/flows/flw_local_1/responses?page=1&pageSize=30" \
  -H "x-api-key: ps_sua_chave_aqui"
```

Devolve o que os clientes enviaram, ligado ao contato que respondeu, dentro da janela de retenção. Resposta vencida nunca é servida.

## Chave do endpoint `data_exchange`

<Note>
  **Ainda não exposta na API pública.** A gestão da chave do endpoint `data_exchange` — gerar o par, registrar a metade pública na Meta e conferir se a Meta ainda tem a nossa — está construída, mas ainda não tem rota REST. Esta seção lista os endpoints no dia em que existirem.

  O que o recurso faz, e o desenho por trás dele, está em [Flows](/pt-BR/concepts/flows). Se você precisar antes de estar exposto, fale com o suporte.
</Note>

## Erros

Toda falha desta superfície responde o mesmo envelope — existe exatamente uma forma para ler, e uma grafia:

```json theme={null}
{ "error": "<português>", "errorEN": "<inglês>", "code": "<CÓDIGO>" }
```

`FLOW_JSON_INVALID` acrescenta um array `validationErrors`; `FLOW_UNKNOWN_FIELDS` acrescenta as chaves recusadas. Nunca assuma que o conjunto de códigos é fechado — trate os que você conhece e caia no `code` para o resto.

| Código                               | Status | Significado                                                 |
| ------------------------------------ | ------ | ----------------------------------------------------------- |
| `FLOW_NOT_FOUND`                     | 404    | Nenhum Flow com esse id dentro da WABA da sua chave         |
| `FLOW_NOT_DRAFT`                     | 409    | O Flow saiu de `DRAFT` — clone para começar nova versão     |
| `FLOW_REQUIRES_META_NUMBER`          | 422    | O número da chave não é Meta, ou não tem WABA               |
| `FLOW_NUMBER_FROM_KEY`               | 422    | Não foi possível derivar o número a partir da chave         |
| `FLOW_JSON_INVALID`                  | 422    | A Meta recusou o documento — leia `validationErrors`        |
| `FLOW_JSON_REQUIRED`                 | 400    | `flowJson` ausente ou vazio                                 |
| `FLOW_NAME_REQUIRED`                 | 400    | `name` faltando na criação                                  |
| `FLOW_NAME_INVALID`                  | 400    | `name` presente mas não é string utilizável                 |
| `FLOW_CATEGORY_INVALID`              | 400    | Categoria fora do conjunto que a Meta aceita                |
| `FLOW_CLONE_SOURCE_INVALID`          | 400    | `cloneFromFlowId` não é um id local utilizável              |
| `FLOW_PATCH_EMPTY`                   | 400    | `PATCH` sem `name` e sem `categories`                       |
| `FLOW_UNKNOWN_FIELDS`                | 400    | O corpo trouxe campo que a rota não aceita                  |
| `FLOW_PUBLISH_REQUIRES_CONFIRMATION` | 400    | `publish` sem `"confirm": true`                             |
| `FLOW_DEPRECATE_INVALID_SUCCESSOR`   | 400    | `supersededByFlowId` presente mas em branco ou desconhecido |
| `FLOW_BODY_INVALID`                  | 400    | Corpo malformado, ou campo obrigatório faltando             |
| `INVALID_PAGINATION`                 | 400    | `page` × `pageSize` além do teto de offset profundo         |
| `INTERNAL_ERROR`                     | 500    | Nosso. Tente de novo e informe o código ao suporte          |

<Note>
  Os dois últimos **não têm prefixo `FLOW_`** — são compartilhados por toda a API pública e mantêm a grafia global. Um cliente que filtre por `code.startsWith("FLOW_")` perderia os dois.
</Note>

## Relacionado

* [Flows](/pt-BR/concepts/flows)
* [Templates](/pt-BR/concepts/templates)
* [Visão geral da API](/pt-BR/api/overview)
