aembidocs

Chatbot para WhatsApp

API do WhatsApp

Integre sistemas, dispositivos e plataformas ao atendimento por WhatsApp: envie mensagens, consulte contatos e tickets e receba eventos em tempo real.

URL base
https://wa.aembi.com/api/v1
Formato
JSON · UTF-8
Protocolo
somente HTTPS

A versão faz parte do caminho (/api/v1). Mudanças incompatíveis só acontecem numa nova versão; dentro da v1, apenas adicionamos campos e endpoints. Por isso, sua integração deve ignorar campos desconhecidos nas respostas.

Início rápido

  1. Solicite uma chave de API ao administrador da sua conta. Ele gera a chave no painel, no menu API, só com as permissões que a sua integração precisa.
  2. Guarde a chave numa variável de ambiente do seu servidor (nos exemplos, WA_API_KEY).
  3. Valide a integração chamando o endpoint abaixo. Ele deve retornar o nome da sua empresa.
curl -X GET "https://wa.aembi.com/api/v1/me" \
  -H "Authorization: Bearer $WA_API_KEY"

Próximos passos

  • Eventos em tempo real: cadastre um webhook para receber mensagens, status de entrega e encerramentos sem precisar consultar a API. Veja o guia de webhooks.
  • Segurança: se o seu servidor tiver IP fixo, informe-o ao administrador para que a chave aceite chamadas só dele.
  • Suporte: guarde o cabeçalho X-Request-Id das respostas nos seus logs. Com ele, o administrador encontra a chamada exata no painel.

Autenticação

Envie a chave em toda requisição, de preferência no cabeçalho Authorization:

Cabeçalho HTTP
Authorization: Bearer wak_0a1b2c3d4e5f_SEU_SEGREDO_DE_32_CARACTERES

Como alternativa, também é aceito o cabeçalho X-API-Key: wak_....

As chaves começam com wak_, seguido de um prefixo público de 12 caracteres e do segredo. Guardamos apenas um hash, então a chave completa aparece uma única vez, na criação. Se ela for perdida, é preciso gerar outra.

A chave é um segredo de servidor. Nunca a coloque em apps mobile, JavaScript de navegador, firmware de aparelhos ou repositórios de código. Para dispositivos e apps, faça as chamadas pelo seu backend, que guarda a chave e repassa as requisições.

Cada chave pode ter restrição de IP, data de validade e ser revogada a qualquer momento. Recomendamos uma chave por integração: se uma vazar, você revoga só aquela.

Isolamento por empresa

A plataforma é multiempresa. A chave identifica a sua empresa, então você nunca informa um ID de empresa nas requisições. Todos os dados retornados e todas as ações executadas ficam restritos à empresa dona da chave. Um recurso de outra empresa se comporta como inexistente e retorna 404 not_found.

A URL base pode ser qualquer domínio da plataforma: quem define a empresa é sempre a chave.

Escopos

Cada chave recebe só as permissões de que a integração precisa. Chamar um endpoint sem o escopo exigido retorna 403 insufficient_scope.

EscopoPermite
*Acesso total a todos os recursos da empresa. Use só em integrações internas.
messages:sendEnviar mensagens (texto, mídia e documentos).
messages:readConsultar mensagens, conversas e status de entrega, e baixar mídias.
contacts:readConsultar contatos.
contacts:writeCriar e atualizar contatos.
tickets:readConsultar tickets e atendimentos.
tickets:writeAbrir tickets no ERP a partir de conversas.
conversations:writeTransferir conversas entre departamentos e encerrá-las.
webhooks:manageCadastrar e remover webhooks.

Respostas e erros

As respostas de sucesso trazem o conteúdo em data:

Sucesso
{
  "data": {
    "...": "..."
  }
}

Os erros seguem sempre o mesmo formato, com um code estável e uma message descritiva. Para lógica de tratamento, use o campo code: a message é descritiva e pode mudar.

Toda resposta traz o cabeçalho X-Request-Id. Registre esse valor nos seus logs e informe-o ao suporte ao relatar um problema: com ele localizamos a requisição exata. O administrador da sua conta também consulta as chamadas dos últimos 30 dias no painel, no menu API.

