Orquestra · WhatsApp GatewayGuia de integração

Guia de integração — Orquestra WhatsApp Gateway

Guia para quem vai ligar um sistema (condomínio, oficina, loja, ERP) ao gateway e enviar mensagens de WhatsApp. Descreve a API como está em produção, conferida contra o código em 08/11/2026. Começa por um resumo em 5 passos; a lista de todas as rotas está na seção 16.

Comece em 5 passos

Integrar leva poucos minutos e duas chamadas HTTP. Nada de servidor de WhatsApp para manter: o gateway cuida da conexão, da fila, dos limites e da entrega.

  1. Receba seus dados de acesso. O gestor do gateway envia o link, a chave de API e o número que vai enviar (botão Copiar dados de integração). Guarde a chave no servidor do seu sistema.
  2. Conecte o número. Leia o QR Code com o WhatsApp do celular, no painel ou dentro do seu próprio sistema (seção 8).
  3. Teste a integração. Troque a chave por um token e chame GET /v1/diagnostico: a resposta diz, em português, se está tudo pronto ou o que falta.
  4. Envie a primeira mensagem. POST /v1/mensagens com o telefone (pode ser (27) 99999-9999) e o texto. Pronto.
  5. Acompanhe. Consulte a situação ou receba avisos por webhook. Quando quiser, ligue anexos, enquetes e o recebimento de respostas.
# 1) token (vale 5 minutos)
curl -X POST https://gateway.orquestrasistemas.com.br/v1/autenticacao/token -H "X-API-Key: <SUA_CHAVE>"

# 2) mensagem
curl -X POST https://gateway.orquestrasistemas.com.br/v1/mensagens \
  -H "Authorization: Bearer <TOKEN>" -H "Idempotency-Key: aviso-0001" -H "Content-Type: application/json" \
  -d '{"destinatario": "(27) 99999-9999", "texto": "Olá! Seu pedido está pronto.", "consentimento": {"evidencia": "Cliente aceitou avisos no cadastro."}}'

O restante deste guia detalha cada passo, os recursos opcionais e os erros.

O caminho completo, do início ao fim

Etapa Chamada O que observar
1. Token POST /v1/autenticacao/token com X-API-Key Guarde o token e reaproveite até perto de expirar (seção 2)
2. Conferir a chave GET /v1/credenciais/contexto Tipo da chave, número e permissões (seção 3)
3. Número conectado? GET /v1/diagnostico (ou GET /v1/instancias) pronto_para_enviar: true; se não, conecte pelo QR (seção 8)
4. Autorização da pessoa POST /v1/contatos/consentimentos ou o objeto consentimento no envio Sem consentimento, o envio responde 422 (seção 6)
5. Enviar POST /v1/mensagens com Idempotency-Key 202 = aceita na fila, ainda não entregue (seção 4)
6. Acompanhar Webhook mensagem.situacao (seção 9) ou GET /v1/mensagens/{mensagem_id} entregue, lida, falhou (seção 7)
7. Saldo e limites GET /v1/saldo, GET /v1/limites Evite o 402 acompanhando disponiveis (seções 4.5 e 11)
8. Respostas (se contratou) Webhook mensagem.recebida ou GET /v1/mensagens-recebidas Responda com responder_a (seções 8.1 e 8.2)
9. Quem pediu para sair POST /v1/contatos/descadastros Quem responde SAIR no WhatsApp já é descadastrado sozinho (seções 6, 8.1)

1. O que você precisa receber

O gestor do gateway entrega três informações, de preferência pelo botão Copiar dados de integração da tela de chaves, por um canal seguro:

Informação Exemplo Para que serve
Link do gateway https://gateway.orquestrasistemas.com.br Base de todas as chamadas. As rotas da API começam em /v1/.
Chave de API oak_… (segredo exibido uma única vez) Vai no cabeçalho X-API-Key, só para pedir o token temporário. Guarde-a no servidor do seu sistema, nunca no navegador, no aplicativo ou no Git.
Instância Condomínio Simon — instancia_id (UUID) O número de WhatsApp que envia. Já vem amarrado à chave: você não informa o número remetente nas mensagens. Serve para consultar e para o QR Code.

Também importam:

Permissão (código) Nome canônico Nome no painel Libera
messages.send mensagens:enviar Enviar mensagens Enviar (/v1/mensagens, lote), verificar número, saldo e diagnóstico completo
messages.read mensagens:ler Consultar mensagens Situação e lista de mensagens, votos de enquete, fila, números (/v1/instancias, também com instancias:conectar) e grupo
messages.cancel mensagens:cancelar Cancelar mensagens Cancelar mensagem ainda não despachada
consents.write contatos:escrever Registrar consentimento e descadastro Registrar e consultar consentimento, descadastrar e usar o objeto consentimento no envio
contatos:ler contatos:ler Consultar consentimento do destinatário GET /v1/contatos/consentimentos (só consulta)
instances.connect instancias:conectar Conectar o número por QR Conectar, consultar o QR e desconectar o número da chave
messages.receive mensagens:receber Ler mensagens recebidas Ler, baixar mídia e apagar mensagens recebidas (módulo do plano)
media.write arquivos:enviar Enviar arquivos Enviar documentos, imagens, áudio e vídeo
media.read arquivos:ler Consultar arquivos Consultar e baixar os arquivos enviados pela chave
analytics.read consumo:ler Consultar consumo GET /v1/consumo, /v1/saldo, /v1/limites e /v1/assinatura
campaigns.write campanhas:iniciar Campanhas Criar, listar e iniciar campanhas (a aprovação é sempre feita por uma pessoa no painel)

Os dois nomes de cada linha são equivalentes: uma chave com mensagens:enviar vale onde se pede messages.send e vice-versa, e quem pode registrar consentimento também pode consultá-lo. Se faltar alguma, o gestor abre Chaves de API › Editar permissões no painel e marca o que falta: o segredo não muda, o seu sistema não precisa trocar nada e a permissão vale na hora, inclusive para o token já emitido.

Para descobrir, a qualquer momento, a instância e as permissões da sua chave: GET /v1/credenciais/contexto (seção 3). Para conferir se está tudo pronto para enviar: GET /v1/diagnostico (seção 3).

2. Autenticação: chave → token temporário

A chave nunca acompanha as mensagens. Ela só troca por um token temporário (300 segundos), usado nas demais chamadas.

curl -X POST https://gateway.orquestrasistemas.com.br/v1/autenticacao/token \
  -H "X-API-Key: <CHAVE_DE_API>"
{ "token_acesso": "oat_…", "token_tipo": "Bearer", "expira_em_segundos": 300 }

3. Conferir a credencial

