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

# Cards e pipelines

> Pipeline, etapas e cards; caso guiado de dashboard de marketing; criar card por webhook e mover etapa.

"Card" é o nome público do funil do Whatix — venda, suporte, cobrança, qualquer processo em etapas (o nome interno é "opportunity", mas a API usa "card" por ser mais genérico). Um **pipeline** tem **etapas** (stages) ordenadas; cada **card** está numa etapa de um pipeline, tem um contato, um valor opcional e um status (`open`, `won`, `lost`).

## Mapa mental

```
pipeline "Vendas"
 ├─ etapa "Novo lead"     (order: 0)
 ├─ etapa "Qualificado"   (order: 1)
 ├─ etapa "Proposta"      (order: 2)
 └─ etapa "Fechado"       (order: 3, isFinal: true, isWon: true)

card #501 → pipeline "Vendas", etapa "Qualificado", contato Maria, value: 4200, sourceId: 3 (Instagram Ads)
```

Todos os endpoints de card/pipeline exigem `cards:read` (leitura) ou `cards:write` (criar/mover) e enxergam a conta inteira — como um admin vendo o kanban completo, não só os cards do usuário do token.

## Caso guiado: dashboard de marketing

Você quer montar um painel externo com: cards do mês por etapa, por origem (`sourceId` — de onde veio o lead, ex. Instagram Ads, indicação) e motivos de perda. Nenhum endpoint devolve isso pronto — você monta agregando as respostas.

<Steps>
  <Step title="Monte o funil com os pipelines">
    [`GET /v1/pipelines`](/pt/referencia/cards/listar-pipelines-e-suas-etapas) devolve os pipelines da sua conta com as etapas já ordenadas (`stages[].order`). Use isso para saber quais `stageId` existem e montar as colunas do funil antes de buscar os cards.

    ```bash theme={null}
    curl https://sua-instancia.whatix.cloud/wapi/v1/pipelines \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN"
    ```
  </Step>

  <Step title="Busque os cards do período, paginando">
    [`GET /v1/cards`](/pt/referencia/cards/listar-cards) filtra por `pipelineId`, `createdAtAfter`/`createdAtBefore` (offset de fuso obrigatório — veja [Paginação e filtros](/pt/paginacao-e-filtros)). Pagine até `hasMore: false`:

    ```js theme={null}
    async function fetchCardsDoMes(pipelineId) {
      const all = [];
      let pageNumber = 1;
      let hasMore = true;

      while (hasMore) {
        const url = new URL("https://sua-instancia.whatix.cloud/wapi/v1/cards");
        url.searchParams.set("pipelineId", pipelineId);
        url.searchParams.set("createdAtAfter", "2026-09-01T00:00:00-03:00");
        url.searchParams.set("createdAtBefore", "2026-09-30T23:59:59-03:00");
        url.searchParams.set("pageNumber", pageNumber);
        url.searchParams.set("pageSize", 100);

        const r = await fetch(url, {
          headers: { Authorization: "Bearer wtx_live_SEU_TOKEN" }
        });
        const { cards, hasMore: more } = await r.json();
        all.push(...cards);
        hasMore = more;
        pageNumber++;
      }

      return all;
    }
    ```

    A resposta também traz `totalValue` — soma de `value` de **todos** os cards que casam o filtro, não só da página atual. Útil pra um card de "valor total do funil" sem somar manualmente.
  </Step>

  <Step title="Agrupe por etapa e por origem">
    Cada card na resposta já vem com `stage: { id, name }` e `source: { id, name }` embutidos — não precisa de outra chamada pra resolver os nomes:

    ```js theme={null}
    function agrupar(cards) {
      const porEtapa = {};
      const porOrigem = {};
      for (const card of cards) {
        const etapa = card.stage?.name ?? "sem etapa";
        const origem = card.source?.name ?? "sem origem";
        porEtapa[etapa] = (porEtapa[etapa] ?? 0) + 1;
        porOrigem[origem] = (porOrigem[origem] ?? 0) + 1;
      }
      return { porEtapa, porOrigem };
    }
    ```
  </Step>

  <Step title="Resolva os motivos de perda">
    Pra um card com `status: "lost"`, o objeto já traz `lostReason: { id, name }`. Se quiser a lista completa de motivos configurados (pra montar um filtro no seu painel, por exemplo), use [`GET /v1/lost-reasons`](/pt/referencia/cards/listar-motivos-de-perda):

    ```bash theme={null}
    curl https://sua-instancia.whatix.cloud/wapi/v1/lost-reasons \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN"
    ```
  </Step>

  <Step title="Agende a atualização respeitando o rate limit">
    Rode a coleta a cada 15 minutos (ou o intervalo que fizer sentido pro seu painel). Com paginação de 100 cards por página, um pipeline com 500 cards no mês gasta 5 requisições — bem abaixo do limite de 120/min por token. Se você tem vários pipelines ou várias companies, distribua as chamadas em vez de disparar tudo no mesmo segundo:

    ```js theme={null}
    import { setTimeout as sleep } from "node:timers/promises";

    async function coletarTodosPipelines(pipelineIds) {
      const resultado = {};
      for (const id of pipelineIds) {
        resultado[id] = agrupar(await fetchCardsDoMes(id));
        await sleep(500); // espaça as chamadas entre pipelines
      }
      return resultado;
    }
    ```
  </Step>