HTTPCódigoQuando ocorre
401unauthorizedChave ausente, em formato inválido, revogada ou expirada.
403tenant_suspendedA empresa dona da chave está suspensa.
403ip_not_allowedA chave tem restrição de IP e a requisição veio de outro endereço.
403insufficient_scopeA chave não tem o escopo exigido pelo endpoint.
404not_foundRecurso inexistente ou pertencente a outra empresa.
422validation_errorCorpo ou parâmetros inválidos. A mensagem indica o campo.
409no_connectionNão há conexão de atendimento conectada para enviar.
409connection_offlineA conexão informada está desconectada.
409contact_opted_outO contato pediu para não receber mensagens. Use transactional: true só para mensagens transacionais.
409contact_existsJá existe um contato com este número. A mensagem traz o id do existente.
409conversation_closedA conversa já está encerrada ou o encerramento já está em andamento.
409ticket_in_progressJá existe uma abertura de ticket em andamento para esta conversa.
502erp_unavailableO ERP recusou ou não respondeu à abertura do ticket. A mensagem traz o motivo.
404media_not_foundA mensagem não tem mídia.
410media_expiredO arquivo da mídia não está mais disponível no servidor.
429rate_limitedLimite de requisições ou de envios por minuto excedido. Aguarde o tempo indicado em Retry-After.
500internal_errorErro inesperado. Informe o requestId ao suporte.

Limites de uso

Para proteger a plataforma e os números de WhatsApp, cada chave tem um limite de requisições por minuto, e cada empresa tem um limite de envios de mensagem por minuto.

LimitePadrãoAplica-se a
Requisições120 por minutoCada chave de API, em todos os endpoints. O administrador pode ampliar ao criar a chave.
Envios de mensagem60 por minutoA empresa toda, somando todas as chaves, em POST /api/v1/messages.

As janelas seguem o minuto do relógio. Toda resposta autenticada informa o consumo:

CabeçalhoConteúdo
X-RateLimit-LimitLimite de requisições da chave por minuto.
X-RateLimit-RemainingQuantas ainda restam na janela atual.
X-RateLimit-ResetSegundos até a janela reiniciar.

Ao exceder, a resposta é 429 rate_limited, com o cabeçalho Retry-After em segundos. Aguarde esse tempo antes de tentar de novo. Em disparos grandes, distribua os envios ao longo do tempo. Repetir a mesma requisição com a mesma Idempotency-Key é seguro e não conta como envio novo.

Conta

Verificar chave

GET/api/v1/me

Retorna a empresa (tenant) e as permissões da chave usada. Ideal para validar a integração na primeira configuração.

Escopo exigido: nenhum (qualquer chave válida)

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/me" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "tenant": {
      "id": "cm1a2b3c4d5e6f7g8h9i0j",
      "slug": "sua-empresa",
      "name": "Sua Empresa"
    },
    "key": {
      "name": "Integração ERP",
      "scopes": [
        "messages:send",
        "contacts:read"
      ],
      "rateLimitPerMinute": 120
    },
    "ip": "203.0.113.10"
  }
}

Mensagens

Enviar mensagem

POST/api/v1/messages

Coloca a mensagem na fila de envio e responde na hora com status queued; acompanhe a entrega pelo endpoint de consulta. Envie o cabeçalho Idempotency-Key (até 100 caracteres, ex.: o número da fatura) para poder repetir a requisição com segurança: a mesma chave devolve a mesma mensagem, sem reenviar. Por padrão, o envio sai pela conexão de atendimento. Mídias podem ser enviadas por URL https ou em base64, com até 25 MB.

Escopo exigido: messages:send

Corpo da requisição

CampoTipoObrigatórioDescrição
tostringSimNúmero com DDI e DDD, só dígitos. Números brasileiros sem 55 são completados. Ex.: 5518999999999
typestringNãotext (padrão), image, video, audio ou document.
textstringNãoTexto da mensagem, obrigatório para text. Até 4096 caracteres, com formatação do WhatsApp (*negrito*, _itálico_).
mediaUrlstringNãoURL https da mídia. Informe mediaUrl ou mediaBase64.
mediaBase64stringNãoConteúdo da mídia em base64, com ou sem o prefixo data:.
mimeTypestringNãoTipo do arquivo, ex.: application/pdf. Obrigatório com mediaBase64.
fileNamestringNãoNome exibido para documentos, ex.: fatura-2026-09.pdf.
captionstringNãoLegenda de imagem, vídeo ou documento (até 1024 caracteres).
connectionstringNãoNome da conexão de envio. Padrão: a conexão de atendimento.
namestringNãoNome do contato, usado quando ele ainda não existe.
transactionalbooleanNãotrue para mensagens transacionais (fatura, aviso técnico), que são entregues mesmo a contatos descadastrados de campanhas. Padrão: false.

