Pular para o conteúdo

Listar mensagens

Liste as mensagens (enviadas e recebidas) de um número, paginadas por cursor, mais recentes primeiro. Sem filtros, a lista é a caixa de entrada inteira do número; com group_id, contact_id e direction você lê só um grupo, só uma pessoa ou só o que foi recebido. Cada item é um resumo: para o corpo completo de uma mensagem específica, use Consultar mensagem.

Use GET /v1/messages para o número principal. Para números extras, use GET /v1/numbers/{ref}/messages, onde {ref} é o alias atual do número ou o id dele (num_…, o number_id do envelope do webhook/polling e de cada item). Prefira o id: o alias lido numa mensagem antiga pode ter sido renomeado depois.

Escopo obrigatório: messages:read

Parâmetros de consulta

ParâmetroTipoDescrição
cursorstringToken opaco retornado em cursor pela página anterior. Ausente = primeira página.
limitintQuantidade de itens por página. Intervalo: 1 a 200. Padrão: 50.
group_idstringSó as mensagens deste grupo (grp_ + 32 hex). O grupo precisa ser do número da URL.
contact_idstringSó as mensagens que este contato enviou ou recebeu (ct_ + 32 hex), inclusive o que ele disse em grupos. O contato precisa ser do número da URL.
directionstringinbound (só as recebidas) ou outbound (só as enviadas).

Os filtros se combinam: a lista traz só as mensagens que atendem a todos os que você informou. Sem nenhum filtro, vem tudo. Os ids de grupo e de contato vêm dos grupos e contatos do número e dos eventos (from.contact_id, to.group_id).

Filtrar a lista

Ler um grupo:

ler-um-grupo.sh
curl -G "https://api.turbonotify.com/v1/numbers/ALIAS_DO_NUMERO/messages" \
-H "Authorization: Bearer SUA_CHAVE" \
--data-urlencode "group_id=grp_0198d3c5a1b24d3e9f01a2b3c4d5e6f7" \
--data-urlencode "limit=50"

Ler o que uma pessoa disse nesse grupo (group_id e contact_id juntos):

o-que-a-pessoa-disse-no-grupo.sh
curl -G "https://api.turbonotify.com/v1/numbers/ALIAS_DO_NUMERO/messages" \
-H "Authorization: Bearer SUA_CHAVE" \
--data-urlencode "group_id=grp_0198d3c5a1b24d3e9f01a2b3c4d5e6f7" \
--data-urlencode "contact_id=ct_0198d3c5a1b24d3e9f01a2b3c4d5e6f8"

Ler só o que o número recebeu:

so-recebidas.sh
curl -G "https://api.turbonotify.com/v1/numbers/ALIAS_DO_NUMERO/messages" \
-H "Authorization: Bearer SUA_CHAVE" \
--data-urlencode "direction=inbound"

Para continuar na página seguinte, repita a chamada com os mesmos filtros e o cursor devolvido pela página anterior. Não troque um filtro no meio da leitura: o cursor pertence à lista que o gerou. Para mudar de filtro, comece de novo sem cursor.

Listar mensagens

listar-mensagens.sh
curl -X GET \
"https://api.turbonotify.com/v1/numbers/ALIAS_DO_NUMERO/messages?limit=20" \
-H "Authorization: Bearer SUA_CHAVE"

Resposta (200)

Status HTTP: 200
{
  "data": [
    {
      "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70",
      "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
      "number_alias": "main",
      "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e",
      "from": {
        "contact_id": "ct_0198d3c5a1b24d3e9f01a2b3c4d5e6f8",
        "name": "Lucas",
        "number": "5511988887777"
      },
      "to": {
        "group_id": "grp_0198d3c5a1b24d3e9f01a2b3c4d5e6f7",
        "name": "Equipe de Vendas"
      },
      "direction": "inbound",
      "content_type": "text",
      "channel": "whatsapp",
      "origin": "native",
      "preview": "Pedido 4521 separado",
      "content_expired": false,
      "at": "2026-06-01T13:04:07Z",
      "status": {
        "state": "delivered",
        "at": "2026-06-01T13:04:09Z",
        "error": null
      }
    },
    {
      "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f71",
      "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
      "number_alias": "main",
      "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e",
      "from": {
        "number": "5511973117056",
        "alias": "main"
      },
      "to": {
        "contact_id": "ct_0198d3c5a1b24d3e9f01a2b3c4d5e6f9",
        "name": "Ana",
        "number": "5511977776666"
      },
      "direction": "outbound",
      "content_type": "text",
      "channel": "whatsapp",
      "origin": "tn",
      "preview": "Olá, seu pedido foi confirmado!",
      "content_expired": false,
      "at": "2026-06-01T13:00:05Z",
      "status": {
        "state": "delivered",
        "at": "2026-06-01T13:01:00Z",
        "error": null
      }
    },
    {
      "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f72",
      "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
      "number_alias": "main",
      "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e",
      "from": {
        "contact_id": "ct_0198d3c5a1b24d3e9f01a2b3c4d5e6f9",
        "name": "Ana",
        "number": "5511977776666"
      },
      "to": {
        "number": "5511973117056",
        "alias": "main"
      },
      "direction": "inbound",
      "content_type": "image",
      "channel": "whatsapp",
      "origin": "native",
      "preview": "Foto do produto",
      "content_expired": false,
      "at": "2026-06-01T12:58:00Z",
      "status": {
        "state": "delivered",
        "at": "2026-06-01T12:58:00Z",
        "error": null
      }
    }
  ],
  "cursor": "eyJpZCI6Im1zZ18wMSJ9",
  "has_more": true
}

