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

# Construindo uma integração

> Um exemplo completo com a API do Connecta DESK, do pedido no e-commerce ao contato cadastrado, à mensagem enviada e ao card no painel do CRM.

Esta página mostra uma integração do começo ao fim. No exemplo, um e-commerce precisa fazer três
coisas a cada pedido confirmado: cadastrar o contato no Connecta DESK (ou atualizar, se ele já
existir), avisar o cliente pelo WhatsApp e abrir um card no painel de pós-venda do CRM.

Ao longo do exemplo aparecem práticas que servem para qualquer integração: usar upsert em vez de
create, localizar registros por um identificador do seu sistema e usar idempotência nas ações que
o cliente vê.

## O escopo do token

Crie o token só com as permissões que este caso usa:

```json theme={null}
{
  "contacts": ["GET", "POST"],
  "messages": ["POST"],
  "crm": ["GET", "POST"],
  "channels": ["GET"]
}
```

O token não tem `DELETE` nem `PUT`, porque esta integração nunca apaga nem edita nada. Se um dia
ela precisar atualizar contatos, acrescente `contacts: PUT` ao token.

<Steps>
  <Step title="Busque os ids fixos uma vez">
    O canal de saída e o painel do CRM não mudam de um pedido para outro. Busque os ids uma vez e
    guarde na configuração:

    ```bash theme={null}
    curl -s --url "$CONNECTA_URL/channels" \
      --header "Authorization: Bearer $CONNECTA_TOKEN" | jq '.data[] | {id, name, status}'

    curl -s --url "$CONNECTA_URL/crm/boards" \
      --header "Authorization: Bearer $CONNECTA_TOKEN" | jq '.data[] | {id, name}'
    ```

    <Warning>
      Não copie esses ids de um ambiente para outro. O painel "Pós-venda" tem um id em homologação
      e outro em produção. Busque pelo nome em cada ambiente ou mantenha uma configuração separada
      para cada um.
    </Warning>
  </Step>

  <Step title="Cadastre ou atualize o contato">
    Criar com `POST /contacts` dá erro quando o cliente já comprou antes. Use o upsert, que cria o
    contato ou atualiza o existente pelo número:

    ```javascript theme={null}
    const { data: contato } = await api('/contacts/upsert', {
      method: 'POST',
      body: JSON.stringify({
        number: pedido.telefone,
        name: pedido.nomeCliente,
        email: pedido.email,
        code: String(pedido.clienteId)
      })
    }).then((r) => r.json());
    ```

    <Tip>
      O campo `code` guarda o identificador do cliente **no seu sistema**. Com ele preenchido desde
      o início, você encontra o contato depois com `GET /contacts?code=…`, sem depender do
      telefone, que pode mudar.
    </Tip>
  </Step>

  <Step title="Avise o cliente">
    Fora da janela de 24 horas do WhatsApp, a primeira mensagem precisa ser um template aprovado:

    ```javascript theme={null}
    const { data: job } = await api('/messages', {
      method: 'POST',
      headers: { 'Idempotency-Key': `pedido-${pedido.id}-confirmacao` },
      body: JSON.stringify({
        channelId: CANAL_VENDAS,
        type: 'template',
        number: pedido.telefone,
        contactName: pedido.nomeCliente,
        template: {
          name: 'confirmacao_pedido',
          parameters: [pedido.nomeCliente, String(pedido.id)]
        }
      })
    }).then((r) => r.json());
    ```

    A chave de idempotência usa o id do pedido, então é sempre a mesma para aquele pedido. Se a sua
    rotina rodar duas vezes, o cliente recebe uma mensagem só. Para acompanhar o `jobId`, veja
    [Enviar mensagens](/guides/enviar-mensagens).
  </Step>

  <Step title="Abra o card no painel">
    Para criar o card, informe nome, painel e etapa. O contato é vinculado em uma segunda chamada,
    no endpoint de vínculo:

    ```javascript theme={null}
    const { data: card } = await api('/crm/cards', {
      method: 'POST',
      headers: { 'Idempotency-Key': `pedido-${pedido.id}-card` },
      body: JSON.stringify({
        name: `Pedido #${pedido.id}`,
        crmBoardId: FUNIL_POS_VENDA,
        crmBoardListId: ETAPA_AGUARDANDO_ENVIO,
        description: `${pedido.itens.length} itens, total ${pedido.total}`
      })
    }).then((r) => r.json());

    await api(`/crm/cards/${card.id}/contacts`, {
      method: 'POST',
      body: JSON.stringify({ contactId: contato.id, type: 'add' })
    });
    ```

    O vínculo usa `contato.id`, que é o id devolvido no passo 2. Os ids do Connecta DESK valem só
    dentro do Connecta DESK. O id do pedido no seu sistema vai no nome do card ou em um campo
    personalizado.
  </Step>
</Steps>

## Os padrões que se repetem

<AccordionGroup>
  <Accordion title="Upsert em vez de create" icon="refresh-cw">
    É comum uma integração processar o mesmo dado mais de uma vez, por causa de uma nova tentativa,
    de um reprocessamento ou de um erro humano. Quando existir um endpoint de upsert, prefira
    usá-lo. Assim a rotina pode rodar de novo sem duplicar cadastros.
  </Accordion>

  <Accordion title="Busque pelo identificador do seu sistema" icon="key">
    Guarde o identificador do seu sistema em um campo do Connecta DESK (`code` no contato, `sku` no
    produto) e faça as buscas por ele. Assim a integração pode rodar de novo sem problema, e você
    não precisa manter uma tabela que relaciona os seus ids aos do Connecta DESK.
  </Accordion>

  <Accordion title="Idempotência em tudo que tem efeito externo" icon="repeat">
    Uma mensagem enviada e um card criado são coisas que o cliente final vê. Nas duas chamadas,
    envie uma `Idempotency-Key` montada a partir de um dado do seu negócio, como o id do pedido.
  </Accordion>

  <Accordion title="Guarde o que não muda" icon="database">
    Canais, painéis, etapas, filas, tags e campos personalizados quase nunca mudam. Buscar esses
    dados a cada execução é a forma mais comum de gastar o limite por minuto sem necessidade.
  </Accordion>
</AccordionGroup>

## Antes de colocar em produção

<Check>
  O token tem só os módulos e métodos que a rotina usa, sem `DELETE` "por precaução".
</Check>

<Check>
  A chave está em variável de ambiente ou cofre de segredos, nunca no repositório.
</Check>

<Check>
  Toda chamada que gera algo visível para o cliente envia uma `Idempotency-Key` montada a partir de
  um identificador do seu sistema.
</Check>

<Check>
  A integração separa erro definitivo (`4xx`, não repetir) de erro temporário (`429` e `5xx`,
  repetir depois de esperar). Veja [Erros](/essentials/erros).
</Check>

<Check>
  O log registra `error.code`, método e caminho de cada falha. São esses dados que o suporte usa
  para localizar a requisição.
</Check>

<Check>
  Se o token tem prazo, a data de expiração está anotada junto da integração. Um token vencido
  recebe o mesmo `401` de um token errado.
</Check>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.