curl https://gateway.orquestrasistemas.com.br/v1/credenciais/contexto \
  -H "Authorization: Bearer <TOKEN>"
{
  "chave_id": "…",
  "tipo_destino": "fixo",
  "instancia_id": "…",
  "grupo_balanceamento_id": null,
  "escopos": ["messages.send", "messages.read"]
}

tipo_destino fixo é chave individual (tem instancia_id); balanceado é chave geral (tem grupo_balanceamento_id). Qualquer token válido pode consultar.

Testar a integração (botão "Testar integração")

GET /v1/diagnostico confere, sem enviar mensagem, tudo o que o gateway exige para despachar: chave ativa e com permissão de envio, conta e aplicação ativas, assinatura vigente, saldo de mensagens do plano, número conectado e não pausado, horário de envio, limites por minuto/hora/dia e fila andando. Qualquer token válido pode chamar (até 20 vezes por minuto); chave sem a permissão de envio recebe só a indicação de que falta essa permissão.

curl "https://gateway.orquestrasistemas.com.br/v1/diagnostico?destinatario=(27)%2099999-9999" \
  -H "Authorization: Bearer <TOKEN>"
{
  "pronto_para_enviar": false,
  "resumo": "Não é possível enviar agora: \"Portaria\" não está conectado (situação: disconnected). Leia o QR Code para conectar.",
  "verificacoes": [
    {
      "codigo": "chave",
      "titulo": "Chave de API ativa",
      "situacao": "ok",
      "detalhe": "Sem data de expiração."
    },
    {
      "codigo": "saldo",
      "titulo": "Saldo de mensagens do plano",
      "situacao": "ok",
      "detalhe": "488 de 500 disponíveis até 07/12/2026, 11:00 (12 enviadas, 0 na fila)."
    },
    {
      "codigo": "numero",
      "titulo": "Número conectado",
      "situacao": "erro",
      "detalhe": "\"Portaria\" não está conectado (situação: disconnected). Leia o QR Code para conectar."
    }
  ],
  "credencial": {
    "chave_id": "…",
    "tipo_destino": "fixo",
    "instancia_id": "…",
    "grupo_balanceamento_id": null,
    "escopos": ["…"]
  },
  "numeros": [
    {
      "instancia_id": "…",
      "nome": "Portaria",
      "situacao": "disconnected",
      "envios_pausados": false,
      "janela_aberta": true
    }
  ],
  "plano": {
    "modelo_cobranca": "mensal",
    "limite_mensagens": 500,
    "usadas_no_periodo": 12,
    "na_fila": 0,
    "disponiveis": 488,
    "…": "…"
  },
  "destino": {
    "destinatario": "+5527999999999",
    "finalidade": "transacional",
    "consentido": true,
    "descadastrado": false
  },
  "verificado_em": "…"
}

No painel, a ação Testar de cada chave mostra o mesmo resultado (proprietário, administrador e auditor).

4. Enviar uma mensagem

curl -X POST https://gateway.orquestrasistemas.com.br/v1/mensagens \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Idempotency-Key: pedido-1234-pronto" \
  -H "Content-Type: application/json" \
  -d '{
    "destinatario": "(27) 99999-9999",
    "texto": "Seu veículo está pronto para retirada.",
    "consentimento": { "evidencia": "Cliente aceitou avisos de serviço no cadastro." }
  }'

Resposta 202 (aceita na fila, ainda não entregue):

{
  "mensagem_id": "…",
  "situacao": "na_fila",
  "criado_em": "…",
  "consulta": "/v1/mensagens/…",
  "requisicao_id": "…"
}
Campo Obrigatório Regra
destinatario sim Telefone. Veja a seção 5.
texto um conteúdo Até 16.000 caracteres, com emojis e a formatação do WhatsApp (*negrito*, _itálico_).
arquivo_id um conteúdo Arquivo enviado antes por POST /v1/arquivos (seção 4.2), com legenda opcional (até 4.000 caracteres). Com áudio OGG, "voz": true chega como mensagem de voz gravada.
localizacao um conteúdo { "latitude": -20.31, "longitude": -40.31, "nome": "Oficina", "endereco": "…" } — chega como mapa.
contato um conteúdo { "nome": "Portaria", "telefone": "(27) 3333-4444", "empresa": "…" } — chega como cartão de contato com botão para conversar.
enquete um conteúdo { "pergunta": "Aprova a reforma?", "opcoes": ["Sim", "Não"], "multipla": false } — 2 a 12 opções diferentes; resultado em GET /v1/mensagens/{id}/votos (seção 8.3).
responder_a não mensagem_recebida_id de uma mensagem recebida: a resposta chega citando a original (seção 8.2).
finalidade não Motivo do contato; padrão transacional. Precisa ser exatamente a do consentimento. Não pode começar com resposta: (reservado).
consentimento não { "evidencia": "…", "concedido_em": "…" }. Registra a autorização no primeiro envio (seção 6).
agendar_para não Data e hora ISO 8601 com fuso (ex.: 2026-11-20T09:00:00-03:00), até 30 dias à frente.
campanha_id não Só para campanhas aprovadas desta chave (seção 10).
metadados não Até 20 pares texto → texto (até 500 caracteres cada), para o seu controle (por exemplo, número do pedido). Ficam guardados cifrados e não voltam nas consultas.

4.1 Enviar em lote

Até 100 mensagens numa chamada, cada uma com sua própria chave de idempotência. Um item recusado não impede os outros.

curl -X POST https://gateway.orquestrasistemas.com.br/v1/mensagens/lote \
  -H "Authorization: Bearer <TOKEN>" -H "Idempotency-Key: lote-2026-11-08-0001" -H "Content-Type: application/json" \
  -d '{ "itens": [
        { "chave_idempotencia": "revisao-1001", "mensagem": { "destinatario": "(27) 99999-9999", "texto": "Revisão amanhã às 9h." } },
        { "chave_idempotencia": "revisao-1002", "mensagem": { "destinatario": "(27) 98888-7777", "texto": "Revisão amanhã às 10h." } }
      ] }'
{
  "itens": [
    {
      "chave_idempotencia": "revisao-1001",
      "aceita": true,
      "mensagem_id": "…",
      "situacao": "na_fila",
      "criado_em": "…",
      "consulta": "/v1/mensagens/…"
    },
    {
      "chave_idempotencia": "revisao-1002",
      "aceita": false,
      "erro": {
        "codigo": "CONSENTIMENTO_AUSENTE",
        "mensagem": "Destinatário sem consentimento válido para esta finalidade. …",
        "status": 422
      }
    }
  ],
  "requisicao_id": "…"
}

4.2 Enviar arquivos (anexos)

São duas chamadas: primeiro o arquivo, depois a mensagem com o arquivo_id.

# 1) Envia o arquivo (multipart, campo "arquivo"). Exige a permissão media.write.
curl -X POST https://gateway.orquestrasistemas.com.br/v1/arquivos \
  -H "Authorization: Bearer <TOKEN>" \
  -F "arquivo=@orcamento.pdf;type=application/pdf"