Exemplo de requisição

curl -X POST "https://wa.aembi.com/api/v1/messages" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Idempotency-Key: fatura-2026-09-10452" \
  -H "Content-Type: application/json" \
  -d '{
  "to": "5518999999999",
  "type": "document",
  "mediaUrl": "https://exemplo.com.br/faturas/2026-09.pdf",
  "fileName": "fatura-2026-09.pdf",
  "caption": "Sua fatura de setembro",
  "transactional": true
}'

Resposta 202

JSON
{
  "data": {
    "id": "cmv1a2b3c4d5e6f7g8h9i0j1",
    "status": "queued",
    "type": "document",
    "to": "5518999999999",
    "connection": "principal",
    "conversationId": null,
    "protocol": null,
    "error": null,
    "createdAt": "2026-09-29T13:20:00.000Z",
    "sentAt": null
  }
}

Consultar mensagem

GET/api/v1/messages/{id}

Retorna o status atual de uma mensagem enviada pela API: queued (na fila), sent (enviada), delivered (entregue), read (lida) ou failed (falhou; o motivo vem em error).

Escopo exigido: messages:read

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID retornado no envio.

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/messages/SEU_ID" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "id": "cmv1a2b3c4d5e6f7g8h9i0j1",
    "status": "delivered",
    "type": "document",
    "to": "5518999999999",
    "connection": "principal",
    "conversationId": "cmv1k2l3m4n5o6p7q8r9s0t1",
    "protocol": "20260929-000123",
    "error": null,
    "createdAt": "2026-09-29T13:20:00.000Z",
    "sentAt": "2026-09-29T13:20:03.000Z"
  }
}

Baixar mídia

GET/api/v1/messages/{id}/media

Retorna o arquivo de uma mensagem com mídia (imagem, áudio, vídeo ou documento), recebida ou enviada. Aceita o id que vem no webhook message.received (o link pronto está em mediaUrl) ou o id retornado no envio pela API. Os erros continuam em JSON.

Escopo exigido: messages:read

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID da mensagem (webhook) ou ID do envio pela API.

Exemplo de requisição

# -O -J salva com o nome original do arquivo
curl -fOJ "https://wa.aembi.com/api/v1/messages/SEU_ID/media" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

O próprio arquivo, com os cabeçalhos Content-Type (tipo do arquivo), Content-Length e Content-Disposition (nome original).

Contatos

Listar contatos

GET/api/v1/contacts

Lista os contatos da empresa, dos mais recentes para os mais antigos. Quando houver mais resultados, nextCursor vem preenchido: envie esse valor em cursor para buscar a próxima página.

Escopo exigido: contacts:read

Parâmetros de consulta

CampoTipoObrigatórioDescrição
qstringNãoBusca por nome ou por parte do número.
waIdstringNãoNúmero exato, ex.: 5518999999999.
tagstringNãoSomente contatos com esta tag.
erpClienteIdstringNãoSomente contatos vinculados a este cliente do ERP.
optOutbooleanNãotrue para descadastrados de campanhas; false para os demais.
limitintegerNãoItens por página, de 1 a 100. Padrão: 50.
cursorstringNãoValor de nextCursor da página anterior.

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/contacts" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "items": [
      {
        "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
        "waId": "5518999999999",
        "name": "Maria Souza",
        "pushName": "Maria",
        "tags": [
          "cliente",
          "fibra"
        ],
        "notes": null,
        "erpClienteId": "10452",
        "erpContatoId": null,
        "birthday": "1990-05-14",
        "optOut": false,
        "optOutAt": null,
        "verified": true,
        "createdAt": "2026-08-02T14:10:00.000Z",
        "updatedAt": "2026-09-29T16:00:00.000Z"
      }
    ],
    "nextCursor": "cmu9z8y7x6w5v4u3t2s1r0q9"
  }
}

Consultar contato

GET/api/v1/contacts/{id}

Retorna o contato e as suas 5 conversas mais recentes.

