Consultar mensagem
Recupere o conteúdo completo, o status de entrega e a referência de mídia de qualquer mensagem enviada ou recebida na sua conta, usando o id como identificador.
Use GET /v1/messages/{id} para o número principal. Para números extras, use GET /v1/numbers/{ref}/messages/{id}, onde {ref} é o alias atual do número ou o id dele (num_…, o number_id da mensagem). Prefira o id: o alias lido numa mensagem antiga pode ter sido renomeado depois.
Escopo obrigatório: messages:read
Consultar mensagem
curl -X GET \ https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70 \ -H "Authorization: Bearer SUA_CHAVE"const messageId = 'msg_01993c5a1b24d3e9f01a2b3c4d5e6f70';const response = await fetch( `https://api.turbonotify.com/v1/messages/${messageId}`, { method: 'GET', headers: { 'Authorization': 'Bearer SUA_CHAVE' } });
const message = await response.json();console.log(message);import requests
message_id = "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70"url = f"https://api.turbonotify.com/v1/messages/{message_id}"headers = {"Authorization": "Bearer SUA_CHAVE"}
response = requests.get(url, headers=headers)message = response.json()print(message)package main
import ( "fmt" "io" "net/http")
func main() { messageID := "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70" req, _ := http.NewRequest("GET", "https://api.turbonotify.com/v1/messages/"+messageID, nil) req.Header.Set("Authorization", "Bearer SUA_CHAVE") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))}<?php$messageId = 'msg_01993c5a1b24d3e9f01a2b3c4d5e6f70';$url = "https://api.turbonotify.com/v1/messages/{$messageId}";
$options = [ 'http' => [ 'header' => ['Authorization: Bearer SUA_CHAVE'], 'method' => 'GET' ]];
$context = stream_context_create($options);$result = file_get_contents($url, false, $context);$response = json_decode($result, true);var_dump($response);?>var client = new HttpClient();client.DefaultRequestHeaders.Add("Authorization", "Bearer SUA_CHAVE");
var messageId = "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70";var response = await client.GetAsync( $"https://api.turbonotify.com/v1/messages/{messageId}");var result = await response.Content.ReadAsStringAsync();Console.WriteLine(result);import java.net.http.*;import java.net.URI;
public class GetMensagem { public static void main(String[] args) throws Exception { var client = HttpClient.newHttpClient(); var messageId = "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70";
var request = HttpRequest.newBuilder() .uri(URI.create( "https://api.turbonotify.com/v1/messages/" + messageId)) .header("Authorization", "Bearer SUA_CHAVE") .GET() .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); }}Resposta: mensagem de texto (outbound)
{
"id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70",
"organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"number_alias": "main",
"number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e",
"from": {
"name": "Turbo Notify",
"number": "5511973117056",
"alias": "main"
},
"to": {
"name": "João",
"number": "5511988887777"
},
"preview": "Olá, seu pedido foi confirmado!",
"content_expired": false,
"text": "Olá, seu pedido foi confirmado!",
"content_type": "text",
"direction": "outbound",
"channel": "whatsapp",
"origin": "tn",
"at": "2026-06-01T13:00:05Z",
"status": {
"state": "delivered",
"at": "2026-06-01T13:01:00Z"
}
} Resposta: mensagem de imagem (inbound)
{
"id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f71",
"organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"number_alias": "main",
"number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e",
"from": {
"name": "João",
"number": "5511988887777"
},
"to": {
"name": "Turbo Notify",
"number": "5511973117056",
"alias": "main"
},
"preview": "Foto do produto",
"content_expired": false,
"image": {
"caption": "Foto do produto",
"kind": "available",
"url": "https://your-bucket.r2.cloudflarestorage.com/organizations/.../image.jpeg?X-Amz-...",
"expires_at": "2026-06-01T14:00:00Z",
"mime_type": "image/jpeg",
"size_bytes": 123456
},
"content_type": "image",
"direction": "inbound",
"channel": "whatsapp",
"origin": "native",
"at": "2026-06-01T13:00:00Z",
"status": {
"state": "read",
"at": "2026-06-01T13:05:00Z"
}
} Campos da resposta
from e to são objetos cujo formato depende da direção da mensagem, o mesmo usado nos webhooks. O seu número na conversa é identificado pelo number_id (e pelo number_alias, o alias que ele tinha na hora); os dados dele estão em GET /v1/numbers/{ref}, com o number_id.
| Campo | Tipo | Descrição |
|---|---|---|
id | string | Identificador público da mensagem (msg_ + 32 hex) |
organization_id | string | Identificador público da sua organização (org_{hex32}). Sempre presente. |
number_alias | string | O alias que o número Turbo Notify 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. Para ler os dados do número, use GET /v1/numbers/{ref} com o number_id. |
number_id | string | Identificador do número (num_…), logo depois do number_alias. Nunca muda, mesmo que o alias seja renomeado. |
from | object | Remetente: o formato depende da direção |
to | object | Destinatário: o formato depende da direção |
preview | string | Trecho inicial do conteúdo. Expira junto com o conteúdo, no prazo de retenção do plano (vira ""), veja Sobre preview |
content_expired | boolean | true quando o conteúdo já passou do prazo de retenção do seu plano e foi apagado. A mensagem continua consultável por 12 meses, veja Conteúdo expirado |
| campo específico | object|string | Conteúdo completo nomeado pelo content_type (text, image, …). Veja a tabela abaixo. |
content_type | string | text, image, audio, video, document, sticker, location, calendar, contact, interactive. Enquetes e tipos não reconhecidos são armazenados como text. O preview preserva o texto disponível. |
direction | string | outbound (enviada) ou inbound (recebida) |
channel | string | Sempre "whatsapp" |
origin | string | "tn" (envio via Turbo Notify API) ou "native" (digitada no celular). Sempre presente. |
at | string | ISO 8601 em UTC com sufixo Z e precisão de segundos (ex.: 2026-06-01T13:00:05Z): momento estável de ocorrência da mensagem no WhatsApp (outbound: o momento real de envio; inbound: o momento de recebimento). Não muda conforme a mensagem avança sent → delivered → read. Para o momento da entrega/leitura atual, use status.at. |
status | object | {state, at}: estado atual de entrega e o status.at correspondente. Diferente do at raiz, o status.at avança a cada transição (move para delivered, depois read). |
mentions | array | Opcional: presente apenas quando a mensagem menciona alguém (inbound ou outbound). Lista de contatos mencionados, itens {contact_id?, name?, number} (mesmo formato do from inbound), na ordem dos tokens no texto, sem duplicatas. Um @<digitos> no texto só é menção se o número constar aqui. Segue a mesma janela de retenção do restante do conteúdo. Veja Menções (@mentions). |
Campos por content_type
Além dos campos comuns acima, cada content_type expõe um campo específico com o conteúdo completo:
content_type | Campo específico | Descrição |
|---|---|---|
text | text | Texto completo da mensagem |
image | image | Objeto com caption? e campos de mídia (kind, url?, expires_at?, …) diretamente |
video | video | Objeto com caption? e campos de mídia |
audio | audio | Objeto com campos de mídia e transcription? (BYO Speech-to-Text, se configurado) |
document | document | Objeto com filename?, caption? e campos de mídia |
sticker | sticker | Objeto com campos de mídia |
location | location | Objeto com latitude, longitude, name?, address?, url?, is_live?, accuracy_m?, comment?, thumbnail_b64?. is_live aparece apenas quando true (localização em tempo real). Nunca vem como false; trate a ausência da chave como “não é ao vivo”. accuracy_m (precisão do GPS, em metros) e comment (nota de texto anexada pelo remetente) só aparecem em localizações inbound quando o WhatsApp os envia. Não podem ser enviados pela API. |
calendar | calendar | Objeto com name, description?, start_at?, end_at?, join_link?, location?, has_reminder?, reminder_offset_sec?, extra_guests_allowed?. Um calendário inbound também pode trazer is_canceled?/is_scheduled_call? (o organizador cancelou o evento / é uma chamada agendada: sinalizados pelo próprio WhatsApp; is_scheduled_call nunca é enviável pela API). Em um calendário inbound, has_reminder/extra_guests_allowed/is_canceled/is_scheduled_call aparecem apenas quando true: trate a ausência da chave como “não”. Em um calendário outbound enviado pela API, o conteúdo é devolvido exatamente como foi enviado: "has_reminder": false, "extra_guests_allowed": false e chaves explicitamente null (description, end_at, join_link, location, reminder_offset_sec) podem aparecer (is_scheduled_call nunca aparece em calendários outbound; is_canceled aparece como true depois que você cancela o evento pela API). Trate false/null/chave ausente como equivalentes. |
contact | contact | Cartão vCard estruturado: mesmos campos aceitos no envio (Enviar contato): display_name, phones[], emails?, organization?, title?, address? |
interactive | interactive | Objeto com interactive_type (sempre "cta_url"), body, header? ({type: "text", text}), footer?, url_button: {display_text, url} |
Para mensagens replied (citações):
| Campo | Tipo | Descrição |
|---|---|---|
replied | object | {message_id: string, preview: string}: id e trecho da mensagem citada |
Para mensagens reacted:
| Campo | Tipo | Descrição |
|---|---|---|
text | string | O emoji da reação (ex.: "👍") ou "" para remoção |
Exemplo: mensagem de texto
{ "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "Turbo Notify", "alias": "main", "number": "5511973117056" }, "to": { "name": "João", "number": "5511988887777" }, "preview": "Olá, seu pedido foi confirmado!", "content_expired": false, "text": "Olá, seu pedido foi confirmado!", "content_type": "text", "direction": "outbound", "channel": "whatsapp", "origin": "tn", "at": "2026-06-01T13:00:05Z", "status": { "state": "delivered", "at": "2026-06-01T13:01:00Z" }}Exemplo: mensagem de imagem (inbound, BYO Storage ativo)
{ "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f71", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "João", "number": "5511988887777" }, "to": { "alias": "main", "number": "5511973117056" }, "preview": "Foto do produto", "content_expired": false, "image": { "caption": "Foto do produto", "kind": "available", "url": "https://<bucket>.s3.amazonaws.com/...?X-Amz-Signature=...", "expires_at": "2026-06-01T14:00:00Z", "mime_type": "image/jpeg", "size_bytes": 184320 }, "content_type": "image", "direction": "inbound", "channel": "whatsapp", "origin": "native", "at": "2026-06-01T13:00:00Z", "status": { "state": "read", "at": "2026-06-01T13:05:00Z" }}Exemplo: mensagem de imagem (sem BYO Storage)
{ "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f72", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "João", "number": "5511988887777" }, "to": { "alias": "main", "number": "5511973117056" }, "preview": "[Image]", "content_expired": false, "image": { "kind": "unavailable", "reason": "no_storage", "mime_type": "image/jpeg", "size_bytes": 184320, "setup_hint": { "setup_url": "https://dashboard.turbonotify.com/storage", "docs_url": "https://docs.turbonotify.com/general/byo-storage-setup/", "message": "Configure BYO Object Storage to receive media." } }, "content_type": "image", "direction": "inbound", "channel": "whatsapp", "origin": "native", "at": "2026-06-01T13:00:00Z", "status": { "state": "delivered", "at": "2026-06-01T13:00:00Z" }}Exemplo: mensagem de localização
{ "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f73", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "João", "number": "5511988887777" }, "to": { "alias": "main", "number": "5511973117056" }, "preview": "Av. Paulista, Avenida Paulista, 1578, Bela Vista,", "content_expired": false, "location": { "latitude": -23.561414, "longitude": -46.655881, "name": "Av. Paulista", "address": "Avenida Paulista, 1578, Bela Vista, São Paulo - SP", "url": "https://maps.google.com/?q=-23.561414,-46.655881", "thumbnail_b64": "..." }, "content_type": "location", "direction": "inbound", "channel": "whatsapp", "origin": "native", "at": "2026-06-01T13:00:00Z", "status": { "state": "delivered", "at": "2026-06-01T13:00:00Z" }}Exemplo: mensagem de calendário
{ "id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f74", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "João", "number": "5511988887777" }, "to": { "alias": "main", "number": "5511973117056" }, "preview": "Sprint Planning — Mon, 1 Jun 2026 17:00", "content_expired": false, "calendar": { "name": "Sprint Planning", "description": "Planejamento da sprint 24.", "start_at": "2026-06-01T14:00:00-03:00", "end_at": "2026-06-01T15:00:00-03:00", "join_link": "https://meet.example.com/sprint-planning", "has_reminder": true, "reminder_offset_sec": 600 }, "content_type": "calendar", "direction": "inbound", "channel": "whatsapp", "origin": "native", "at": "2026-06-01T13:00:00Z", "status": { "state": "delivered", "at": "2026-06-01T13:00:00Z" }}Exemplo: mensagem interativa (cta_url)
{ "id": "msg_d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "Turbo Notify", "alias": "main", "number": "5511973117056" }, "to": { "name": "João", "number": "5511988887777" }, "preview": "Clique para saber mais", "content_expired": false, "interactive": { "interactive_type": "cta_url", "body": "Clique para saber mais", "footer": "Turbo Notify", "url_button": { "display_text": "Acessar", "url": "https://turbonotify.com" } }, "content_type": "interactive", "direction": "outbound", "channel": "whatsapp", "origin": "tn", "at": "2026-06-01T13:00:05Z", "status": { "state": "delivered", "at": "2026-06-01T13:01:00Z" }}O botão url_button abre o link diretamente no destinatário; header é opcional ({type: "text", text}; por enquanto, só texto).
Exemplo: mensagem com menções
O texto carrega os tokens @<numero> normalizados; a estrutura mentions traz
os contatos correspondentes, já resolvidos:
{ "id": "msg_c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "Turbo Notify", "alias": "main", "number": "5511973117056" }, "to": { "group_id": "grp_e442553a56b14e2695f6acfd7d651b1a", "name": "Equipe Vendas" }, "preview": "Bom dia @5511977776666, pode validar o pedido de hoje?", "content_expired": false, "text": "Bom dia @5511977776666, pode validar o pedido de hoje?", "mentions": [ { "contact_id": "ct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "name": "Maria Santos", "number": "5511977776666" } ], "content_type": "text", "direction": "outbound", "channel": "whatsapp", "origin": "tn", "at": "2026-07-03T13:00:05Z", "status": { "state": "delivered", "at": "2026-07-03T13:01:00Z" }}O campo aparece em mensagens inbound e outbound e continua presente depois que o conteúdo expira: ele não traz texto, só os contatos mencionados (veja Retenção das mensagens).
Exemplo: reply (mensagem citada)
{ "id": "msg_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "João", "number": "5511988887777" }, "to": { "alias": "main", "number": "5511973117056" }, "preview": "Sim, é esse pedido mesmo!", "content_expired": false, "text": "Sim, é esse pedido mesmo!", "replied": { "message_id": "msg_99887766554433221100ffeeddccbbaa", "preview": "Pedido #1234 foi aprovado." }, "content_type": "text", "direction": "inbound", "channel": "whatsapp", "origin": "native", "at": "2026-06-01T13:00:00Z", "status": { "state": "delivered", "at": "2026-06-01T13:00:00Z" }}Exemplo: reação
{ "id": "msg_b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5", "organization_id": "org_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "number_alias": "main", "number_id": "num_0f8e2c4a6b1d4e7f9a3c5d7e9b1a3c5e", "from": { "name": "João", "number": "5511988887777" }, "to": { "alias": "main", "number": "5511973117056" }, "preview": "👍", "content_expired": false, "text": "👍", "content_type": "text", "direction": "inbound", "channel": "whatsapp", "origin": "native", "at": "2026-06-01T13:00:00Z", "status": { "state": "delivered", "at": "2026-06-01T13:00:00Z" }}Campos de mídia
Os campos de mídia aparecem diretamente no objeto específico (image, video, audio, document, sticker), sem sub-objeto media. O campo kind indica qual dos formatos abaixo a mídia tem:
kind: "available"
Mídia disponível para download. Requer Object Storage configurado (abre em nova aba) .
| Campo | Tipo | Descrição |
|---|---|---|
kind | string | "available" |
url | string | URL pré-assinada de download |
expires_at | string | null | Timestamp de expiração do acesso à mídia. Consulte a mensagem novamente para obter uma nova URL após a expiração, dentro do prazo de retenção de conteúdo do plano. |
mime_type | string | null | Tipo MIME do arquivo (ex: image/jpeg, application/pdf) |
size_bytes | integer | null | Tamanho do arquivo em bytes |
kind: "pending"
A mídia ainda está sendo copiada para o seu bucket. Aguarde alguns segundos e consulte novamente.
| Campo | Tipo | Descrição |
|---|---|---|
kind | string | "pending" |
mime_type | string | null | Tipo MIME detectado |
size_bytes | integer | null | Tamanho do arquivo em bytes |
kind: "unavailable"
Mídia não está disponível. O campo reason indica a causa.
| Campo | Tipo | Descrição |
|---|---|---|
kind | string | "unavailable" |
reason | string | no_storage: nenhum Object Storage configurado; storage_circuit_open: o seu Object Storage vinha falhando repetidamente no momento do recebimento, e o envio de mídia para ele estava suspenso temporariamente; upload_failed: storage saudável, mas este item não pôde ser persistido; deleted: a mensagem foi apagada/revogada depois que a mídia já havia sido armazenada; expired: o conteúdo passou do prazo de retenção do seu plano e foi apagado (veja Conteúdo expirado). O motivo é registrado no momento do recebimento, da exclusão ou da expiração e não muda depois. |
mime_type | string | null | Tipo MIME detectado |
size_bytes | integer | null | Tamanho do arquivo em bytes |
setup_hint | object | null | Presente quando reason é no_storage ou storage_circuit_open. Contém setup_url, docs_url e message. |
audio.transcription
Quando BYO Speech-to-Text está ligado na sua organização, mensagens de áudio inbound ganham o campo transcription dentro do objeto audio, também identificado por kind (omitido quando a transcrição não foi tentada, por exemplo em áudio outbound ou sem transcrição configurada). Mesmo formato do data.transcription dos webhooks/polling de message.received:
| Campo | Tipo | Descrição |
|---|---|---|
kind | string | "available": transcrição concluída, ou "unavailable": não disponível. |
text | string | (apenas available) Texto transcrito. |
language | string | null | (apenas available) Código BCP-47 do idioma detectado/configurado, quando disponível. |
reason | string | (apenas unavailable) Um destes 6 valores: not_configured, provider_error, file_too_large (áudio acima de 24 MB), timeout, provider_not_supported (o provedor de transcrição configurado não é suportado no momento) ou internal_error. |
hint | string | null | (apenas unavailable) Orientação opcional, escrita para quem lê o evento (com o link da tela Transcrição do dashboard quando há algo a ajustar lá). |
O fluxo da mensagem nunca é bloqueado por falha de transcrição. Para ligar a transcrição, veja Transcrição de áudio.
Status da mensagem
O campo status contém o estado atual de entrega:
| Campo | Tipo | Descrição |
|---|---|---|
state | string | Estado atual: pending (aceito no 202, ainda não enviado ao WhatsApp), sent, delivered, read, failed (outbound); delivered, read (inbound) |
at | string | ISO 8601: timestamp do estado atual de entrega; avança a cada transição (sent → delivered → read). Não confundir com o at no nível raiz, que é o momento estável de ocorrência da mensagem. |
error | object | Erro estruturado da falha: presente apenas quando state: "failed"; ausente (chave omitida) nos demais estados. Mesmo formato do data.error dos webhooks *.failed. |
error.code | string | Código estável: decida o tratamento da falha por este valor. |
error.message | string | Descrição legível da falha, para logs e depuração. Pode trazer um diagnóstico do WhatsApp em inglês; identificadores pessoais (números, JIDs) nunca aparecem nela. O contrato estável é o code, não este texto. |
error.hint | string | Orientação acionável, derivada do code. Ausente (chave omitida, nunca null) quando não há orientação para o code. |
Sobre preview
O campo preview segue exatamente o mesmo prazo de retenção de conteúdo do plano da sua organização, veja Retenção das mensagens. Ele não é guardado por mais tempo que o restante do conteúdo. Enquanto o conteúdo está dentro do prazo, preview traz um trecho do conteúdo, sem partir emoji ou acento ao meio:
- Para
text: primeiros ~50 caracteres do texto. - Para
location: texto humanizado sem colchetes (ex:Av. Paulista, Sao Paulo, SP). - Para
calendar: resumo sem colchetes, com dia da semana e mês abreviados em inglês e o horário convertido para UTC (ex:Sprint Planning — Mon, 1 Jun 2026 17:00). - Para mídia: a legenda (quando disponível) ou o rótulo do tipo em inglês (
[Image],[Video],[Audio],[Document],[Sticker]). Documento: legenda → nome do arquivo →[Document]. Áudio com transcrição é a exceção: quando há transcrição disponível, opreviewtraz o texto transcrito (truncado em 50 caracteres).[Audio]só aparece quando não há transcrição nem legenda. - Para
interactive: o texto dobodyda mensagem, ou[Interactive]quando o corpo está vazio. - Para reações: o emoji ou
""para remoção. - Menções: cada token
@<numero>presente emmentionsconta como exatamente 1 caractere no limite de ~50, e o recorte nunca corta um token no meio; um@<digitos>literal fora dementionsconta caractere a caractere. O valor é idêntico aodata.previewdos webhooks/polling.
Depois que o conteúdo passa do prazo de retenção do seu plano, preview vira "" e content_expired passa a true: não há um caminho que devolva o recorte depois desse ponto. O mesmo prazo apaga data.preview (junto com o resto do conteúdo) da cópia guardada de uma entrega de webhook já concluída e dos eventos de GET /v1/events: consulta por id, webhook e polling seguem um único prazo. A exceção é uma entrega de webhook ainda não concluída, que guarda o conteúdo até ser entregue ou até acabarem as novas tentativas (no máximo 72 horas).
Conteúdo expirado
Só o conteúdo expira. Uma mensagem que passou do prazo de retenção de conteúdo do seu plano (1 hora por padrão, até 72 horas conforme o plano) continua consultável por 12 meses, e status, recibos, reações, resposta, reação, edição e exclusão continuam funcionando. Na resposta:
content_expiredétrue.previewetextsão"".- Os blocos de mídia e de conteúdo estruturado (
image,video,audio,document,sticker,location,calendar,contact,interactive) trazem{"kind": "unavailable", "reason": "expired"}, a mesma forma de uma mensagem apagada, com outroreason. replied.previewé""quando a mensagem citada expirou;replied.message_idcontinua lá.mentionscontinua presente.
Uma mensagem apagada mantém a forma de mensagem apagada (reason: "deleted") e content_expired: false.
Sobre kind
Para receber mídia inbound com kind: "available", configure a integração de Object Storage (abre em nova aba) no dashboard. Sem essa configuração, o Turbo Notify não tem onde armazenar a mídia e retorna kind: "unavailable" com reason: "no_storage".
Quando o Object Storage está configurado, o Turbo Notify copia cada mídia recebida para o seu bucket e retorna uma URL pré-assinada com validade configurável. O acesso à mídia expira conforme o expires_at. Você pode obter uma nova URL consultando a mensagem novamente enquanto o conteúdo está dentro do prazo de retenção do seu plano. Depois dele, o bloco de mídia vem como {"kind": "unavailable", "reason": "expired"} e o arquivo continua no seu bucket (Conteúdo expirado).
Erros possíveis
| Status | Código de erro | Descrição |
|---|---|---|
400 | invalid_message_id | O identificador não corresponde ao formato msg_ + 32 hex. |
401 | not_authenticated | A chave de API está inválida ou ausente. |
403 | insufficient_scope | A chave de API não possui o escopo messages:read (resposta inclui required_scope). |
404 | not_found | Mensagem não encontrada, pertence a outra organização ou pertence a um número diferente do da URL (na rota atalho sem {ref}, o número principal): mesma resposta de uma mensagem que nunca existiu. |
404 | number_not_found | (apenas na rota com {ref}) O alias ou o id informado na URL não corresponde a nenhum número cadastrado. |
400 | invalid_number_id | (apenas na rota com {ref}) O valor na URL começa com num_, mas não é um id de número válido. |
429 | rate_limit_exceeded | Limite de requisições ultrapassado. |
500 | internal_error | Erro interno inesperado no servidor. |