Introduction
The WhatSMS REST API lets you send messages, manage contacts, tickets and campaigns, and listen to real-time events via webhooks. Every request is authenticated by API Key and isolated by tenant.
Base URL
https://api.whatsms.pt/api/v2
Full CRUD, structured pagination, media support and per-API-Key rate limiting.
Per-tenant API Key
Each account has its own API Key. Create and revoke under Settings → Integrations → API Keys.
HTTPS required
All requests must use HTTPS. HTTP requests are rejected automatically.
GDPR-native
Data stays on European servers. Native GDPR compliance, without sending data outside the EU.
Authentication
Every request requires the Authorization header with the API Key in Bearer format. Get your API Key under Settings → Integrations → API Keys.
Required header
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
cURL example
curl -X GET https://api.whatsms.pt/api/v2/contacts \ -H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json"
⚠️ Security
Never expose the API Key in client-side code or public repositories. Use environment variables on the server. If a key is compromised, revoke it immediately under Settings → Integrations → API Keys.
Errors
The API uses standard HTTP status codes. The error body always follows this structure:
Error format
{ "error": "Mensagem descritiva do erro" }| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created successfully |
| 400 | Bad request — missing or invalid parameters |
| 401 | Not authenticated — invalid or missing API Key |
| 403 | No permission for this resource |
| 404 | Resource not found |
| 429 | Rate limit reached — wait before retrying |
| 500 | Internal server error |
Tickets
A ticket represents a conversation with a contact. It groups messages, channel, status and the assigned agent.
/api/v2/ticketsList ticketsParameters
Filter by status: open, pending, closed
Filter by contact
Page (default: 1)
Results per page (default: 20, max: 100)
Response
{
"tickets": [
{
"id": "uuid",
"status": "open",
"channel": "whatsapp",
"contact": { "id": "uuid", "name": "João Silva", "number": "351910000000" },
"lastMessage": "Olá, preciso de ajuda",
"createdAt": "2026-01-15T10:30:00Z"
}
],
"count": 42,
"hasMore": true
}/api/v2/tickets/:ticketIdGet ticket by IDParameters
Ticket ID
Response
{
"ticket": {
"id": "uuid",
"status": "open",
"channel": "whatsapp",
"contact": { "id": "uuid", "name": "João Silva", "number": "351910000000" },
"user": { "id": "uuid", "name": "Agente" },
"queue": { "id": "uuid", "name": "Suporte" },
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T11:00:00Z"
}
}/api/v2/ticketsCreate ticketRequest body
{
"contactId": "uuid", // obrigatório
"status": "open", // open | pending | closed
"channel": "whatsapp", // whatsapp | sms | telegram
"whatsappId": "uuid", // canal WhatsApp a usar
"queueId": "uuid", // fila/departamento
"userId": "uuid" // agente responsável
}Response
{
"ticket": {
"id": "uuid",
"status": "open",
"channel": "whatsapp",
"createdAt": "2026-01-15T10:30:00Z"
}
}/api/v2/tickets/:ticketIdUpdate ticketParameters
Ticket ID
Request body
{
"status": "closed", // open | pending | closed
"userId": "uuid", // reatribuir agente
"queueId": "uuid" // mover para fila
}Response
{
"ticket": { "id": "uuid", "status": "closed", "updatedAt": "2026-01-15T12:00:00Z" }
}Ticket messages
Messages are nested under the ticket. To send a message, use POST /tickets/:id/messages.
/api/v2/tickets/:ticketId/messagesList ticket messagesParameters
Ticket ID
Page (default: 1)
Results per page (default: 20)
Response
{
"messages": [
{
"id": "uuid",
"body": "Olá, preciso de ajuda",
"fromMe": false,
"mediaType": null,
"mediaUrl": null,
"ack": 3,
"createdAt": "2026-01-15T10:30:00Z"
}
],
"count": 15,
"hasMore": false
}/api/v2/tickets/:ticketId/messages60 req/min por API KeySend messageParameters
Ticket ID
Request body
{
"body": "Olá! Como posso ajudar?", // obrigatório (texto ou caption)
"mediaUrl": "https://...", // URL de imagem/documento/áudio
"mediaType": "image", // image | document | audio | video
"quotedMsgId": "uuid" // responder a mensagem específica
}Response
{
"message": {
"id": "uuid",
"body": "Olá! Como posso ajudar?",
"fromMe": true,
"ack": 1,
"createdAt": "2026-01-15T10:31:00Z"
}
}Contacts
Full contact management. A contact can have multiple tickets across multiple channels.
/api/v2/contactsList contactsParameters
Search by name, number or email
Page (default: 1)
Results per page (default: 20)
Response
{
"contacts": [
{
"id": "uuid",
"name": "João Silva",
"number": "351910000000",
"email": "joao@exemplo.pt",
"profilePicUrl": "https://...",
"isGroup": false,
"createdAt": "2026-01-10T09:00:00Z"
}
],
"count": 150,
"hasMore": true
}/api/v2/contacts/:contactIdGet contact by IDParameters
Contact ID
Response
{
"contact": {
"id": "uuid",
"name": "João Silva",
"number": "351910000000",
"email": "joao@exemplo.pt",
"extraInfo": [{ "name": "NIF", "value": "123456789" }],
"createdAt": "2026-01-10T09:00:00Z"
}
}/api/v2/contactsCreate contactRequest body
{
"name": "João Silva", // obrigatório
"number": "351910000000", // obrigatório
"email": "joao@exemplo.pt",
"profilePicUrl": "https://...",
"extraInfo": [
{ "name": "NIF", "value": "123456789" }
]
}Response
{
"contact": {
"id": "uuid",
"name": "João Silva",
"number": "351910000000",
"createdAt": "2026-01-15T10:00:00Z"
}
}/api/v2/contacts/:contactIdUpdate contactParameters
Contact ID
Request body
{
"name": "João M. Silva",
"email": "joao.novo@exemplo.pt",
"extraInfo": [{ "name": "Empresa", "value": "Acme Lda" }]
}Response
{
"contact": { "id": "uuid", "name": "João M. Silva", "updatedAt": "2026-01-15T11:00:00Z" }
}Campaigns
Bulk sending to contact groups via WhatsApp or SMS, with scheduling and status control.
/api/v2/campaignsList campaignsParameters
pending | running | finished | cancelled
Page (default: 1)
Response
{
"campaigns": [
{
"id": "uuid",
"name": "Promoção Verão 2026",
"status": "finished",
"scheduledAt": "2026-06-01T09:00:00Z",
"sentCount": 342,
"failedCount": 3,
"createdAt": "2026-05-20T10:00:00Z"
}
],
"count": 8,
"hasMore": false
}/api/v2/campaignsCreate campaignRequest body
{
"name": "Promoção Verão 2026", // obrigatório
"message": "Olá {{name}}! ...", // obrigatório (suporta {{name}}, {{number}})
"whatsappId": "uuid", // canal a usar
"contactGroupId": "uuid", // grupo de contactos alvo
"scheduledAt": "2026-06-01T09:00:00Z" // null = envio imediato
}Response
{
"campaign": {
"id": "uuid",
"name": "Promoção Verão 2026",
"status": "pending",
"createdAt": "2026-05-20T10:00:00Z"
}
}AI Agents
AI agents reply to tickets automatically using LLMs (Cortecs Sovereign Cloud — EU AI Act compliant). They can use tools such as KiviCare, calendar, CRM and knowledge base.
/api/v2/ai-agentsList AI agentsNo required parameters.
Response
{
"agents": [
{
"id": "uuid",
"name": "Maria — Assistente FamilyClinic",
"model": "cortecs:llama-4-maverick",
"isActive": true,
"enabledIntegrations": ["kivicare"],
"handoffEnabled": true,
"createdAt": "2026-03-01T10:00:00Z"
}
]
}/api/v2/ai-agents/assignAssign AI agent to a ticketRequest body
{
"ticketId": "uuid", // obrigatório
"agentId": "uuid" // obrigatório — null para remover agente
}Response
{
"ticket": { "id": "uuid", "aiAgentId": "uuid", "updatedAt": "2026-01-15T10:00:00Z" }
}Webhooks
Configure an HTTPS endpoint to receive real-time events. WhatSMS makes a POST with a JSON payload signed with HMAC-SHA256.
Signature header
X-WhatSMS-Signature: sha256=<hmac_hex>
Verify the signature with your webhook secret to ensure the request comes from WhatSMS.
Available events
| Event | Description |
|---|---|
| message.received | New message received from a contact |
| message.sent | Message sent successfully |
| message.ack | Delivery/read receipt (ack 1–5) |
| ticket.created | New ticket created |
| ticket.updated | Ticket updated (status, agent, queue) |
| ticket.closed | Ticket closed |
| contact.created | New contact created |
| contact.updated | Contact updated |
| campaign.finished | Campaign finished |
Example payload — message.received
{
"event": "message.received",
"tenantId": "uuid",
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"message": {
"id": "uuid",
"body": "Olá, preciso de ajuda",
"fromMe": false,
"mediaType": null,
"createdAt": "2026-01-15T10:30:00Z"
},
"ticket": { "id": "uuid", "status": "open" },
"contact": { "id": "uuid", "name": "João Silva", "number": "351910000000" }
}
}Rate Limits
Limits are per API Key and per 1-minute window. When you hit the limit you get a 429 with the Retry-After header in seconds.
| Endpoint | Limit |
|---|---|
| All v2 endpoints | 300 req/min por API Key |
| POST /tickets/:id/messages | 60 req/min por API Key |
| POST /campaigns | 10 req/min por API Key |
SDKs & Integrations
Integrate WhatSMS into your stack with the official n8n node or directly via HTTP in any language.
n8n node
Native n8n node with webhook trigger and message-sending actions. Available on the n8n marketplace.
Coming soonHTTP / REST
Standard REST API compatible with any language — JavaScript, Python, PHP, Go, etc. Use the interactive Swagger to explore and test every endpoint.
Open Swagger →Example — send a message with fetch
const res = await fetch("https://api.whatsms.pt/api/v2/tickets/{ticketId}/messages", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_xxxxxxxxxxxx",
"Content-Type": "application/json"
},
body: JSON.stringify({ body: "Olá! Como posso ajudar?" })
});
const { message } = await res.json();
console.log(message.id);Full documentation and test every endpoint in the interactive Swagger.
⚡ Open Swagger