</Steps>

## Criar card por webhook de formulário

Quando um lead preenche um formulário externo, crie o card direto pelo número — sem precisar buscar o `contactId` antes. [`POST /v1/cards`](/pt/referencia/cards/criar-card) aceita `phone` como alternativa a `contactId`; o contato é criado/reaproveitado automaticamente:

```bash theme={null}
curl -X POST https://sua-instancia.whatix.cloud/wapi/v1/cards \
  -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "5534999990000",
    "name": "João Pereira",
    "pipelineId": 1,
    "sourceId": 3,
    "title": "Interesse via landing page",
    "value": 1500
  }'
```

Envie exatamente um entre `contactId` e `phone` — os dois juntos (ou nenhum) resultam em `400 ERR_CONTACT_IDENTIFIER_REQUIRED`.

<Warning>Envie sempre `stageId` ao criar um card. Se omitir, o card é criado **sem etapa** (`stage: null`) e não aparece nas colunas do kanban.</Warning>

<Note>Se o pipeline tiver uma etapa que exige origem (`sourceRequired`) e você não enviar `sourceId`, a API responde `400 ERR_SOURCE_REQUIRED_FOR_STAGE` — só quando `stageId` também é enviado no `POST`. Confira as etapas em `GET /v1/pipelines` antes de montar a integração.</Note>

### Preenchendo campos personalizados

`customFields` aceita os campos personalizados do pipeline informado (ou globais da sua conta) — a chave é o `name` técnico do campo, o valor já no formato do tipo (select/multiselect precisam bater com as `options` cadastradas):

```bash theme={null}
curl -X POST https://sua-instancia.whatix.cloud/wapi/v1/cards \
  -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "5534999990000",
    "name": "João Pereira",
    "pipelineId": 1,
    "sourceId": 3,
    "title": "Interesse via landing page",
    "value": 1500,
    "customFields": {
      "referred_by": "Indicação",
      "payment_method": "Cartão"
    }
  }'
```

<Note>Campo inexistente, de outro pipeline/conta ou com valor inválido é **ignorado** — nunca falha a criação do card — e reportado em `ignoredCustomFields` (`[{ name, reason }]`) na resposta, presente só quando algum campo foi ignorado.</Note>

## Mover card de etapa

[`PATCH /v1/cards/{cardId}/stage`](/pt/referencia/cards/mover-card-de-etapa) move exatamente como um arrastar-e-soltar no kanban: dispara as mesmas automações da etapa e atualiza em tempo real pra quem estiver com o kanban aberto.

```bash theme={null}
curl -X PATCH https://sua-instancia.whatix.cloud/wapi/v1/cards/501/stage \
  -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "stageId": 8 }'
```

<Warning>`stageId` precisa ser do mesmo pipeline do card. Mover para etapa de outro pipeline devolve `422 ERR_STAGE_NOT_IN_PIPELINE` — mover entre pipelines não é permitido pela API.</Warning>

## Erros comuns

| Código | HTTP | Causa |
| - | - | - |
| `ERR_INVALID_DATE_FILTER` | 400 | Filtro de data sem offset de fuso ou intervalo invertido. |
| `ERR_NO_CARD_FOUND` | 404 | Card inexistente ou de outra conta. |
| `ERR_CONTACT_IDENTIFIER_REQUIRED` | 400 | Nenhum ou ambos `contactId`/`phone` informados ao criar card. |
| `ERR_PIPELINE_NOT_FOUND` | 404 | `pipelineId` inexistente ou de outra conta. |
| `ERR_STAGE_NOT_IN_PIPELINE` | 422 | `stageId` existe, mas é de outro pipeline. |
| `ERR_SOURCE_NOT_FOUND` | 404 | `sourceId` inexistente ou de outra conta. |

Veja o [catálogo completo de erros](/pt/erros-e-limites).