Cada item traz o mesmo envelope do evento message.* daquela mensagem (veja Webhooks), mais content_expired e status. As menções, a transcrição e o conteúdo completo (text, mídia) ficam só na resposta de Consultar mensagem.

Campos da resposta

CampoTipoDescrição
dataarrayPágina de mensagens, mais recentes primeiro
cursorstring|nullToken opaco para a próxima página; null quando não há mais itens
has_morebooleantrue se há mais páginas disponíveis
data[].idstringIdentificador público da mensagem (msg_ + 32 hex)
data[].organization_idstringIdentificador público da sua organização (org_ + 32 hex)
data[].number_aliasstringO alias que o número tinha na hora da mensagem ("main" ou o alias de um número extra), o mesmo valor do evento. Se o número for renomeado depois, as mensagens antigas mantêm o alias antigo.
data[].number_idstringIdentificador do número (num_…), logo depois do number_alias. Nunca muda, mesmo que o alias seja renomeado.
data[].fromobjectRemetente. Mensagem recebida: {contact_id?, name?, number}. Mensagem enviada: {contact_id?, name?, number, alias}
data[].toobjectDestinatário. Em um grupo: {group_id, name?}. Mensagem recebida em conversa individual: {contact_id?, name?, number, alias}. Mensagem enviada em conversa individual: {contact_id?, name?, number}
data[].directionstringoutbound (enviada) ou inbound (recebida)
data[].content_typestringtext, image, audio, video, document, sticker, location, calendar, etc.
data[].channelstringSempre whatsapp
data[].originstringtn (enviada pela API) ou native (enviada ou recebida no próprio WhatsApp)
data[].previewstringTrecho inicial do conteúdo, sem partir emoji ou acento ao meio (mesmo valor de GET /v1/numbers/{ref}/messages/{id})
data[].content_expiredbooleantrue quando o conteúdo já passou do prazo de retenção do seu plano e foi apagado. A mensagem continua na lista por 12 meses, com preview vazio
data[].atstringISO 8601: momento estável de ocorrência da mensagem (outbound: envio real; inbound: recebimento). Não avança conforme a mensagem progride sent → delivered → read.
data[].statusobject{state, at, error}: estado atual de entrega e o timestamp dessa transição. O error vem preenchido ({code, message, hint}) quando state é "failed" e é null nos demais estados, igual nas duas rotas.

Ordenação e paginação

  • Os itens vêm na ordem em que o Turbo Notify aceitou cada mensagem (mais recente primeiro), não estritamente por at. Para a maioria das mensagens os dois coincidem; em envios outbound, at (o momento real de envio) pode ficar alguns segundos depois do aceite, porque o envio simula o tempo de digitação antes de chegar ao WhatsApp. Por isso at pode sair levemente fora de ordem dentro da página. A página é sempre completa e sem lacunas.
  • A paginação usa cursor: passe o valor retornado como cursor na próxima chamada, com os mesmos filtros, até has_more ser false. Trate o cursor como um token opaco e não monte nem altere o valor.
  • Só o conteúdo expira: uma mensagem que já passou do prazo de retenção de conteúdo do seu plano continua na lista por 12 meses, com content_expired: true e preview vazio. O remetente e o grupo continuam na mensagem, e ela continua aparecendo nos filtros group_id e contact_id.

Erros possíveis

StatusCódigo de erroDescrição
401not_authenticatedA chave de API está inválida ou ausente.
403insufficient_scopeA chave de API não possui o escopo messages:read (resposta inclui required_scope).
404number_not_foundO alias ou o id informado na URL não corresponde a nenhum número cadastrado.
400invalid_number_idO valor na URL começa com num_, mas não é um id de número válido.
400invalid_group_idO group_id não está no formato grp_ + 32 hex.
400invalid_contact_idO contact_id não está no formato ct_ + 32 hex.
400invalid_cursorO cursor não é um valor devolvido por uma página anterior. Use o valor exato da última resposta, com os mesmos filtros, ou recomece sem cursor.
404group_not_foundO grupo não existe neste número. Um group_id de outro número responde assim, em vez de devolver uma lista vazia.
404contact_not_foundO contato não existe neste número. Um contact_id de outro número responde assim, em vez de devolver uma lista vazia.
422validation_errorUm parâmetro de consulta não passou na validação: por exemplo limit fora do intervalo de 1 a 200, ou direction diferente de inbound e outbound. A resposta traz details.errors com os campos rejeitados.
500internal_errorErro interno inesperado no servidor.

Veja também: Consultar mensagem · Polling de eventos.