> ## 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.

# Contatos e tags

> Upsert idempotente, campos customizados, número imutável e o fluxo de segmentar por tag.

Contatos são identificados pelo `number` (E.164 sem `+`). O upsert usa esse número como chave — rodar a mesma chamada duas vezes não duplica o contato.

<Steps>
  <Step title="Crie ou atualize por número (upsert)">
    [`POST /v1/contacts`](/pt/referencia/contatos/criar-ou-atualizar-contato-por-número-upsert) requer escopo `contacts:write`. Se o `number` já existe, os campos enviados sobrescrevem os atuais e a resposta vem com `200` e `created: false`; se não existe, cria e responde `201` com `created: true`.

    ```bash theme={null}
    curl -X POST https://sua-instancia.whatix.cloud/wapi/v1/contacts \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "number": "5534999990000", "name": "Maria Souza", "email": "maria@exemplo.com" }'
    ```

    <Note>Rodar essa mesma chamada de novo não cria um segundo contato — é seguro reenviar em caso de timeout ou retry da sua integração.</Note>
  </Step>

  <Step title="Campos customizados com extraInfo">
    Use `extraInfo` para dados que não têm campo próprio (plano contratado, ID no seu CRM, etc.). É uma lista de `{ name, value }` — ambos string:

    ```bash theme={null}
    curl -X POST https://sua-instancia.whatix.cloud/wapi/v1/contacts \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "number": "5534999990000",
        "extraInfo": [
          { "name": "plano", "value": "premium" },
          { "name": "crm_id", "value": "8842" }
        ]
      }'
    ```

    `extraInfo` só volta no `GET /v1/contacts/{contactId}` de um contato específico — a listagem (`GET /v1/contacts`) não traz esse campo, pra manter a resposta enxuta.
  </Step>

  <Step title="Número é imutável">
    [`PUT /v1/contacts/{contactId}`](/pt/referencia/contatos/atualizar-contato) atualiza `name`, `email`, `document`, `disableBot` e `extraInfo` — mas não aceita `number`. Enviar `number` no corpo devolve `400 ERR_NUMBER_IMMUTABLE`. Pra corrigir um número errado, use o painel.

    ```bash theme={null}
    curl -X PUT https://sua-instancia.whatix.cloud/wapi/v1/contacts/501 \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "name": "Maria Souza Lima" }'
    ```
  </Step>
</Steps>

## Fluxo guiado: segmentar "lead contatado"

Um caso comum: marcar quem já recebeu uma campanha e depois filtrar só quem ainda não foi contatado.

<Steps>
  <Step title="Crie a tag no painel">
    Tags são criadas em **Tags** (menu lateral) — não existe endpoint de criação de tag na API pública, só leitura e sincronização no contato.
  </Step>

  <Step title="Descubra o ID da tag">
    ```bash theme={null}
    curl https://sua-instancia.whatix.cloud/wapi/v1/tags \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN"
    ```

    Anote o `id` da tag "lead-contatado" na resposta (`tags[].id`).
  </Step>

  <Step title="Aplique a tag no contato">
    [`PUT /v1/contacts/{contactId}/tags`](/pt/referencia/contatos/sincronizar-tags-do-contato) **substitui todas** as tags do contato — não é um "adicionar", é uma sincronização completa. Envie a lista final de IDs:

    ```bash theme={null}
    curl -X PUT https://sua-instancia.whatix.cloud/wapi/v1/contacts/501/tags \
      -H "Authorization: Bearer wtx_live_SEU_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{ "tagIds": [12] }'
    ```

    <Warning>Se o contato já tinha outras tags e você quer preservá-las, inclua os IDs delas também no array — o endpoint não faz merge.</Warning>
  </Step>

  <Step title="Filtre quem ainda não foi contatado">
    `GET /v1/contacts` filtra por `tagId` (contato com qualquer uma das tags listadas). Para excluir quem já tem a tag, use o filtro equivalente em [`GET /v1/cards`](/pt/guias/cards-e-pipelines), que tem `notContactTagId` (exclui pelo contato do card). Se seu caso é sobre contatos e não cards, busque todos e filtre no seu lado, ou inverta a lógica: tagueie os "não contatados" em vez dos "contatados".

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

## Erros comuns

| Código | HTTP | Causa |
| - | - | - |
| `ERR_MISSING_NUMBER` | 400 | `number` é obrigatório no upsert. |
| `ERR_NUMBER_IMMUTABLE` | 400 | `PUT /v1/contacts/{contactId}` recebeu `number` no corpo. |
| `ERR_NUMBER_NOT_ON_WHATSAPP` | 422 | `validateNumber=true` e o número não existe no WhatsApp — veja [Números de telefone](/pt/numeros-de-telefone). |
| `ERR_NO_CONTACT_FOUND` | 404 | Contato inexistente ou de outra conta. |
| `ERR_INVALID_TAG_IDS` | 400 | `tagIds` ausente, não é array, ou contém tag de outra conta. |

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