Escopo exigido: contacts:read

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID do contato ou o número, ex.: 5518999999999.

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/contacts/SEU_ID" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
    "waId": "5518999999999",
    "name": "Maria Souza",
    "pushName": "Maria",
    "tags": [
      "cliente",
      "fibra"
    ],
    "notes": null,
    "erpClienteId": "10452",
    "erpContatoId": null,
    "birthday": "1990-05-14",
    "optOut": false,
    "optOutAt": null,
    "verified": true,
    "createdAt": "2026-08-02T14:10:00.000Z",
    "updatedAt": "2026-09-29T16:00:00.000Z",
    "recentConversations": [
      {
        "id": "cmv1k2l3m4n5o6p7q8r9s0t1",
        "protocol": "202609290012",
        "status": "closed",
        "contact": {
          "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
          "waId": "5518999999999",
          "name": "Maria Souza"
        },
        "connection": "principal",
        "department": "Financeiro",
        "agent": "João Lima",
        "tickets": [
          {
            "id": "8812",
            "number": "61234"
          }
        ],
        "rating": {
          "score": 5,
          "comment": "Atendimento rápido"
        },
        "openedAt": "2026-09-29T15:40:00.000Z",
        "lastMessageAt": "2026-09-29T16:09:00.000Z",
        "closedAt": "2026-09-29T16:10:00.000Z"
      }
    ]
  }
}

Cadastrar contato

POST/api/v1/contacts

Cadastra um contato. Se o número já existir, retorna 409 contact_exists com o id do contato existente; nesse caso, use o PATCH.

Escopo exigido: contacts:write

Corpo da requisição

CampoTipoObrigatórioDescrição
waIdstringSimNúmero com DDI e DDD. Números brasileiros sem 55 são completados.
namestringNãoNome do contato (até 120 caracteres).
tagsstring[]NãoLista de tags (até 30, com até 40 caracteres cada).
notesstringNãoObservações internas (até 2000 caracteres).
erpClienteIdstringNãoID do cliente no ERP.
erpContatoIdstringNãoID do contato no ERP.
birthdaystringNãoData de nascimento no formato AAAA-MM-DD.
optOutbooleanNãotrue se o contato não quer receber campanhas.

Exemplo de requisição

curl -X POST "https://wa.aembi.com/api/v1/contacts" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "waId": "5518999999999",
  "name": "Maria Souza",
  "tags": [
    "cliente",
    "fibra"
  ],
  "erpClienteId": "10452"
}'

Resposta 201

JSON
{
  "data": {
    "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
    "waId": "5518999999999",
    "name": "Maria Souza",
    "pushName": "Maria",
    "tags": [
      "cliente",
      "fibra"
    ],
    "notes": null,
    "erpClienteId": "10452",
    "erpContatoId": null,
    "birthday": "1990-05-14",
    "optOut": false,
    "optOutAt": null,
    "verified": false,
    "createdAt": "2026-08-02T14:10:00.000Z",
    "updatedAt": "2026-09-29T16:00:00.000Z"
  }
}

Atualizar contato

PATCH/api/v1/contacts/{id}

Altera somente os campos enviados; null limpa o campo. Use addTags e removeTags para mexer nas tags sem reenviar a lista. Ao trocar o erpClienteId, a verificação de identidade é desfeita (verified volta a false): o cliente precisa confirmar a identidade de novo no WhatsApp antes de receber faturas.

Escopo exigido: contacts:write

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID do contato ou o número.

Corpo da requisição

CampoTipoObrigatórioDescrição
namestringNãoNome do contato (até 120 caracteres).
tagsstring[]NãoLista de tags (até 30, com até 40 caracteres cada).
notesstringNãoObservações internas (até 2000 caracteres).
erpClienteIdstringNãoID do cliente no ERP.
erpContatoIdstringNãoID do contato no ERP.
birthdaystringNãoData de nascimento no formato AAAA-MM-DD.
optOutbooleanNãotrue se o contato não quer receber campanhas.
addTagsstring[]NãoTags a acrescentar.
removeTagsstring[]NãoTags a remover.

Exemplo de requisição

curl -X PATCH "https://wa.aembi.com/api/v1/contacts/SEU_ID" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "addTags": [
    "inadimplente"
  ],
  "notes": "Prefere contato à tarde"
}'

Resposta 200