# → 202 { "arquivo_id": "…", "situacao": "disponivel", "tipo_mime": "application/pdf", "tamanho_bytes": 48211 }

# 2) Envia a mensagem com o arquivo.
curl -X POST https://gateway.orquestrasistemas.com.br/v1/mensagens \
  -H "Authorization: Bearer <TOKEN>" -H "Idempotency-Key: orcamento-1234" -H "Content-Type: application/json" \
  -d '{ "destinatario": "(27) 99999-9999", "arquivo_id": "…", "legenda": "Seu orçamento" }'
Formato Chega como
PDF, Word (DOCX), Excel (XLSX), PowerPoint (PPTX) Documento, com o nome original do arquivo
JPEG, PNG, WebP Foto, com a legenda
MP3, M4A, OGG Áudio; OGG com "voz": true chega como mensagem de voz gravada
MP4 Vídeo, com a legenda

4.3 Verificar se o número tem WhatsApp

curl -X POST https://gateway.orquestrasistemas.com.br/v1/contatos/verificar \
  -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \
  -d '{ "destinatario": "(27) 99999-9999" }'
# → 200 { "destinatario": "+5527999999999", "tem_whatsapp": true, "verificado_em": "…", "origem": "consulta" }

4.4 Agendar e cancelar

curl -X POST https://gateway.orquestrasistemas.com.br/v1/mensagens/<MENSAGEM_ID>/cancelar \
  -H "Authorization: Bearer <TOKEN>"

Rotas POST sem corpo (cancelar, conectar, desconectar, iniciar campanha): envie sem Content-Type, ou com Content-Type: application/json e o corpo {}. Cabeçalho JSON com corpo vazio responde 400.

4.5 Saldo do plano

Cada plano tem um limite de mensagens por período da assinatura (do início ao fim da assinatura, não do mês do calendário), somando todas as chaves e números da organização. Consulte com GET /v1/saldo (permissão messages.send ou analytics.read):

{
  "modelo_cobranca": "mensal",
  "limite_mensagens": 500,
  "limite_efetivo": 500,
  "teto_excedente_mensagens": 0,
  "excedente_usado": 0,
  "valor_mensagem_excedente": 0,
  "valor_excedente_estimado_centavos": 0,
  "usadas_no_periodo": 402,
  "na_fila": 15,
  "disponiveis": 83,
  "percentual_usado": 83,
  "periodo_inicio": "2026-11-07T15:56:04.885Z",
  "periodo_fim": "2026-12-07T15:56:04.885Z",
  "recebimento": {
    "incluido": true,
    "limite_recebidas": 100,
    "guardadas_no_periodo": 12,
    "excedentes": 0,
    "descartadas": 0,
    "valor_excedente_centavos": 0,
    "a_cobrar_centavos": 0,
    "retencao_maxima_dias": 30
  }
}

modelo_cobranca diz como o plano cobra:

Modelo Como funciona Campos que importam
mensal Mensalidade com limite de mensagens no período. Ao atingir o limite, o envio para até a renovação. limite_mensagens, disponiveis, periodo_fim (renovação)
franquia_excedente A mensalidade inclui limite_mensagens; o que passar, até teto_excedente_mensagens, é cobrado na próxima fatura a valor_mensagem_excedente por mensagem. excedente_usado e valor_excedente_estimado_centavos mostram o acumulado. limite_efetivo (franquia + teto), excedente_usado, valor_excedente_estimado_centavos
pacote_creditos Créditos pré-pagos (limite_mensagens) válidos até periodo_fim; comprar outro pacote soma os créditos que sobraram. disponiveis (créditos restantes), periodo_fim (validade)

Exemplo de franquia com excedente: franquia de 1.000, teto de 200 excedentes a R$ 0,12 cada; já foram 1.035 mensagens.

{
  "modelo_cobranca": "franquia_excedente",
  "limite_mensagens": 1000,
  "teto_excedente_mensagens": 200,
  "limite_efetivo": 1200,
  "usadas_no_periodo": 1035,
  "excedente_usado": 35,
  "valor_mensagem_excedente": 0.12,
  "valor_excedente_estimado_centavos": 420,
  "na_fila": 12,
  "disponiveis": 153,
  "percentual_usado": 87,
  "…": "…"
}

O envio é bloqueado ao atingir limite_efetivo (limite do plano + teto de excedente, na franquia).

5. Formato do telefone

Envie o número como o seu sistema tiver guardado. O gateway remove pontuação, espaços, traços e parênteses e inclui o +55 quando houver DDD + número:

Você envia O gateway usa
(27) 99999-9999, 27 99999.9999, 27999999999 +5527999999999
027 99999-9999, 55 27 99999-9999, 0055… +5527999999999
(27) 3333-4444 (fixo) +552733334444
+351 912 345 678 (outro país, com +) +351912345678

Número sem DDD ou estrangeiro sem + responde 400 TELEFONE_INVALIDO: o gateway não adivinha país. As consultas devolvem o número no formato internacional (+55…).

Isso vale para os campos destinatario e destinatarios no corpo das requisições, para o telefone do cartão de contato, e para os filtros remetente (mensagens recebidas) e destinatario (diagnóstico). Exceção: a consulta GET /v1/contatos/consentimentos exige o telefone já em E.164, com o + codificado como %2B (seção 6).

6. Consentimento

Toda mensagem precisa de autorização do destinatário para a finalidade (LGPD e regras do WhatsApp). Duas formas:

  1. Antes do envio: POST /v1/contatos/consentimentos com destinatario, finalidade, evidencia e concedido_em opcional (por exemplo, no cadastro do cliente). Exige consents.write.

bash curl -X POST https://gateway.orquestrasistemas.com.br/v1/contatos/consentimentos \ -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json" \ -d '{ "destinatario": "(27) 99999-9999", "finalidade": "transacional", "evidencia": "Aceitou avisos por WhatsApp no formulário de cadastro em 01/11/2026." }' # → 201 { "id": "…" }

  1. No primeiro envio: o objeto consentimento na mensagem (exige a permissão consents.write). Nos envios seguintes ele já existe e pode ser omitido (se vier, é ignorado).

Consultar antes de registrar ou enviar: GET /v1/contatos/consentimentos?destinatario=%2B5527999999999&finalidade=transacional (permissão contatos:ler ou contatos:escrever) responde sempre 200:

{
  "destinatario": "+5527999999999",
  "finalidade": "transacional",
  "consentido": true,
  "descadastrado": false,
  "registro_id": "…",
  "concedido_em": "2026-11-07T10:00:00Z",
  "expira_em": null
}

