WhatSMS/API Docs
Swagger →Start free
IntroductionAuthenticationErrorsTickets↳ MessagesContactsCampaignsAI AgentsWebhooksRate LimitsSDKs & n8n
⚡ Interactive Swagger

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.

v2

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" }
CodeMeaning
200Success
201Created successfully
400Bad request — missing or invalid parameters
401Not authenticated — invalid or missing API Key
403No permission for this resource
404Resource not found
429Rate limit reached — wait before retrying
500Internal server error

Tickets

A ticket represents a conversation with a contact. It groups messages, channel, status and the assigned agent.

GET/api/v2/ticketsList tickets

Parameters

status·string

Filter by status: open, pending, closed

contactId·uuid

Filter by contact

pageNumber·integer

Page (default: 1)

pageSize·integer

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
}
GET/api/v2/tickets/:ticketIdGet ticket by ID

Parameters

ticketId·uuidrequired

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"
  }
}
POST/api/v2/ticketsCreate ticket

Request 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"
  }
}
PUT/api/v2/tickets/:ticketIdUpdate ticket

Parameters

ticketId·uuidrequired

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.

GET/api/v2/tickets/:ticketId/messagesList ticket messages

Parameters

ticketId·uuidrequired

Ticket ID

pageNumber·integer

Page (default: 1)

pageSize·integer

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
}
POST/api/v2/tickets/:ticketId/messages60 req/min por API KeySend message

Parameters

ticketId·uuidrequired

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.

GET/api/v2/contactsList contacts

Parameters

searchParam·string

Search by name, number or email

pageNumber·integer

Page (default: 1)

pageSize·integer

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
}
GET/api/v2/contacts/:contactIdGet contact by ID

Parameters

contactId·uuidrequired

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"
  }
}
POST/api/v2/contactsCreate contact

Request 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"
  }
}
PUT/api/v2/contacts/:contactIdUpdate contact

Parameters

contactId·uuidrequired

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.

⚠️ Campaigns are subject to the WhatsApp Business acceptable use policies. Unauthorised bulk sending may result in the number being blocked.
GET/api/v2/campaignsList campaigns

Parameters

status·string

pending | running | finished | cancelled

pageNumber·integer

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
}
POST/api/v2/campaignsCreate campaign

Request 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.

GET/api/v2/ai-agentsList AI agents

No 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"
    }
  ]
}
POST/api/v2/ai-agents/assignAssign AI agent to a ticket

Request 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

EventDescription
message.receivedNew message received from a contact
message.sentMessage sent successfully
message.ackDelivery/read receipt (ack 1–5)
ticket.createdNew ticket created
ticket.updatedTicket updated (status, agent, queue)
ticket.closedTicket closed
contact.createdNew contact created
contact.updatedContact updated
campaign.finishedCampaign 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.

EndpointLimit
All v2 endpoints300 req/min por API Key
POST /tickets/:id/messages60 req/min por API Key
POST /campaigns10 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 soon
🔌

HTTP / 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