JSON
{
  "data": {
    "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
    "waId": "5518999999999",
    "name": "Maria Souza",
    "pushName": "Maria",
    "tags": [
      "cliente",
      "fibra",
      "inadimplente"
    ],
    "notes": "Prefere contato à tarde",
    "erpClienteId": "10452",
    "erpContatoId": null,
    "birthday": "1990-05-14",
    "optOut": false,
    "optOutAt": null,
    "verified": true,
    "createdAt": "2026-08-02T14:10:00.000Z",
    "updatedAt": "2026-09-29T16:00:00.000Z"
  }
}

Conversas

Listar conversas

GET/api/v1/conversations

Lista as conversas da empresa, da última mensagem mais recente para a mais antiga, com filtros e paginação por cursor.

Escopo exigido: messages:read

Parâmetros de consulta

CampoTipoObrigatórioDescrição
contactIdstringNãoSomente conversas deste contato.
waIdstringNãoSomente conversas deste número.
statusstringNãobot, queued (na fila), open (com atendente) ou closed.
sincestringNãoSomente conversas com mensagens a partir desta data, ex.: 2026-09-01.
limitintegerNãoItens por página, de 1 a 100. Padrão: 50.
cursorstringNãoValor de nextCursor da página anterior.

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/conversations" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "items": [
      {
        "id": "cmv1k2l3m4n5o6p7q8r9s0t1",
        "protocol": "202609290012",
        "status": "closed",
        "contact": {
          "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
          "waId": "5518999999999",
          "name": "Maria Souza"
        },
        "connection": "principal",
        "department": "Financeiro",
        "agent": "João Lima",
        "tickets": [
          {
            "id": "8812",
            "number": "61234"
          }
        ],
        "rating": {
          "score": 5,
          "comment": "Atendimento rápido"
        },
        "openedAt": "2026-09-29T15:40:00.000Z",
        "lastMessageAt": "2026-09-29T16:09:00.000Z",
        "closedAt": "2026-09-29T16:10:00.000Z"
      }
    ],
    "nextCursor": null
  }
}

Consultar conversa

GET/api/v1/conversations/{id}

Retorna a conversa com departamento, atendente, tickets, avaliação e as últimas mensagens em ordem cronológica. Mensagens com mídia trazem mediaUrl para download.

Escopo exigido: messages:read

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID da conversa ou o protocolo, ex.: 202609290012.

Parâmetros de consulta

CampoTipoObrigatórioDescrição
messagesintegerNãoQuantas das últimas mensagens retornar, de 0 a 200. Padrão: 50.

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/conversations/SEU_ID" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "id": "cmv1k2l3m4n5o6p7q8r9s0t1",
    "protocol": "202609290012",
    "status": "closed",
    "contact": {
      "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
      "waId": "5518999999999",
      "name": "Maria Souza"
    },
    "connection": "principal",
    "department": "Financeiro",
    "agent": "João Lima",
    "tickets": [
      {
        "id": "8812",
        "number": "61234"
      }
    ],
    "rating": {
      "score": 5,
      "comment": "Atendimento rápido"
    },
    "openedAt": "2026-09-29T15:40:00.000Z",
    "lastMessageAt": "2026-09-29T16:09:00.000Z",
    "closedAt": "2026-09-29T16:10:00.000Z",
    "messages": [
      {
        "id": "cmw1a2b3c4d5e6f7g8h9i0j1",
        "direction": "in",
        "senderType": "contact",
        "agent": null,
        "type": "text",
        "text": "Oi, preciso da segunda via da fatura",
        "fileName": null,
        "mimeType": null,
        "mediaUrl": null,
        "status": null,
        "createdAt": "2026-09-29T15:40:00.000Z"
      }
    ],
    "hasMoreMessages": false
  }
}

Listar departamentos

GET/api/v1/departments

Lista os departamentos ativos da empresa, usados na transferência de conversas e na abertura de tickets.

Escopo exigido: nenhum (qualquer chave válida)

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/departments" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": [
    {
      "id": "cmd1a2b3c4d5e6f7g8h9i0j1",
      "name": "Financeiro",
      "slug": "financeiro"
    },
    {
      "id": "cmd2a2b3c4d5e6f7g8h9i0j2",
      "name": "Suporte",
      "slug": "suporte"
    }
  ]
}

Transferir conversa

POST/api/v1/conversations/{id}/transfer

Transfere a conversa para outro departamento e a coloca na fila, como no painel. O cliente recebe o aviso "Estou transferindo você para <departamento>..." e, se a conversa tiver ticket, a transferência é registrada nele como comentário interno.