O telefone vai em E.164 (+55…, com o + codificado como %2B) e a finalidade precisa ser exatamente a usada no registro. Descadastro prevalece (consentido=false, descadastrado=true). A consulta não cria nada: não registre consentimento só porque ela voltou falso; registre quando houver a autorização da pessoa.

Regras:

7. Acompanhar a entrega

GET /v1/mensagens/{mensagem_id} devolve situacao, motivo_fila e datas. GET /v1/mensagens lista as 100 mais recentes da sua chave (sem filtros nesta versão). Ambos exigem messages.read.

{
  "mensagem_id": "…",
  "situacao": "entregue",
  "motivo_fila": null,
  "criado_em": "…",
  "atualizado_em": "…"
}
Situação Significado
na_fila Aceita, aguardando vez (limites, horário, conexão ou horário agendado)
reservada Separada para envio
em_envio Sendo enviada
aceita_provedor O WhatsApp recebeu
entregue Chegou ao celular
lida Foi lida (se o destinatário permite confirmação de leitura)
falhou Não foi possível enviar
incerta Sem confirmação; não é reenviada automaticamente (evita mensagem duplicada)
expirada Passou 24 h do horário previsto sem conseguir enviar
cancelada Cancelada por você, por descadastro/consentimento revogado ou por encerramento da conta

motivo_fila explica a espera (ou o fim) da mensagem:

motivo_fila Significado
aguardando_intervalo_entre_envios Espaçamento entre envios do número (seção 11); sai em segundos
sem_instancia_elegivel_quota_janela_ou_afinidade Número desconectado, fora do horário de envio ou no limite por minuto/hora/dia
quota_assinatura Saldo do plano esgotado
conta_assinatura_ou_chave_indisponivel Conta, assinatura ou chave indisponível (a fila fica pausada)
grupo_pausado_ou_restrito, grupo_com_restricao_requer_revisao, grupo_sem_minimo_de_membros Problema no grupo da chave geral: fale com o gestor
campanha_pausada, campanha_aguarda_provedor_oficial Mensagem de campanha (seção 10)
consentimento_ausente_ou_revogado Cancelada: a pessoa saiu ou o consentimento foi revogado
numero_sem_whatsapp Falhou: o número não tem WhatsApp (conferência prévia ligada)
reconciliacao_necessaria Incerta: o serviço reiniciou durante o envio

Também ajudam: GET /v1/filas (até 100 mensagens da chave em na_fila, agendada ou incerta, com o motivo) e GET /v1/consumo (total de mensagens da chave no mês, por situação).

Para não ficar consultando, use webhooks (seção 9).

8. Conectar o número pelo seu sistema (QR Code)

Para o administrador do cliente ler o QR Code dentro do seu sistema, sem acesso ao painel do gateway. Exige a permissão instancias:conectar (ou instances.connect) e, para acompanhar a situação, messages.read. Chave individual acessa só o próprio número; chave geral, só os números ativos do seu grupo.

  1. Descubra o número: GET /v1/credenciais/contexto (instancia_id) ou GET /v1/instancias.
  2. POST /v1/instancias/{instancia_id}/conectar (sem corpo, ou {}) → 200 com {"solicitada", "instancia_id", "situacao"}. Se já estiver conectado, ou já houver conexão/QR em andamento, solicitada vem false e nada é reiniciado: não chame conectar a cada atualização da tela.
  3. A cada 3 segundos, GET /v1/instancias/{instancia_id}/qr:
  4. 200 com qr e expira_em: desenhe o conteúdo de qr como QR Code no seu servidor (ele troca sozinho a cada 20 a 60 segundos);
  5. 404 QR_INDISPONIVEL: ainda gerando, já lido ou conectado. Confira a situacao em GET /v1/instancias (ou no diagnóstico): connected significa pareado; feche a tela.
  6. Para trocar de número: POST /v1/instancias/{instancia_id}/desconectar e repita. Desconectar só funciona com chave individual (chave geral: 409 CHAVE_SEM_INSTANCIA); limite de 10 por minuto.

A chave e o token ficam no seu servidor; o navegador recebe só a imagem do QR. Não guarde o QR em logs nem em URLs. Instância restrita pelo WhatsApp responde 409 INSTANCIA_RESTRITA e exige análise do gestor. Número de outra chave responde 404 INSTANCIA_INDISPONIVEL. Cada pedido de conexão, desconexão e cada QR exibido ficam na auditoria (sem o conteúdo do QR).

Situações do número (situacao em GET /v1/instancias), com os nomes que a API devolve hoje: created (nunca conectado), qr_pending (aguardando leitura do QR), connecting, connected (único que envia), reconnecting, disconnected, logged_out (sessão encerrada no celular: leia o QR de novo), restricted (restringido pelo WhatsApp), paused e error.

8.1 Receber mensagens (módulo do plano)

Cada número tem um modo de recebimento, escolhido pelo gestor no painel (Instâncias › Editar):

Modo O que acontece
Ignorar Nada é lido.
Só descadastro automático (padrão) Quem responder só SAIR, PARAR, CANCELAR, DESCADASTRAR, REMOVER ou STOP é descadastrado na hora (envios para ele passam a ser recusados). Nenhuma mensagem é guardada.
Recebimento completo Além do descadastro, cada mensagem é guardada cifrada pelo prazo escolhido (até o máximo do plano), avisada por webhook e consultável pela API. Exige o módulo no plano.

Grupos e o histórico antigo do celular são ignorados (grupos podem ser ligados por número). O módulo é cobrado pelo plano: uma cota de mensagens guardadas por período e, se o plano prever, um valor por mensagem acima da cota; sem esse valor, acima da cota as mensagens deixam de ser guardadas. O consumo aparece em GET /v1/saldo (campo recebimento).

Com a permissão messages.receive:

# Novas desde a última consulta (ordem de chegada; guarde o recebido_em da última):
curl "https://gateway.orquestrasistemas.com.br/v1/mensagens-recebidas?apos=2026-11-07T18:00:00Z" \
  -H "Authorization: Bearer <TOKEN>"
{
  "itens": [
    {
      "mensagem_recebida_id": "…",
      "instancia_id": "…",
      "remetente": "+5527999999999",
      "nome_exibicao": "Maria",
      "tipo": "texto",
      "texto": "Meu carro está pronto?",
      "legenda": null,
      "nome_arquivo": null,
      "voz": false,
      "localizacao": null,
      "contato": null,
      "reacao": null,
      "tem_midia": false,
      "midia_tipo": null,
      "midia_tamanho": null,
      "resposta_a_mensagem_id": "…",
      "recebido_em": "…",
      "expira_em": "…"
    }
  ]
}

8.2 Responder (atendimento, "responda 1 para confirmar")

Envie a mensagem normalmente com responder_a = mensagem_recebida_id. Ela sai pelo mesmo número que recebeu e chega citando a original:

