/mensagensEnviar mensagem
Texto, mídia (imagem, vídeo, áudio, documento) ou modelo aprovado. Vai para a fila de envio e responde 202 na hora; acompanhe pelo webhook mensagem.status ou por GET /mensagens/{id}. Na API oficial, fora da janela de 24 h só modelo. Envie Idempotency-Key ou id_cliente (UUID) para identificar o envio.
POST /mensagens
curl -X POST "https://chat.forsi.com.br/api/v1/mensagens" \
-H "Authorization: Bearer $FORSI_CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-4821" \
-d '{
"telefone": "+55 31 98888-7777",
"tipo": "texto",
"texto": "Olá! Sua consulta está confirmada para amanhã às 9h."
}'const resposta = await fetch('https://chat.forsi.com.br/api/v1/mensagens', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FORSI_CHAVE}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'pedido-4821',
},
body: JSON.stringify({
"telefone": "+55 31 98888-7777",
"tipo": "texto",
"texto": "Olá! Sua consulta está confirmada para amanhã às 9h."
}),
});
const json = await resposta.json();
if (!resposta.ok) throw new Error(`${resposta.status} ${json.erro.codigo}: ${json.erro.mensagem}`);
console.log(json.dados);import os
import requests
resposta = requests.post(
"https://chat.forsi.com.br/api/v1/mensagens",
headers={
"Authorization": f"Bearer {os.environ['FORSI_CHAVE']}",
"Idempotency-Key": "pedido-4821",
},
json={
"telefone": "+55 31 98888-7777",
"tipo": "texto",
"texto": "Olá! Sua consulta está confirmada para amanhã às 9h.",
},
timeout=30,
)
corpo = resposta.json()
if not resposta.ok:
raise RuntimeError(f"{resposta.status_code} {corpo['erro']['codigo']}: {corpo['erro']['mensagem']}")
print(corpo["dados"]){
"dados": {
"mensagem_id": "4e8cb5d0-2d89-4fc2-93bb-c8e5e17d4629",
"conversa_id": "495a7cae-128c-4b66-adbc-807b3959c129",
"contato_id": "08054a7f-a752-481e-9915-cf4cf944df39",
"caixa_id": "7a9d53e9-9d1b-4122-923b-fac67958f445",
"conversa_nova": true,
"estado": "pendente"
}
}Corpo da requisição#
JSON. Mande com Content-Type: application/json.
id_clienteuuidopcionalUUID estável do envio; substitui Idempotency-Key.
conversa_iduuidopcionalResponder numa conversa que já existe. Ou use telefone.
telefonestringopcionalCom DDI e DDD. Acha o contato com ou sem o 9º dígito.
- mín. 8 caracteres
- até 30 caracteres
- ex.: +55 31 98888-7777
caixa_iduuidopcionalObrigatório quando a organização tem mais de um WhatsApp conectado.
nomestringopcionalNome do contato, se ele ainda não tiver.
- até 120 caracteres
tipo"texto" | "imagem" | "video" | "audio" | "documento" | "modelo"opcional- padrão "texto"
textostringopcionalTexto (ou legenda da mídia).
- até 4096 caracteres
midiaobjectopcional—midia.urlstringobrigatórioLink público https do arquivo. O WhatsApp baixa dele na hora do envio.
- até 2000 caracteres
midia.mimestringopcional- até 100 caracteres
midia.nome_arquivostringopcionalNome que o contato vê (documentos).
- até 200 caracteres
modeloobjectopcional—modelo.iduuidopcionalId do modelo (GET /modelos). Ou use nome + idioma.
modelo.nomestringopcional- até 512 caracteres
modelo.idiomastringopcional- até 15 caracteres
- ex.: pt_BR
modelo.corpolista de string ou objeto livreopcionalValores das variáveis do corpo: lista na ordem ({{1}}, {{2}}...) ou objeto por nome.
modelo.cabecalhoobjectopcional—modelo.botoesobjeto livreopcionalFinal do link (ou código) por índice do botão: {"0": "abc123"}.
responde_a_iduuidopcionalId de uma mensagem desta conversa para citar.
canal"auto" | "oficial" | "qr"opcionalOpcional, só WhatsApp. "auto" (padrão, igual a omitir) segue a regra da caixa; "oficial" sai só pela API oficial da Meta; "qr" sai só pelo WhatsApp por QR. Manual nunca troca de canal: se o escolhido não puder enviar, dá erro.
Resposta#
202 com {"dados": ...}: EnvioAceito.
mensagem_iduuidopcional—conversa_iduuidopcional—contato_iduuid ou nullopcional—caixa_iduuidopcional—conversa_novabooleanopcional—estado"pendente"opcional—
Erros#
Todo erro vem como {"erro": {"codigo", "mensagem"}}. Trate pelo codigo: ele não muda.
| Status | Quando |
|---|---|
| 401 | Chave ausente ou inválida (resposta igual para qualquer motivo). |
| 403 | Plano sem API, escopo insuficiente ou https/origem recusados. |
| 409 | Conflito (repetido, Idempotency-Key com outro corpo). |
| 422 | Dados inválidos ou regra de negócio (ex.: janela_fechada; conta_restrita = a Meta restringiu a conta por spam: erro traz os campos de RestricaoMeta, como ate, escopo, motivo e retry_after_s). |
| 429 | Limite de requisições. Veja Retry-After. |
Códigos de erro que esta área pode devolver22
tipo_invalidotipo deve ser texto, imagem, video, audio, documento ou modelo.texto_vazioMensagem de texto precisa de texto.texto_longoTexto longo demais (máximo de 4096 caracteres).midia_invalidaLink de mídia inválido: use https com um endereço público.midia_indisponivelO arquivo desta mídia está indisponível.midia_baixandoO download desta mídia está na fila. Tente novamente após Retry-After.janela_fechadaA janela de 24 horas desta conversa está fechada. Na API oficial do WhatsApp, só um modelo aprovado reabre a conversa.conta_restritaA Meta restringiu esta conta do WhatsApp por spam. Nada foi enviado: erro.ate diz até quando (erro.retry_after_s e o cabeçalho Retry-After, em segundos), erro.escopo o que está barrado e erro.motivo o código. Quem administra pode desligar a trava em Configurações > API.caixa_obrigatoriaA organização tem mais de um WhatsApp conectado: informe caixa_id (veja GET /me).caixa_sem_conexaoNenhum WhatsApp conectado nesta caixa.caixa_inexistenteCaixa não encontrada (veja GET /me).canal_sem_iniciarEste canal não permite começar conversa por telefone. Use uma caixa de WhatsApp.modelo_sem_api_oficialModelos só saem pela API oficial do WhatsApp. Conecte a API oficial nesta caixa.modelo_inexistenteModelo não encontrado nesta caixa (veja GET /modelos).telefone_invalidoTelefone inválido. Mande com DDI e DDD.conversa_inexistenteConversa não encontrada.mensagem_inexistenteMensagem não encontrada.canal_oficial_indisponivelcanal "oficial": a API oficial do WhatsApp não está conectada nesta caixa. Nada foi enviado pelo QR.canal_oficial_sem_grupocanal "oficial": a API oficial do WhatsApp não envia para grupo. Use "qr" ou omita o campo.canal_qr_indisponivelcanal "qr": o WhatsApp por QR não está conectado nesta caixa. Nada foi enviado pela API oficial.canal_qr_sem_modelocanal "qr": modelos só saem pela API oficial do WhatsApp. Use "oficial" ou omita o campo.canal_so_whatsappcanal "oficial"/"qr" só vale em caixa de WhatsApp. Omita o campo (ou use "auto").
Os erros que valem para qualquer rota (chave, limite, JSON) estão em Erros.