Escopo exigido: conversations:write

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID ou protocolo da conversa.

Corpo da requisição

CampoTipoObrigatórioDescrição
departmentstringSimNome, slug ou id do departamento de destino.
agentstringNãoE-mail de um atendente, para direcionar a conversa a ele.
notestringNãoObservação registrada no ticket (até 1000 caracteres).

Exemplo de requisição

curl -X POST "https://wa.aembi.com/api/v1/conversations/SEU_ID/transfer" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "department": "Financeiro",
  "note": "Cliente pediu renegociação"
}'

Resposta 200

JSON
{
  "data": {
    "id": "cmv1k2l3m4n5o6p7q8r9s0t1",
    "protocol": "202609290012",
    "status": "queued",
    "contact": {
      "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
      "waId": "5518999999999",
      "name": "Maria Souza"
    },
    "connection": "principal",
    "department": "Financeiro",
    "agent": null,
    "tickets": [
      {
        "id": "8812",
        "number": "61234"
      }
    ],
    "rating": {
      "score": 5,
      "comment": "Atendimento rápido"
    },
    "openedAt": "2026-09-29T15:40:00.000Z",
    "lastMessageAt": "2026-09-29T16:09:00.000Z",
    "closedAt": null
  }
}

Encerrar conversa

POST/api/v1/conversations/{id}/close

Encerra a conversa pelo mesmo fluxo do painel, com o histórico enviado ao ticket. Se ela passou por atendimento humano ou tem ticket, e ainda não foi avaliada, o cliente recebe a pesquisa de satisfação e a conversa fecha após a resposta (ou em 2 horas); nos demais casos, fecha na hora com a mensagem de encerramento. Envie survey: false para fechar na hora, sem pesquisa. A resposta é 202; a conclusão chega pelo webhook conversation.closed.

Escopo exigido: conversations:write

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID ou protocolo da conversa.

Corpo da requisição

CampoTipoObrigatórioDescrição
surveybooleanNãofalse para encerrar sem a pesquisa de satisfação. Padrão: true.

Exemplo de requisição

curl -X POST "https://wa.aembi.com/api/v1/conversations/SEU_ID/close" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "survey": false
}'

Resposta 202

JSON
{
  "data": {
    "id": "cmv1k2l3m4n5o6p7q8r9s0t1",
    "protocol": "202609290012",
    "status": "closing",
    "survey": false
  }
}

Tickets

Listar tickets

GET/api/v1/tickets

Lista os tickets abertos no ERP a partir de conversas do WhatsApp, do mais recente para o mais antigo.

Escopo exigido: tickets:read

Parâmetros de consulta

CampoTipoObrigatórioDescrição
contactIdstringNãoSomente tickets deste contato.
waIdstringNãoSomente tickets deste número.
conversationIdstringNãoID ou protocolo da conversa.
numberstringNãoNúmero do chamado no ERP.
sincestringNãoSomente tickets abertos a partir desta data, ex.: 2026-09-01.
limitintegerNãoItens por página, de 1 a 100. Padrão: 50.
cursorstringNãoValor de nextCursor da página anterior.

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/tickets" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "items": [
      {
        "id": "cmt1a2b3c4d5e6f7g8h9i0j1",
        "ticketId": "8812",
        "number": "61234",
        "conversationId": "cmv1k2l3m4n5o6p7q8r9s0t1",
        "protocol": "202609290012",
        "contact": {
          "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
          "waId": "5518999999999",
          "name": "Maria Souza"
        },
        "createdAt": "2026-09-29T15:55:00.000Z"
      }
    ],
    "nextCursor": null
  }
}

Abrir ticket

POST/api/v1/tickets

Abre um ticket no ERP para uma conversa, pelo mesmo fluxo do bot: o histórico da conversa entra como comentário interno e as mídias como anexos. Cada conversa tem no máximo um ticket; se já existir, ele é retornado com created: false. Se o ERP demorar mais de 20 segundos, a resposta é 202 com status processing; consulte depois em GET /api/v1/tickets?conversationId=... ou aguarde o webhook ticket.created.

Escopo exigido: tickets:write

Corpo da requisição

CampoTipoObrigatórioDescrição
conversationIdstringSimID ou protocolo da conversa.
departmentstringNãoNome do departamento, ex.: Suporte. Padrão: o departamento da conversa.
descriptionstringNãoDescrição do problema (até 4000 caracteres).