{ "destinatario": "+5527999999999", "texto": "Está pronto, pode retirar.", "responder_a": "…" }

Quem escreveu nas últimas 24 horas autoriza a resposta: o gateway registra esse consentimento sozinho, com a mensagem recebida como evidência, válido por 24 horas e preso àquela conversa (finalidade resposta:<id>). Ele vale só para atendimento (finalidade transacional ou atendimento), não autoriza outros envios para a pessoa e não vale em campanhas (RESPOSTA_EM_CAMPANHA). Depois disso, ou para quem pediu para sair, valem as regras normais da seção 6. O destinatario precisa ser quem escreveu (422 RESPOSTA_OUTRO_DESTINATARIO). Para um robô de confirmação, leia o texto recebido (por exemplo, 1) e responda com responder_a.

8.3 Enquetes

{
  "destinatario": "+5527999999999",
  "enquete": { "pergunta": "Aprova a reforma da fachada?", "opcoes": ["Sim", "Não", "Abstenção"] }
}
{
  "mensagem_id": "…",
  "pergunta": "Aprova a reforma da fachada?",
  "multipla": false,
  "total_eleitores": 2,
  "opcoes": [
    { "opcao": "Sim", "votos": 1 },
    { "opcao": "Não", "votos": 1 },
    { "opcao": "Abstenção", "votos": 0 }
  ],
  "votos": [{ "eleitor": "+5527999999999", "opcoes": ["Sim"], "votado_em": "…" }]
}

9. Webhooks (avisos automáticos)

Em vez de ficar consultando, cadastre no painel (Webhooks, proprietário ou administrador) uma URL do seu sistema e os eventos desejados. O segredo do webhook (owh_…) aparece uma única vez na criação: guarde-o no servidor.

Evento Quando Corpo (JSON)
mensagem.situacao A cada mudança de situação de uma mensagem enviada evento_id, evento, mensagem_id, situacao, ocorrido_em
mensagem.recebida Mensagem recebida guardada (recebimento completo) evento_id, evento, mensagem_recebida_id, instancia_id, remetente, tipo, texto (ou legenda), tem_midia, resposta_a_mensagem_id, descadastro, recebido_em
contato.descadastrado Alguém respondeu SAIR (ou equivalente) no WhatsApp evento_id, evento, destinatario, instancia_id, motivo (pedido_pelo_whatsapp), ocorrido_em
enquete.voto Voto (ou voto retirado) numa enquete enviada evento_id, evento, mensagem_id, eleitor, opcoes, votado_em

Exemplo de mensagem.situacao:

{
  "evento_id": "…",
  "evento": "mensagem.situacao",
  "mensagem_id": "…",
  "situacao": "entregue",
  "ocorrido_em": "2026-11-08T15:57:02.114Z"
}

mensagem.situacao é enviado para na_fila (no aceite), em_envio, aceita_provedor, entregue, lida, falhou e incerta. Cancelamentos e expirações não geram webhook nesta versão: consulte GET /v1/mensagens/{mensagem_id} se precisar confirmá-los. Só entram eventos ocorridos depois de o webhook ser criado.

Como o gateway chama a sua URL: POST com Content-Type: application/json e os cabeçalhos

Cabeçalho Conteúdo
X-Orquestra-Evento O mesmo evento_id do corpo
X-Orquestra-Timestamp Momento do envio, em segundos desde 1970 (UTC)
X-Orquestra-Assinatura sha256=<HMAC-SHA256 em hexadecimal de "<timestamp>.<corpo bruto>">

O que o seu sistema deve fazer:

  1. Ler o corpo bruto (antes de converter o JSON) e calcular HMAC-SHA256(segredo, timestamp + "." + corpo); comparar com a assinatura em tempo constante. Diferente: responda 401 e ignore.
  2. Recusar timestamp com mais de 5 minutos de diferença do seu relógio (proteção contra repetição).
  3. Tratar evento_id já processado como duplicado (a entrega é "ao menos uma vez").
  4. Responder 2xx rápido (em até 10 segundos) e processar depois, se for demorado.

Retentativas: erro 5xx, 429, tempo esgotado ou falha de conexão são repetidos com espera crescente (10 s, 20 s, 40 s… até 1 hora, com pequena variação), até 8 tentativas no total. Outro 4xx encerra as tentativas daquele evento. Redirecionamentos não são seguidos.

Requisitos da URL: https://, porta 443, com nome de domínio (não IP) que aponte só para endereços públicos; sem usuário/senha nem # na URL.

// Node.js (Express): conferir a assinatura com o corpo bruto.
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const vistos = new Set(); // em produção, guarde no banco
app.post('/webhooks/orquestra', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Orquestra-Timestamp') ?? '';
  const recebida = req.get('X-Orquestra-Assinatura') ?? '';
  const esperada =
    'sha256=' +
    crypto
      .createHmac('sha256', process.env.ORQUESTRA_SEGREDO_WEBHOOK)
      .update(`${timestamp}.${req.body.toString('utf8')}`)
      .digest('hex');
  const valida =
    recebida.length === esperada.length &&
    crypto.timingSafeEqual(Buffer.from(recebida), Buffer.from(esperada));
  if (!valida || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.sendStatus(401);
  const evento = JSON.parse(req.body.toString('utf8'));
  if (!vistos.has(evento.evento_id)) {
    vistos.add(evento.evento_id);
    // processar: evento.evento, evento.mensagem_id, evento.situacao…
  }
  res.sendStatus(204);
});
<?php
// PHP: conferir a assinatura com o corpo bruto.
$corpo = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_ORQUESTRA_TIMESTAMP'] ?? '';
$esperada = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $corpo, getenv('ORQUESTRA_SEGREDO_WEBHOOK'));
if (!hash_equals($esperada, $_SERVER['HTTP_X_ORQUESTRA_ASSINATURA'] ?? '') || abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit;
}
$evento = json_decode($corpo, true);
// ignore $evento['evento_id'] já processado; depois trate $evento['evento']
http_response_code(204);
# Python (Flask): conferir a assinatura com o corpo bruto.
import hashlib, hmac, os, time
from flask import Flask, request

app = Flask(__name__)

