Quickstart
Em 5 passos você tem uma instância conectada e enviando mensagens.
- Obter API key — Entre em contato com o time comercial para obter as credenciais do seu tenant.
- Criar instância —
POST /instance/createcom o nome e a URL do seu webhook. - Escanear QR —
GET /instance/:name/qre escanear o código com o celular. - Aguardar conexão —
GET /instance/:name/statusaté receberstate: "open". - Enviar primeira mensagem —
POST /message/text.
# 1. Criar instância
curl -X POST https://wa.sinapsia.com.ar/instance/create \
-H "apikey: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"instanceName":"acme__soporte__main","webhookUrl":"https://meu-saas.com/webhooks/whatsapp","webhookSecret":"meu-segredo-seguro-32"}'
# 2. Obter QR (imagem base64)
curl https://wa.sinapsia.com.ar/instance/acme__soporte__main/qr \
-H "apikey: SUA_API_KEY"
# 3. Verificar estado (aguardar state: "open")
curl https://wa.sinapsia.com.ar/instance/acme__soporte__main/status \
-H "apikey: SUA_API_KEY"
# 4. Enviar primeira mensagem
curl -X POST https://wa.sinapsia.com.ar/message/text \
-H "apikey: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"instanceName":"acme__soporte__main","to":"5491112345678","text":"Olá! Esta é minha primeira mensagem.","lane":"normal"}'
Autenticação
Todas as requests (exceto GET /health) exigem o header apikey.
apikey: SUA_API_KEY
| Header | Acesso |
|---|---|
apikey: <sua-api-key> |
Apenas instâncias e recursos do seu tenant |
Naming de instâncias
Todas as instâncias seguem o formato:
{tenantId}__{clientId}__{label}
Exemplo:
acme__soporte__main
__). O sistema valida este formato antes de criar qualquer instância.
Lanes (Anti-ban)
Cada mensagem é enviada por uma das três lanes. A lane controla o delay e os limites de envio para proteger o número do WhatsApp.
| Lane | Delay | Limite/hora | Limite/dia | Usar para |
|---|---|---|---|---|
urgent |
1–2s | Sem limite | Sem limite | OTP, alertas críticos |
normal |
3–8s | 80 msg | 500 msg | Conversação, notificações |
bulk |
8–15s | 30 msg | 200 msg | Campanhas em massa |
- Limites por instância, não por tenant.
urgentsempre é processado antes denormaloubulk.- Mensagens que excedem o limite aguardam na fila, não são descartadas.
- Padrão se não especificado:
normal.
Endpoints — Instâncias
close até que um número seja vinculado escaneando o QR.{
"instanceName": "acme__soporte__main",
"webhookUrl": "https://meu-saas.com/webhooks/whatsapp",
"webhookSecret": "secreto-minimo-16-caracteres"
}
201 Created
{ "instanceName": "acme__soporte__main", "status": "close" }
400 validação do body · 409 instância já existe
{ "qrcode": "data:image/png;base64,iVBORw0KGgo..." }
{ "phoneNumber": "5491112345678" }
200 OK
{ "pairingCode": "123456" }
{
"instanceName": "acme__soporte__main",
"state": "open",
"phoneNumber": "5491112345678",
"connectedAt": "2026-03-15T10:00:00.000Z",
"messagesQueuedCount": 0
}
| state | Descrição |
|---|---|
close | Não vinculado. Requer QR ou pairing code. |
connecting | QR escaneado. Aguardando confirmação. |
open | Conectado e pronto para enviar mensagens. |
{ "device": "iPhone 12", "platform": "WHATSAPP" }
[
{ "instanceName": "acme__soporte__main", "state": "open", "phoneNumber": "5491112345678" },
{ "instanceName": "acme__ventas__main", "state": "close", "phoneNumber": null }
]
// Array de instâncias do tenant
// Array de instâncias do cliente
{ "urgent": 0, "normal": 3, "bulk": 12, "total": 15 }
Endpoints — Mensagens
202 Accepted imediatamente. A mensagem é enfileirada e enviada após o delay anti-ban correspondente à lane.
Resposta compartilhada (todos os endpoints de envio)
{
"id": "3b2e1f4a-8c9d-4e2f-b7a1-0d5c6e3f9b2a",
"status": "pending",
"lane": "normal",
"queuedAt": "2026-03-15T10:30:00.000Z"
}
text suporta spintax.{
"instanceName": "acme__soporte__main",
"to": "5491112345678",
"text": "{Olá|Oi}! Seu código é 1234.",
"lane": "normal"
}
to: número internacional sem + nem espaços. lane é opcional, padrão normal.caption é opcional e suporta spintax.{
"instanceName": "acme__soporte__main",
"to": "5491112345678",
"imageUrl": "https://cdn.example.com/foto.jpg",
"caption": "Veja isso",
"lane": "normal"
}
{
"instanceName": "acme__soporte__main",
"to": "5491112345678",
"documentUrl": "https://cdn.example.com/fatura.pdf",
"fileName": "fatura-001.pdf",
"caption": "Sua fatura em anexo",
"lane": "normal"
}
{
"instanceName": "acme__soporte__main",
"to": "5491112345678",
"audioUrl": "https://cdn.example.com/audio.ogg",
"lane": "normal"
}
caption é opcional.{
"instanceName": "acme__soporte__main",
"to": "5491112345678",
"videoUrl": "https://cdn.example.com/video.mp4",
"caption": "Assista",
"lane": "normal"
}
instanceName para saber qual número foi escolhido.{
"tenantId": "acme",
"clientId": "soporte",
"to": "5491112345678",
"type": "text",
"text": "Olá!",
"lane": "normal"
}
type é obrigatório: text | image | document | audio | video. Cada tipo requer seus campos específicos adicionais.{
"id": "3b2e1f4a-8c9d-4e2f-b7a1-0d5c6e3f9b2a",
"instanceName": "acme__soporte__main",
"lane": "normal",
"to": "5491112345678",
"type": "text",
"status": "sent",
"createdAt": "2026-03-15T10:30:00.000Z",
"sentAt": "2026-03-15T10:30:07.000Z",
"error": null
}
| status | Descrição |
|---|---|
| pending | Na fila, aguardando envio |
| sent | Enviado com sucesso |
| failed | Erro — ver campo error |
Webhooks
Ao criar uma instância é fornecido um webhookUrl. O gateway envia um POST para essa URL toda vez que ocorre um evento relevante.
Verificação de assinatura HMAC
Cada request inclui o header x-gateway-signature com um HMAC-SHA256 em hex do body bruto, assinado com seu webhookSecret. Sempre verificar a assinatura antes de processar.
const crypto = require('crypto')
function verifySignature(rawBody, secret, signature) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return expected === signature
}
import hmac, hashlib
def verify_signature(raw_body: bytes, secret: str, signature: str) -> bool:
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return expected == signature
Política de reenvios
O gateway reenvia 3 vezes: delays de 1s, 5s, 30s. Seu endpoint deve responder 2xx rapidamente.
Eventos
instance.connected / instance.disconnected
{
"event": "instance.connected",
"instanceName": "acme__soporte__main",
"data": {
"state": "open",
"reason": null
},
"timestamp": "2026-03-15T10:30:00.000Z"
}
message.received
{
"event": "message.received",
"instanceName": "acme__soporte__main",
"data": {
"from": "5491112345678",
"pushName": "João Silva",
"messageType": "conversation",
"text": "Olá, preciso de ajuda",
"messageId": "AC3EADB76DB5BE4130602FBF2039604A",
"timestamp": 1773596760
},
"timestamp": "2026-03-15T10:30:00.000Z"
}
texténullpara mensagens com mídia.- Apenas mensagens com
fromMe: falsesão repassadas (não as próprias mensagens enviadas). messageType:conversation,imageMessage,documentMessage,audioMessage,videoMessage.
Monitoramento
Endpoints para consultar estatísticas e gerenciar a fila de mensagens das suas instâncias.
{
"instances": 2,
"totals": { "queued": 5, "sent": 1250, "failed": 3 },
"perInstance": [
{
"instanceName": "acme__soporte__main",
"queue": { "urgent": 0, "normal": 5, "bulk": 2, "total": 7 },
"messages": { "sent": 450, "failed": 1, "pending": 7 }
}
]
}
{ "instanceName": "acme__soporte__main", "flushed": 12 }
GET /health
Endpoint de saúde. Não requer autenticação. Útil para monitores e load balancers.
{
"status": "ok",
"tenants": 1,
"instances": 2,
"queuedMessages": 5,
"uptime": 3600
}
Erros
| Status | Significado | Ação |
|---|---|---|
400 |
Body inválido | Ver campo issues na resposta |
401 |
API key ausente ou inválida | Verificar header apikey |
403 |
Sem acesso a este recurso | O instanceName deve começar com seu tenantId |
404 |
Instância ou mensagem não existe | Verificar nome ou ID |
409 |
Instância já existe | Usar a existente ou escolher outro nome |
502 |
Erro no serviço do WhatsApp | Verificar sessão WhatsApp; tentar novamente |
500 |
Erro interno | Reportar ao operador |
Formato de erro 400
{
"error": "Validation error",
"issues": [
{
"path": ["instanceName"],
"message": "Must follow {tenant}__{client}__{label} format"
}
]
}
Spintax
O spintax permite gerar variantes de texto para evitar mensagens repetitivas. É resolvido aleatoriamente no momento do envio.
Sintaxe: {opção1|opção2|opção3} — suporta aninhamento.
# Simples
{Olá|Oi|E aí}! {Como vai|Tudo bem}?
→ "Oi! Tudo bem?"
# Aninhado
{Bom{-dia|a tarde}|Olá}!
→ "Boa tarde!"
text (em /message/text) · campo caption (em /message/image, /message/document, /message/video)
Referência rápida
| Campo | Formato | Exemplo |
|---|---|---|
instanceName |
{tenant}__{client}__{label} |
acme__soporte__main |
to |
Número internacional sem + |
5491112345678 |
lane |
urgent / normal / bulk |
normal |
type (route) |
text / image / document / audio / video |
text |
webhookSecret |
String, mín. 16 caracteres | meu-segredo-seguro-32 |
| Timestamps | ISO 8601 UTC | 2026-03-15T10:30:00.000Z |
| IDs de mensagem | UUID v4 | 3b2e1f4a-... |
Integrações
Configuração pronta para usar com as plataformas mais populares. Todos os exemplos enviam uma mensagem de texto via POST /message/text — o mesmo padrão se aplica a todos os outros endpoints.
n8n
Adicione um nó HTTP Request e configure assim:
Method: POST
URL: https://wa.sinapsia.com.ar/message/text
Auth: Header Auth → Name: apikey / Value: {{ $vars.WA_API_KEY }}
Body: JSON
{
"instanceName": "acme__suporte__main",
"to": "{{ $json.phone }}",
"text": "{{ $json.message }}",
"lane": "normal"
}
Make (ex-Integromat)
Módulo HTTP → Make a request:
URL: https://wa.sinapsia.com.ar/message/text
Method: POST
Headers: apikey: SUA_API_KEY
Body type: Raw
Content type: application/json
Request body:
{
"instanceName": "acme__suporte__main",
"to": "{{1.phone}}",
"text": "{{1.message}}",
"lane": "normal"
}
Zapier
Ação Webhooks by Zapier → POST:
URL: https://wa.sinapsia.com.ar/message/text
Payload Type: JSON
Data:
instanceName acme__suporte__main
to (campo telefone do trigger)
text (campo mensagem do trigger)
lane normal
Headers:
apikey SUA_API_KEY
Power Automate
Adicione uma ação HTTP ao seu fluxo:
Method: POST
URI: https://wa.sinapsia.com.ar/message/text
Headers:
apikey @{variables('WA_API_KEY')}
Content-Type application/json
Body:
{
"instanceName": "acme__suporte__main",
"to": "@{triggerBody()?['phone']}",
"text": "@{triggerBody()?['message']}",
"lane": "normal"
}
Pipedream
Adicione um step de código Node.js:
import axios from "axios"
export default defineComponent({
async run({ steps, $ }) {
const { data } = await axios.post(
"https://wa.sinapsia.com.ar/message/text",
{
instanceName: "acme__suporte__main",
to: steps.trigger.event.phone,
text: steps.trigger.event.message,
lane: "normal",
},
{ headers: { apikey: process.env.WA_API_KEY } }
)
return data
},
})
Home Assistant
Adicione ao configuration.yaml. Guarde a API key no secrets.yaml.
# configuration.yaml
rest_command:
send_whatsapp:
url: https://wa.sinapsia.com.ar/message/text
method: POST
headers:
apikey: !secret wa_api_key
Content-Type: application/json
payload: >
{"instanceName":"acme__suporte__main",
"to":"{{ to }}",
"text":"{{ message }}",
"lane":"normal"}
content_type: application/json
service: rest_command.send_whatsapp e data: {to: "55...", message: "Olá!"}
Seu backend
// fetch nativo (Node.js 18+)
const res = await fetch("https://wa.sinapsia.com.ar/message/text", {
method: "POST",
headers: {
"apikey": process.env.WA_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
instanceName: "acme__suporte__main",
to: "5511912345678",
text: "Olá do Node.js!",
lane: "normal",
}),
})
const data = await res.json()
import httpx, os
res = httpx.post(
"https://wa.sinapsia.com.ar/message/text",
headers={"apikey": os.environ["WA_API_KEY"]},
json={
"instanceName": "acme__suporte__main",
"to": "5511912345678",
"text": "Olá do Python!",
"lane": "normal",
},
)
data = res.json()
// Guzzle HTTP
$client = new \GuzzleHttp\Client();
$res = $client->post('https://wa.sinapsia.com.ar/message/text', [
'headers' => ['apikey' => getenv('WA_API_KEY')],
'json' => [
'instanceName' => 'acme__suporte__main',
'to' => '5511912345678',
'text' => 'Olá do PHP!',
'lane' => 'normal',
],
]);
$data = json_decode($res->getBody(), true);
import (
"bytes"; "encoding/json"; "net/http"; "os"
)
payload, _ := json.Marshal(map[string]string{
"instanceName": "acme__suporte__main",
"to": "5511912345678",
"text": "Olá do Go!",
"lane": "normal",
})
req, _ := http.NewRequest("POST", "https://wa.sinapsia.com.ar/message/text", bytes.NewBuffer(payload))
req.Header.Set("apikey", os.Getenv("WA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)