Exemplo de requisição

curl -X POST "https://wa.aembi.com/api/v1/tickets" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "conversationId": "202609290012",
  "department": "Suporte",
  "description": "Cliente relata lentidão desde ontem à noite"
}'

Resposta 201

JSON
{
  "data": {
    "id": "cmt1a2b3c4d5e6f7g8h9i0j1",
    "ticketId": "8812",
    "number": "61234",
    "conversationId": "cmv1k2l3m4n5o6p7q8r9s0t1",
    "protocol": "202609290012",
    "contact": {
      "id": "cmu1a2b3c4d5e6f7g8h9i0j1",
      "waId": "5518999999999",
      "name": "Maria Souza"
    },
    "createdAt": "2026-09-29T15:55:00.000Z",
    "created": true
  }
}

Webhooks

Listar webhooks

GET/api/v1/webhooks

Lista os webhooks cadastrados para a sua empresa. O segredo não é retornado.

Escopo exigido: webhooks:manage

Exemplo de requisição

curl -X GET "https://wa.aembi.com/api/v1/webhooks" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": [
    {
      "id": "cmx1a2b3c4d5e6f7g8h9i0j1",
      "name": "ERP",
      "url": "https://erp.exemplo.com.br/webhooks/wa",
      "events": [
        "message.received",
        "ticket.created"
      ],
      "active": true,
      "failCount": 0,
      "disabledReason": null,
      "createdBy": "Maria Souza",
      "createdAt": "2026-09-29T16:00:00.000Z"
    }
  ]
}

Cadastrar webhook

POST/api/v1/webhooks

Cadastra uma URL https para receber eventos. O secret retornado serve para verificar a assinatura e aparece somente nesta resposta. Limite de 10 webhooks por empresa.

Escopo exigido: webhooks:manage

Corpo da requisição

CampoTipoObrigatórioDescrição
namestringSimNome de identificação, ex.: ERP.
urlstringSimURL https que vai receber os eventos.
eventsstring[]SimEventos desejados. Veja a lista no guia de webhooks.

Exemplo de requisição

curl -X POST "https://wa.aembi.com/api/v1/webhooks" \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "ERP",
  "url": "https://erp.exemplo.com.br/webhooks/wa",
  "events": [
    "message.received",
    "message.status"
  ]
}'

Resposta 201

JSON
{
  "data": {
    "id": "cmx1a2b3c4d5e6f7g8h9i0j1",
    "name": "ERP",
    "url": "https://erp.exemplo.com.br/webhooks/wa",
    "events": [
      "message.received",
      "message.status"
    ],
    "active": true,
    "failCount": 0,
    "disabledReason": null,
    "createdBy": "API: Integração ERP",
    "createdAt": "2026-09-29T16:00:00.000Z",
    "secret": "whsec_6y0Qe9cR2vN4mT8bW1kZ3pL5sA7dF0gH"
  }
}

Excluir webhook

DELETE/api/v1/webhooks/{id}

Remove o webhook e o seu histórico de entregas.

Escopo exigido: webhooks:manage

Parâmetros de caminho

CampoTipoObrigatórioDescrição
idstringSimID do webhook.

Exemplo de requisição

curl -X DELETE "https://wa.aembi.com/api/v1/webhooks/SEU_ID" \
  -H "Authorization: Bearer $WA_API_KEY"

Resposta 200

JSON
{
  "data": {
    "id": "cmx1a2b3c4d5e6f7g8h9i0j1",
    "deleted": true
  }
}

Guia de webhooks

Webhooks avisam o seu sistema em tempo real quando algo acontece, sem precisar consultar a API. Cadastre a URL no painel (menu API, seção Webhooks) ou pelo endpoint POST /api/v1/webhooks, escolhendo os eventos.

Eventos

EventoQuando ocorre
message.receivedMensagem recebida de um cliente.
message.statusMudança de status de uma mensagem enviada (pela API, pelo painel ou pelo bot): sent, delivered, read ou failed.
conversation.closedConversa encerrada pelo atendente, pelo bot ou por inatividade.
conversation.ratedCliente respondeu à pesquisa de satisfação.
ticket.createdTicket aberto no ERP a partir de uma conversa.

O botão Testar do painel envia o evento ping, útil para validar a configuração.

Formato

Cada evento é um POST com corpo JSON: id, event, createdAt e o conteúdo em data, que depende do evento. Exemplo de message.received:

message.received
{
  "id": "evt_9b1f2c7d4e6a8b0c1d2e3f4a5b6c7d8e",
  "event": "message.received",
  "createdAt": "2026-09-29T16:00:00.000Z",
  "data": {
    "id": "cmw1a2b3c4d5e6f7g8h9i0j1",
    "conversationId": "cmv1k2l3m4n5o6p7q8r9s0t1",
    "protocol": "202609290012",
    "from": "5518999999999",
    "contactName": "Maria Souza",
    "type": "text",
    "text": "Oi, preciso da segunda via da fatura",
    "fileName": null,
    "mimeType": null,
    "mediaUrl": null,
    "connection": "principal",
    "receivedAt": "2026-09-29T16:00:00.000Z"
  }
}

Cabeçalhos

CabeçalhoConteúdo
X-WA-EventNome do evento, ex.: message.received.
X-WA-Event-IdID único do evento. É o mesmo nas novas tentativas e nos reenvios.
X-WA-DeliveryID desta entrega específica, para suporte.
X-WA-TimestampMomento do envio, em segundos (Unix).
X-WA-SignatureAssinatura sha256=... do corpo.

Verificando a assinatura

Calcule o HMAC-SHA256 de timestamp + "." + corpo com o segredo do webhook (whsec_...) e compare com X-WA-Signature. Use o corpo bruto, exatamente como recebido, e rejeite timestamps com mais de 5 minutos, para que ninguém consiga reaproveitar um evento capturado.

import crypto from "node:crypto";
import express from "express";

const app = express();

// use o corpo bruto: a assinatura é calculada sobre os bytes exatos recebidos
app.post("/webhooks/wa", express.raw({ type: "application/json" }), (req, res) => {
  const ts = req.get("X-WA-Timestamp") || "";
  const recebida = req.get("X-WA-Signature") || "";
  const esperada = "sha256=" + crypto
    .createHmac("sha256", process.env.WA_WEBHOOK_SECRET)
    .update(ts + "." + req.body)
    .digest("hex");

  const valida = recebida.length === esperada.length &&
    crypto.timingSafeEqual(Buffer.from(recebida), Buffer.from(esperada));
  if (!valida || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);

  const evento = JSON.parse(req.body);
  res.sendStatus(200);  // responda rápido
  processar(evento);    // e processe em segundo plano, ignorando evento.id repetido
});

Entrega e novas tentativas

  • Responda com qualquer status 2xx em até 10 segundos. Se o processamento for demorado, faça em segundo plano.
  • Sem resposta 2xx, tentamos de novo após 1 min, 5 min, 30 min, 2 h e 6 h. Redirecionamentos não são seguidos.
  • A entrega é pelo menos uma vez: um evento pode chegar repetido ou fora de ordem. Use o id do evento para ignorar repetições.
  • Depois de 10 eventos seguidos sem sucesso, o webhook é desativado automaticamente. Ele pode ser reativado pelo painel.
  • Em message.status, mensagens enviadas pela API trazem apiMessageId, o mesmo id retornado no envio.

Histórico de versões

29/09/2026

  • Lançamento da API v1: autenticação por chave, escopos, restrição por IP, endpoint /me, documentação e especificação OpenAPI.
  • Envio de mensagens (texto, imagem, vídeo, áudio e documento) com fila, idempotência e consulta de status.
  • Gestão de chaves pelo painel, no menu API: permissões, IPs, validade e revogação.
  • Webhooks de eventos assinados com HMAC-SHA256, com novas tentativas e histórico de entregas.
  • Limites de uso: requisições por chave e envios por empresa, com cabeçalhos X-RateLimit e erro 429.
  • Download de mídia (GET /messages/{id}/media) e campo mediaUrl no webhook message.received.
  • Contatos (listar, consultar, cadastrar e atualizar) e conversas (listar e consultar com o histórico).
  • Tickets: listagem e abertura no ERP pelo mesmo fluxo do bot, com histórico e anexos.
  • Conversas: transferência entre departamentos, encerramento (com ou sem pesquisa) e lista de departamentos.
  • Registro de chamadas: todas as requisições ficam consultáveis por 30 dias no painel, com busca por requestId.
Precisa de ajuda com a integração?Fale com o suporte técnico da Aembi pelo 0800 236 0000 e informe o X-Request-Id da chamada.
Ligar 0800 236 0000