@app.post("/webhooks/orquestra")
def receber():
    corpo = request.get_data()  # bytes, sem converter
    timestamp = request.headers.get("X-Orquestra-Timestamp", "")
    esperada = "sha256=" + hmac.new(
        os.environ["ORQUESTRA_SEGREDO_WEBHOOK"].encode(), timestamp.encode() + b"." + corpo, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(esperada, request.headers.get("X-Orquestra-Assinatura", "")):
        return "", 401
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
        return "", 401
    evento = request.get_json()
    # ignore evento["evento_id"] já processado; depois trate evento["evento"]
    return "", 204

10. Campanhas

Para avisos a muitas pessoas de uma vez (até 500 destinatários por campanha), com a permissão campaigns.write:

  1. Criar: POST /v1/campanhas com nome, finalidade, texto, destinatarios (lista de telefones) e agendar_para opcional → 201 {"campanha_id", "situacao": "rascunho", "instrucao"}.
  2. Aprovar: sempre por uma pessoa no painel (proprietário ou administrador). A API não aprova.
  3. Iniciar: POST /v1/campanhas/{campanha_id}/iniciar (sem corpo) → 202 {"iniciada": true, "mensagens": 120}. É tudo ou nada: só começa se a campanha estiver aprovada (senão 409 CAMPANHA_BLOQUEADA), se o saldo cobrir todos (402) e se todos tiverem consentimento registrado para a finalidade (senão 422 CONSENTIMENTO_AUSENTE e nada é enfileirado).
  4. Acompanhar: GET /v1/campanhas (as 100 mais recentes da chave) e as mensagens em GET /v1/filas.

Atenção: com o conector atual do WhatsApp (não oficial), as mensagens de campanha ficam na fila com motivo_fila = campanha_aguarda_provedor_oficial e expiram em 24 horas: o despacho de campanhas aguarda a integração com o provedor oficial. Para avisos a vários contatos hoje, use o lote (seção 4.1), que segue as regras de consentimento e os limites do número.

Situações da campanha: rascunho, aprovada, agendada, executando, pausada, concluida, cancelada.

11. Limites e espaçamento

Limite Valor O que acontece
Requisições por IP (todas as rotas) 120 por minuto 429 LIMITE_REQUISICOES com Retry-After
Emitir token 30 por minuto 429
Verificar número 30 por minuto e 500 consultas novas por dia (organização) 429 / 429 VERIFICACOES_EXCEDIDAS
Diagnóstico 20 por minuto 429
Desconectar número 10 por minuto 429
Corpo JSON 128 KiB 413
Arquivo 16 MB; um envio por vez por organização 413 ARQUIVO_GRANDE / 429 UPLOAD_OCUPADO
Lote 100 mensagens 400
Campanha 500 destinatários 400 / 422 DESTINATARIOS_INVALIDOS
Mensagens pendentes na organização 10.000 429 FILA_CHEIA
Agendamento Até 30 dias à frente 422 AGENDAMENTO_DISTANTE
Prazo de envio 24 h depois do horário previsto Situação expirada
Envios por número Padrão 5 por minuto, 100 por hora, 500 por dia (o gestor ajusta no painel) Espera na fila (motivo_fila), não falha
Horário de envio por número Padrão 08:00 às 20:00, fuso America/Sao_Paulo (ajustável) Espera a janela abrir
Saldo do plano Conforme o plano (seção 4.5) 402 LIMITE_DO_PLANO_ATINGIDO

Os cabeçalhos X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset mostram quanto resta da janela de 1 minuto. Os limites do seu número aparecem em GET /v1/limites (permissão analytics.read).

Espaçamento: os envios de cada número saem espaçados por igual — o intervalo é 60 segundos divididos pelo limite por minuto. Com o padrão de 5 por minuto, uma mensagem a cada 12 segundos; com 2, a cada 30; com 3, a cada 20. Mensagens que chegam juntas aguardam a vez (motivo_fila = aguardando_intervalo_entre_envios); o 202 continua imediato. Isso protege o número contra bloqueio por envio em rajada. Na chave geral, cada número do grupo tem seu próprio espaçamento, o que aumenta a vazão total.

12. Erros

Os erros vêm com Content-Type: application/problem+json, no formato:

{
  "type": "about:blank",
  "codigo": "CONSENTIMENTO_AUSENTE",
  "title": "Destinatário sem consentimento válido para esta finalidade. Registre-o antes ou envie o objeto \"consentimento\" junto com a mensagem.",
  "status": 422,
  "requisicao_id": "req-1a"
}

Use codigo para tratar o erro no seu sistema e title para mostrar ao usuário. Guarde o requisicao_id: ele identifica a chamada para o suporte.

HTTP codigo Causa Como resolver
400 ENTRADA_INVALIDA Campo faltando, a mais ou fora do formato; Idempotency-Key ausente Conferir o corpo com a seção 4 (ou o docs/openapi.json)
400 ENTRADA_INVALIDA JSON malformado, ou Content-Type: application/json com corpo vazio (a mensagem explica) Enviar JSON válido; em POST sem corpo, não mande Content-Type (ou mande {})
404 ROTA_INEXISTENTE Caminho ou método que não existe Conferir a URL e o método na tabela de rotas
400 TELEFONE_INVALIDO Telefone sem DDD ou estrangeiro sem + Incluir DDD ou o +código do país (seção 5)
400 FINALIDADE_RESERVADA, OPCOES_REPETIDAS Finalidade resposta:…; opções de enquete repetidas Usar outra finalidade; opções diferentes
401 CHAVE_INVALIDA Chave errada ou ausente no pedido de token Conferir a chave (X-API-Key)
401 TOKEN_INVALIDO Token ausente, expirado ou revogado Pedir um token novo e repetir com a mesma Idempotency-Key
402 LIMITE_DO_PLANO_ATINGIDO Saldo do plano esgotado Aguardar a renovação ou ampliar o plano; não repetir automaticamente
403 ESCOPO_NEGADO Falta permissão na chave Pedir ao gestor a permissão (seção 1)
403 CONTA_BLOQUEADA Conta suspensa, envios pausados, aplicação inativa ou assinatura vencida Falar com o gestor; as mensagens já aceitas esperam na fila
403 CHAVE_BLOQUEADA Chave revogada, suspensa ou expirada Pedir uma chave nova
403 IP_NEGADO IP do servidor fora da lista da chave Pedir ao gestor para incluir o IP
404 MENSAGEM_INDISPONIVEL, ARQUIVO_INDISPONIVEL, INSTANCIA_INDISPONIVEL, MIDIA_INDISPONIVEL Não existe ou é de outra chave (mesma resposta) Conferir o identificador
404 QR_INDISPONIVEL Sem QR no momento Continuar consultando; ver a situação em GET /v1/instancias
406 ERRO_INTERNO (status 406) POST /v1/arquivos sem multipart Enviar multipart/form-data com o campo arquivo
409 IDEMPOTENCIA_CONFLITANTE Mesma Idempotency-Key com outro conteúdo Gerar outra chave para a mensagem nova
409 DESTINATARIO_DESCADASTRADO A pessoa pediu para não receber Não enviar; novo opt-in exige revisão administrativa
409 CANCELAMENTO_INDISPONIVEL Envio já começou ou terminou Nada a fazer
409 INSTANCIA_DESCONECTADA Nenhum número conectado para verificar Conectar o número (seção 8)
409 INSTANCIA_RESTRITA, CHAVE_SEM_INSTANCIA Número restringido pelo WhatsApp; desconectar com chave geral Falar com o gestor
409 CAMPANHA_BLOQUEADA Campanha não aprovada, inexistente ou de outra chave Aprovar no painel
413 ARQUIVO_GRANDE, ARMAZENAMENTO_ESGOTADO Arquivo acima de 16 MB; espaço do plano esgotado Reduzir o arquivo; ampliar o plano
415 TIPO_INVALIDO Formato não aceito ou diferente do declarado Usar um formato da seção 4.2 com o tipo correto
422 CONSENTIMENTO_AUSENTE Sem consentimento para a finalidade (ou descadastrado) Registrar o consentimento (seção 6)
422 ARQUIVO_INDISPONIVEL arquivo_id inexistente, de outra chave ou expirado Enviar o arquivo de novo com a mesma chave
422 AGENDAMENTO_DISTANTE agendar_para mais de 30 dias à frente Agendar mais perto
422 VOZ_EXIGE_OGG, VOZ_EXIGE_ARQUIVO voz sem áudio OGG Converter para OGG (Opus) ou tirar voz
422 RESPOSTA_INDISPONIVEL, RESPOSTA_OUTRO_DESTINATARIO, RESPOSTA_EM_CAMPANHA responder_a inválido para este envio Conferir o mensagem_recebida_id e o destinatário
422 NAO_E_ENQUETE Votos pedidos de mensagem que não é enquete Usar o mensagem_id da enquete
422 IDENTIFICADOR_INVALIDO Identificador na URL que não é UUID Conferir o identificador
422 IDEMPOTENCIA_DUPLICADA Duas mensagens do lote com a mesma chave_idempotencia Usar chaves diferentes
422 DATA_INVALIDA concedido_em no futuro Usar a data real
422 ARQUIVO_RECUSADO, ARQUIVO_AUSENTE Antivírus recusou; nenhum arquivo enviado Conferir o arquivo
422 MODULO_NAO_CONTRATADO (Painel) recebimento completo fora do plano Contratar plano com o módulo
429 LIMITE_REQUISICOES Muitas chamadas por minuto Esperar o Retry-After e ir mais devagar
429 FILA_CHEIA, UPLOAD_OCUPADO, VERIFICACOES_EXCEDIDAS Fila de 10.000, upload em andamento, teto de verificações Aguardar e tentar de novo
500 ERRO_INTERNO Falha inesperada Tentar de novo depois; informar o requisicao_id
503 ANTIVIRUS_INDISPONIVEL, VERIFICACAO_INDISPONIVEL Serviço auxiliar sem resposta Tentar de novo em instantes

Quando repetir automaticamente: só em falha de rede, 429, 500 e 503, com espera crescente e a mesma Idempotency-Key. Nunca repita automaticamente 400, 402, 403, 409 ou 422: o resultado será o mesmo até alguém corrigir a causa. Rota inexistente responde 404 sem codigo (formato padrão do servidor).

13. Exemplos completos

Os exemplos leem a chave e o endereço de variáveis de ambiente (ORQUESTRA_CHAVE_API e ORQUESTRA_API_URL), nunca do código. Versões em 8 linguagens: pasta exemplos/ do repositório.

cURL (fluxo completo)

BASE="https://gateway.orquestrasistemas.com.br"
TOKEN=$(curl -s -X POST "$BASE/v1/autenticacao/token" -H "X-API-Key: $ORQUESTRA_CHAVE_API" \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["token_acesso"])')

curl -s "$BASE/v1/diagnostico" -H "Authorization: Bearer $TOKEN"          # tudo pronto?
curl -s "$BASE/v1/saldo" -H "Authorization: Bearer $TOKEN"                # saldo do plano

curl -s -X POST "$BASE/v1/mensagens" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: pedido-1234-pronto" -H "Content-Type: application/json" \
  -d '{"destinatario":"(27) 99999-9999","texto":"Seu veículo está pronto.","consentimento":{"evidencia":"Cliente aceitou avisos no cadastro."}}'

curl -s "$BASE/v1/mensagens/<MENSAGEM_ID>" -H "Authorization: Bearer $TOKEN"  # situação

PHP

<?php
// Configuração: guarde a chave em variável de ambiente, nunca no código.
const BASE = 'https://gateway.orquestrasistemas.com.br';

function orquestra(string $metodo, string $rota, ?array $corpo = null, ?string $token = null, ?string $idempotencia = null): array
{
    $cabecalhos = [$token ? "Authorization: Bearer $token" : 'X-API-Key: ' . getenv('ORQUESTRA_CHAVE_API')];
    if ($idempotencia) {
        $cabecalhos[] = "Idempotency-Key: $idempotencia";
    }
    $ch = curl_init(BASE . $rota);
    $opcoes = [CURLOPT_CUSTOMREQUEST => $metodo, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15];
    if ($corpo !== null) {
        // Só manda Content-Type quando há corpo (POST sem corpo com JSON vazio responde 400).
        $cabecalhos[] = 'Content-Type: application/json';
        $opcoes[CURLOPT_POSTFIELDS] = json_encode($corpo, JSON_UNESCAPED_UNICODE);
    }
    $opcoes[CURLOPT_HTTPHEADER] = $cabecalhos;
    curl_setopt_array($ch, $opcoes);
    $resposta = json_decode((string) curl_exec($ch), true) ?? [];
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    if ($status >= 400) {
        throw new Exception(($resposta['codigo'] ?? $status) . ': ' . ($resposta['title'] ?? ''));
    }
    return $resposta;
}

$token = orquestra('POST', '/v1/autenticacao/token')['token_acesso'];
$envio = orquestra('POST', '/v1/mensagens', [
    'destinatario' => '(27) 99999-9999',
    'texto' => 'Seu veículo está pronto para retirada.',
    'consentimento' => ['evidencia' => 'Cliente aceitou avisos de serviço no cadastro.'],
], $token, 'pedido-1234-pronto');
echo $envio['mensagem_id'], PHP_EOL;
echo orquestra('GET', '/v1/mensagens/' . $envio['mensagem_id'], null, $token)['situacao'], PHP_EOL;

Python (3.10 ou superior, biblioteca requests)

import os, time
import requests

BASE = "https://gateway.orquestrasistemas.com.br"
_token = {"valor": None, "expira": 0.0}


def obter_token() -> str:
    if _token["valor"] and time.time() < _token["expira"] - 30:
        return _token["valor"]
    r = requests.post(f"{BASE}/v1/autenticacao/token", headers={"X-API-Key": os.environ["ORQUESTRA_CHAVE_API"]}, timeout=15)
    r.raise_for_status()
    dados = r.json()
    _token.update(valor=dados["token_acesso"], expira=time.time() + dados["expira_em_segundos"])
    return dados["token_acesso"]


def enviar_mensagem(destinatario: str, texto: str, idempotencia: str) -> dict:
    r = requests.post(
        f"{BASE}/v1/mensagens",
        headers={"Authorization": f"Bearer {obter_token()}", "Idempotency-Key": idempotencia},
        json={"destinatario": destinatario, "texto": texto},
        timeout=15,
    )
    if r.status_code != 202:
        erro = r.json()
        raise RuntimeError(f"{erro.get('codigo')}: {erro.get('title')}")
    return r.json()  # { mensagem_id, situacao: 'na_fila', consulta, ... }


aceita = enviar_mensagem("(27) 99999-9999", "Seu veículo está pronto.", "pedido-1234-pronto")
situacao = requests.get(f"{BASE}{aceita['consulta']}", headers={"Authorization": f"Bearer {obter_token()}"}, timeout=15)
print(aceita["mensagem_id"], situacao.json()["situacao"])

Node.js (18 ou superior)

const BASE = 'https://gateway.orquestrasistemas.com.br';
let token = null;
let expiraEm = 0;

async function obterToken() {
  if (token && Date.now() < expiraEm - 30_000) return token;
  const r = await fetch(`${BASE}/v1/autenticacao/token`, {
    method: 'POST',
    headers: { 'X-API-Key': process.env.ORQUESTRA_CHAVE_API },
  });
  if (!r.ok) throw new Error(`Token: ${r.status}`);
  const dados = await r.json();
  token = dados.token_acesso;
  expiraEm = Date.now() + dados.expira_em_segundos * 1000;
  return token;
}

export async function enviarMensagem(destinatario, texto, idempotencia) {
  const r = await fetch(`${BASE}/v1/mensagens`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${await obterToken()}`,
      'Idempotency-Key': idempotencia,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ destinatario, texto }),
  });
  const dados = await r.json();
  if (!r.ok) throw new Error(`${dados.codigo}: ${dados.title}`);
  return dados; // { mensagem_id, situacao: 'na_fila', consulta, ... }
}

export async function consultarSituacao(idMensagem) {
  const r = await fetch(`${BASE}/v1/mensagens/${idMensagem}`, {
    headers: { Authorization: `Bearer ${await obterToken()}` },
  });
  return r.json(); // { situacao, motivo_fila, ... }
}

14. Boas práticas

15. Quem acessa o quê

No painel, cada pessoa da organização tem um papel. O gestor do gateway (administração global) vê todas as organizações, gerencia planos, aprova pagamentos e acompanha a saúde de todos os números.

Papel Pode Não pode
Proprietário Tudo na organização: números e QR Code, chaves, aplicações, grupos, membros, webhooks, consentimentos, campanhas, contratação do plano, recebimento e exclusão de mensagens recebidas —
Administrador O mesmo que o proprietário no dia a dia —
Operador Enviar e cancelar mensagens, verificar números, ver mensagens, consentimentos, campanhas e mensagens recebidas, responder Criar ou aprovar campanhas, criar ou ver chaves, gerenciar números, membros e plano; ver auditoria e pagamentos
Auditor Ver tudo, inclusive chaves (só o prefixo), membros, auditoria e pagamentos; usar Testar nas chaves Alterar qualquer coisa ou enviar mensagens
Suporte Ver números, saúde do serviço, filas e consumo Ver conteúdo de negócio (mensagens, recebidas, consentimentos, arquivos, campanhas) e chaves

Na API, quem acessa é o sistema integrado, pela chave. A chave só enxerga a organização dela e, conforme o tipo, o número (individual) ou o grupo (geral) a que está ligada; mensagens, arquivos e campanhas só são vistos pela chave que os criou. Cada rota exige a permissão correspondente (tabela da seção 1). O segredo da chave é exibido uma única vez; o painel guarda só um resumo irreversível e o prefixo. Revogar a chave bloqueia na hora todos os tokens emitidos por ela.

Dados pessoais. Mensagens, mídias e mensagens recebidas ficam cifradas; mensagens recebidas são apagadas no fim do prazo de retenção ou a pedido (DELETE /v1/mensagens-recebidas/{id}). Os registros técnicos não guardam conteúdo das mensagens.

16. Todas as rotas

Base: https://gateway.orquestrasistemas.com.br. Todas, exceto a primeira, usam Authorization: Bearer <token>. Quando há duas permissões, qualquer uma basta.

Método Rota Permissão Resposta Seção
POST /v1/autenticacao/token Chave no X-API-Key 200 2
GET /v1/credenciais/contexto Qualquer token 200 3
GET /v1/diagnostico Qualquer token (destinatario: envio) 200 3
POST /v1/mensagens messages.send 202 4
GET /v1/mensagens messages.read 200 7
POST /v1/mensagens/lote messages.send 202 4.1
GET /v1/mensagens/{mensagem_id} messages.read 200 7
POST /v1/mensagens/{mensagem_id}/cancelar messages.cancel 200 4.4
GET /v1/mensagens/{mensagem_id}/votos messages.read 200 8.3
POST /v1/arquivos media.write 202 4.2
GET /v1/arquivos/{arquivo_id} media.read 200 4.2
GET /v1/arquivos/{arquivo_id}/conteudo media.read 200 4.2
POST /v1/contatos/consentimentos consents.write 201 6
GET /v1/contatos/consentimentos contatos:ler ou consents.write 200 6
POST /v1/contatos/descadastros consents.write 200 6
POST /v1/contatos/verificar messages.send 200 4.3
GET /v1/instancias messages.read ou instancias:conectar 200 8
POST /v1/instancias/{instancia_id}/conectar instances.connect 200 8
GET /v1/instancias/{instancia_id}/qr instances.connect 200 8
POST /v1/instancias/{instancia_id}/desconectar instances.connect 200 8
GET /v1/grupos messages.read 200 3
GET /v1/saldo messages.send ou analytics.read 200 4.5
GET /v1/assinatura analytics.read 200 4.5
GET /v1/limites analytics.read 200 11
GET /v1/consumo analytics.read 200 7
GET /v1/filas messages.read 200 7
GET /v1/campanhas campaigns.write 200 10
POST /v1/campanhas campaigns.write 201 10
POST /v1/campanhas/{campanha_id}/iniciar campaigns.write 202 10
GET /v1/mensagens-recebidas messages.receive 200 8.1
GET /v1/mensagens-recebidas/{id} messages.receive 200 8.1
DELETE /v1/mensagens-recebidas/{id} messages.receive 204 8.1
GET /v1/mensagens-recebidas/{id}/midia messages.receive 200 8.1

GET /v1/grupos devolve o grupo da chave geral (id, nome, politica, situacao); para chave individual, lista vazia.