MozeSMS
API Reference / Documentação
API Reference

MozeSMS API

Integre envio de SMS e OTP nas suas aplicações. Suporte a envio simples, envio em massa até 10 000 destinatários e verificação por código único.

Autenticação

A API MozeSMS suporta dois métodos de autenticação. O método recomendado para integrações de backend é API Key + API Secret. O Bearer Token JWT é usado para sessões de utilizador autenticado.

https://api.mozesms.com
RECOMENDADO Método 1 — API Key + API Secret

Ideal para integrações servidor-servidor, automações e sistemas backend. Obtenha as credenciais em my.mozesms.com → Gestão de APIs.

Headers obrigatórios
X-API-Key: mk_xxxxxxxxxxxxxxxxxxxxxxxx
X-API-Secret: sk_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Método 2 — Bearer Token (JWT de sessão)

Usado no painel e em frontends autenticados. Faça login em /auth/login para obter o token e inclua-o em cada pedido.

Header obrigatório
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Comparação de métodos
Método Quando usar Headers
API Key/Secret Backend, automações, integrações servidor-servidor X-API-Key + X-API-Secret
Bearer JWT Painel, frontend autenticado, sessão de utilizador Authorization: Bearer ...
Onde obter as credenciais

Aceda a my.mozesms.com, navegue para Gestão de APIs e gere o par API Key + Secret. Guarde-os em segurança — não serão exibidos novamente.

Segurança

Nunca exponha as credenciais em código client-side. Faça os pedidos à API sempre a partir do servidor. Revogue credenciais comprometidas imediatamente no painel.

Erros & Limites

A API usa códigos HTTP convencionais. Erros devolvem sempre um objeto JSON com success: false e uma mensagem descritiva no campo error.

CódigoSignificado
200Operação realizada com sucesso.
400Parâmetros inválidos ou em falta. Verifique o corpo do pedido.
401Token inválido ou ausente. Verifique o header Authorization.
402Créditos insuficientes. Recarregue o saldo da conta.
404Recurso não encontrado.
429Rate limit excedido. Aguarde antes de repetir o pedido.
500Erro interno do servidor. Contacte o suporte se persistir.
Rate limits

100 pedidos/min por token. Envio bulk: 1 000 SMS/min. Máx. 10 000 destinatários por pedido bulk.

Formato de erro

Todos os erros seguem sempre a mesma estrutura: success: false, error com mensagem legível e, quando aplicável, details com campos inválidos.

Codificação de caracteres

A codificação usada na mensagem determina quantos caracteres cabem num segmento. A API detecta automaticamente a codificação com base no conteúdo do campo message.

Codificação Quando se aplica 1.º segmento Segmentos seguintes Exemplos de caracteres
GSM-7 Mensagens só com caracteres latinos básicos 160 chars 153 chars A–Z, a–z, 0–9, espaço, pontuação comum, @ £ $ ¥ é ù ì ò ç Ø ø Å å
Unicode Mensagens com emojis, acentos especiais ou caracteres não-latinos 70 chars 67 chars Emojis 😊, árabe, chinês, ã õ â ê ô e outros fora do GSM-7
Atenção: Caracteres como ã, â, ê, ô e õ não fazem parte do alfabeto GSM-7 e forçam a mensagem para codificação Unicode, reduzindo o limite por segmento para 70 caracteres. Use variantes sem acento ou verifique sempre o comprimento antes de enviar.
Segmentos & Cobrança

Um segmento é a unidade mínima de faturação. Cada segmento é cobrado como 1 SMS. Mensagens mais longas são divididas automaticamente em múltiplos segmentos e cobradas proporcionalmente.

Comprimento da mensagem Codificação Segmentos SMS cobrados Exemplo
1 – 160 chars GSM-7 1 1 "Olá! A sua encomenda chegou." (29 chars)
161 – 306 chars GSM-7 2 2 Mensagem longa de 200 chars sem caracteres especiais
1 – 70 chars Unicode 1 1 "Bem-vindo! 😊 A sua conta foi criada." (37 chars)
71 – 134 chars Unicode 2 2 Mensagem com emoji acima de 70 chars
Cálculo do custo por mensagem

O campo segments na resposta indica quantos segmentos foram gerados. O campo cost reflecte o custo total em MZN: custo = segmentos × preço por SMS do plano.

Como minimizar segmentos

Use apenas caracteres GSM-7 (evite emojis e acentos como ã/â/ê/ô). Mantenha mensagens abaixo de 160 chars. Em envios bulk, prefira mensagens curtas para reduzir custos totais.

SMS
Envie mensagens de texto simples ou em massa para qualquer número em Moçambique e no mundo.
SMS/Enviar SMS
POST/sms/send
Enviar SMS
Envia uma mensagem SMS para um único número de destino. O número deve estar no formato internacional com código de país.
Body parameters
CampoTipoDescrição
phoneobrigatório string Número de destino.
Ex: 258841234567
messageobrigatório string Texto da mensagem. Máx. 160 chars por SMS.
sender_idopcional string Nome do remetente (Sender ID). Deve estar aprovado.
Default: MozeSMS
Respostas
200SMS enviado com sucesso. Retorna ID e custo da mensagem.
400Parâmetros inválidos ou em falta.
401Token inválido ou ausente.
402Créditos insuficientes.
429Rate limit excedido.
curl -X POST https://api.mozesms.com/sms/send \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "258841234567",
    "message": "Olá, bem-vindo ao MozeSMS!",
    "sender_id": "MozeSMS"
  }'
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/sms/send',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'phone'     => '258841234567',
    'message'   => 'Olá, bem-vindo ao MozeSMS!',
    'sender_id' => 'MozeSMS',
  ]),
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/sms/send', {
  method: 'POST',
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
    'Content-Type'  : 'application/json',
  },
  body: JSON.stringify({
    phone:     '258841234567',
    message:   'Olá, bem-vindo ao MozeSMS!',
    sender_id: 'MozeSMS',
  }),
});
const data = await res.json();
console.log(data);
import requests

res = requests.post(
    'https://api.mozesms.com/sms/send',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
    json={
        'phone'     : '258841234567',
        'message'   : 'Olá, bem-vindo ao MozeSMS!',
        'sender_id' : 'MozeSMS',
    },
)
print(res.json())
require 'net/http'
require 'json'

uri = URI('https://api.mozesms.com/sms/send')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'
req['Content-Type'] = 'application/json'
req.body = {
  'phone'     => '258841234567',
  'message'   => 'Olá, bem-vindo ao MozeSMS!',
  'sender_id' => 'MozeSMS',
}.to_json

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var payload = new { phone = "258841234567", message = "Olá, bem-vindo ao MozeSMS!", sender_id = "MozeSMS" };
var response = await client.PostAsJsonAsync("https://api.mozesms.com/sms/send", payload);
import java.net.http.*; import java.net.URI;

var body = HttpRequest.BodyPublishers.ofString("{\"phone\":\"258841234567\",\"message\":\"Olá, bem-vindo ao MozeSMS!\",\"sender_id\":\"MozeSMS\"}");
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/sms/send"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .header("Content-Type", "application/json")
    .POST(body).build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("bytes"; "encoding/json"; "net/http")

payload, _ := json.Marshal(map[string]string{"phone": "258841234567", "message": "Olá, bem-vindo ao MozeSMS!", "sender_id": "MozeSMS"}
req, _ := http.NewRequest("POST", "https://api.mozesms.com/sms/send", bytes.NewBuffer(payload))
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
{
  "success": true,
  "data": {
    "id":                "MTIzfDE3MDAwMDAwMDB8Nw==",
    "phone":             "258841234567",
    "status":           "enviado",
    "parts":             1,
    "cost":             1.35,
    "remaining_balance": 44.15,
    "gateway_response": "Message queued for delivery"
  }
}
SMS/Envio em massa
POST/sms/bulk
Envio em massa
Envia mensagens para múltiplos destinatários num único pedido. Cada entrada pode ter um texto diferente. Máximo 1 000 mensagens por pedido.
Body parameters
CampoTipoDescrição
messagesobrigatório object[] Array de mensagens.
Cada item: phone (número) e message (mensagem)
sender_idopcional string Sender ID do remetente. Deve estar aprovado.
Respostas
200Envio em massa iniciado com sucesso.
400Array messages vazio, limite excedido ou parâmetros inválidos.
401Token inválido ou ausente.
402Créditos insuficientes para o volume pretendido.
curl -X POST "https://api.mozesms.com/sms/bulk" \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "sender_id": "MozeSMS",
    "messages": [
      {"phone": "258840000000", "message": "Mensagem 1"},
      {"phone": "258850000000", "message": "Mensagem 2"}
    ]
  }'
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/sms/bulk',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'sender_id' => 'MozeSMS',
    'messages'  => [
        ['phone' => '258840000000', 'message' => 'Olá, João!'],
        ['phone' => '258850000000', 'message' => 'Olá, Maria!'],
    ],
  ]),
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/sms/bulk', {
  method: 'POST',
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
    'Content-Type'  : 'application/json',
  },
  body: JSON.stringify({
    sender_id: 'MozeSMS',
    messages: [
      { phone: '258840000000', message: 'Olá, João!' },
      { phone: '258850000000', message: 'Olá, Maria!' },
    ],
  }),
});
const data = await res.json();
console.log(data);
import requests

res = requests.post(
    'https://api.mozesms.com/sms/bulk',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
    json={
        'sender_id' : 'MozeSMS',
        'messages'  : [
            {'phone': '258840000000', 'message': 'Olá, João!'},
            {'phone': '258850000000', 'message': 'Olá, Maria!'},
        ],
    },
)
print(res.json())
require 'net/http'
require 'json'

uri = URI('https://api.mozesms.com/sms/bulk')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'
req['Content-Type'] = 'application/json'
req.body = {
  'sender_id' => 'MozeSMS',
  'messages'  => [
    { 'phone' => '258840000000', 'message' => 'Olá, João!' },
    { 'phone' => '258850000000', 'message' => 'Olá, Maria!' },
  ],
}.to_json

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var payload = new { sender_id = "MozeSMS", messages = new[] {
    new { phone = "258840000000", message = "Msg 1" },
    new { phone = "258850000000", message = "Msg 2" }
}};
var response = await client.PostAsJsonAsync("https://api.mozesms.com/sms/bulk", payload);
import java.net.http.*; import java.net.URI;

var body = HttpRequest.BodyPublishers.ofString("{\"sender_id\":\"MozeSMS\",\"messages\":[{\"phone\":\"258840000000\",\"message\":\"Msg 1\"},{\"phone\":\"258850000000\",\"message\":\"Msg 2\"}]}");
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/sms/bulk"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .header("Content-Type", "application/json")
    .POST(body).build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("bytes"; "encoding/json"; "net/http")

payload, _ := json.Marshal(map[string]interface{}{"sender_id": "MozeSMS", "messages": []map[string]string{{"phone": "258840000000", "message": "Msg 1"},{"phone": "258850000000", "message": "Msg 2"}})
req, _ := http.NewRequest("POST", "https://api.mozesms.com/sms/bulk", bytes.NewBuffer(payload))
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
{
  "success": true,
  "data": {
    "batch_id":          "BATCH_6789abcd",
    "total":             2,
    "sent":              2,
    "failed":            0,
    "queued":            0,
    "cost":             2.70,
    "remaining_balance": 95.50
  }
}
SMS/Estado da mensagem
GET/sms/status/{id}
Estado da mensagem
Consulta o estado de entrega de uma mensagem SMS pelo seu identificador único.
Path parameters
ParâmetroTipoDescrição
idobrigatório string ID único da mensagem, retornado no envio.
Ex: MTIzfDE3MDAwMDAwMDB8Nw==
Estados possíveis
ValorSignificado
queuedNa fila de envio
sentEnviada para a operadora
deliveredEntregue ao destinatário
failedFalhou no envio
expiredExpirou sem entrega
Respostas
200Estado actual da mensagem.
404Mensagem não encontrada com esse ID.
curl -X GET "https://api.mozesms.com/sms/status/MSG123456" \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET"
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
  ],
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D', {
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
  },
});
const data = await res.json();
console.log(data);
import requests

res = requests.get(
    'https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
)
print(res.json())
require 'net/http'

uri = URI('https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var response = await client.GetAsync("https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D");
var data = await response.Content.ReadFromJsonAsync<dynamic>();
import java.net.http.*; import java.net.URI;

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .GET().build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("io"; "net/http")

req, _ := http.NewRequest("GET", "https://api.mozesms.com/sms/status/MTIzfDE3MDAwMDAwMDB8Nw%3D%3D", nil)
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
{
  "success": true,
  "data": {
    "id":           "MTIzfDE3MDAwMDAwMDB8Nw==",
    "phone":        "258841234567",
    "message":      "Olá, a sua encomenda foi enviada.",
    "status":       "entregue",
    "parts":        1,
    "cost":         1.35,
    "created_at":   "2025-01-15 10:30:00",
    "sent_at":      "2025-01-15 10:30:01"
  }
}
SMS/Histórico
GET/sms/history
Histórico de mensagens
Retorna a lista paginada das mensagens enviadas pela conta. Suporta filtros por estado, intervalo de datas e paginação.
Query parameters
ParâmetroTipoDescrição
limitopcional integer Resultados por página. Default: 50. Máx: 100.
pageopcional integer Página actual. Default: 1.
statusopcional string Filtrar por estado: sent · delivered · failed
start_dateopcional date Data inicial (YYYY-MM-DD).
end_dateopcional date Data final (YYYY-MM-DD).
Respostas
200Lista paginada de mensagens com totais.
401Token inválido ou ausente.
curl 'https://api.mozesms.com/sms/history?limit=50&page=1' \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET"
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/sms/history?limit=50&page=1',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
  ],
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/sms/history?limit=50&page=1', {
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
  },
});
const data = await res.json();
console.log(data);
import requests

res = requests.get(
    'https://api.mozesms.com/sms/history?limit=50&page=1',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
)
print(res.json())
require 'net/http'

uri = URI('https://api.mozesms.com/sms/history?limit=50&page=1')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var response = await client.GetAsync("https://api.mozesms.com/sms/history?limit=50&page=1");
var data = await response.Content.ReadFromJsonAsync<dynamic>();
import java.net.http.*; import java.net.URI;

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/sms/history?limit=50&page=1&status=enviado"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .GET().build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("io"; "net/http")

req, _ := http.NewRequest("GET", "https://api.mozesms.com/sms/history?limit=50&page=1", nil)
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
{
  "success": true,
  "data": {
    "items": [
      {
        "id":         "MTIzfDE3MDAwMDAwMDB8Nw==",
        "phone":      "258841234567",
        "message":    "Olá, bem-vindo!",
        "status":     "enviado",
        "parts":      1,
        "cost":       1.35,
        "created_at": "2025-01-15 10:30:00"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page":     50,
      "total":        142,
      "last_page":    3
    }
  }
}
SMS/Webhook e estados
DOC/sms/webhook
Guia de Webhook SMS
Use o webhook para receber o estado final de entrega. O envio em /sms/send retorna o ID inicial da mensagem, e o webhook confirma entrega, falha, expiração ou rejeição.
Eventos principais
sms.sent sms.delivered sms.failed sms.rejected sms.expired sms.undelivered sms.undeliverable sms.cancelled sms.unknown
Campos de correlação
data.message_id Mesmo ID público retornado no /sms/send. Use para reconciliar envio e webhook. data.external_id Identificador técnico do gateway para suporte e troubleshooting.
Mapeamento DLR para estado interno
gateway_statusEstado interno
DELIVEREDentregue
UNDELIVEREDfalhado
FAILEDfalhado
EXPIREDfalhado
DELETEDcancelado
UNDELIVERABLEfalhado
ACCEPTEDprocessando
UNKNOWNpendente
REJECTEDfalhado
Boas práticas
1Trate o webhook como fonte de verdade para estado final (entregue/falhado).
2Processe de forma idempotente usando data.message_id.
3Responda HTTP 200 rapidamente para evitar reentregas desnecessárias.
4Guarde também external_id para auditoria e troubleshooting.
{
  "event": "sms.delivered",
  "timestamp": "2026-04-23T12:34:35+02:00",
  "webhook_id": "659779de-3ec2-11f1-957c-0050565bada5",
  "data": {
    "message_id": "MjA1NzcxfDE3NzY5NDEzODZ8Mjc2Mw==",
    "external_id": "SMS_205771_1776941386",
    "phone": "258878881519",
    "sender_id": "MOZESMS",
    "message": "Texto enviado",
    "status": "entregue",
    "gateway_status": "DELIVERED",
    "error_code": "",
    "delivery_time": "2026-04-23 12:33:41",
    "event_time": "2026-04-23 12:34:35",
    "delivered_to_device": true
  }
}
// Endpoint do seu sistema (cliente) para receber callbacks
$payload = json_decode(file_get_contents('php://input'), true);

$event = $payload['event'] ?? '';
$messageId = $payload['data']['message_id'] ?? '';
$externalId = $payload['data']['external_id'] ?? '';
$status = $payload['data']['status'] ?? '';

// 1) Encontrar registo interno por message_id
// 2) Aplicar atualização idempotente de estado
// 3) Guardar external_id para troubleshooting

http_response_code(200);
echo 'ok';
# Correlação recomendada
1) No /sms/send, guardar data.id
2) No webhook, comparar data.message_id com o id guardado
3) Usar data.external_id para suporte com gateway

# Consulta de status
GET /sms/status/{id}
GET /sms/status/{id}/gateway

# Nota sobre sms_uuid
No schema atual, sms_uuid pode vir null.
Use o id público (base64) como identificador principal.
OTP — One-Time Password
Gere, envie e valide códigos de verificação via SMS para autenticação de dois factores.
OTP/Enviar OTP
POST/otp/send
Enviar OTP
Gera e envia um código OTP (One-Time Password) via SMS para o número especificado. O código expira automaticamente após o tempo configurado.
Body parameters
CampoTipoDescrição
phoneobrigatório string Número de destino.
Ex: 841234567
channelopcional string Canal de envio: sms, email, whatsapp.
Default: sms
digitsopcional integer Comprimento do código. Entre 4 e 8 dígitos.
Default: 6
expiryopcional integer Tempo de validade em segundos. Máx. 3600.
Default: 300 (5 min)
sender_idopcional string Nome do remetente. Máx. 11 caracteres.
Respostas
201OTP enviado. Retorna session_id e data de expiração.
400Parâmetros inválidos ou em falta.
401Token inválido ou ausente.
402Créditos insuficientes.
429Rate limit excedido.
curl -X POST "https://api.mozesms.com/otp/send" \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "841234567",
    "digits": 6,
    "expiry": 300
  }'
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/otp/send',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'phone'  => '841234567',
    'digits' => 6,
    'expiry' => 300,
  ]),
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/otp/send', {
  method: 'POST',
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
    'Content-Type'  : 'application/json',
  },
  body: JSON.stringify({
    phone:  '841234567',
    digits: 6,
    expiry: 300,
  }),
});
const data = await res.json();
console.log(data);
import requests

res = requests.post(
    'https://api.mozesms.com/otp/send',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
    json={
        'phone'  : '841234567',
        'digits' : 6,
        'expiry' : 300,
    },
)
print(res.json())
require 'net/http'
require 'json'

uri = URI('https://api.mozesms.com/otp/send')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'
req['Content-Type'] = 'application/json'
req.body = {
  'phone'  => '841234567',
  'digits' => 6,
  'expiry' => 300,
}.to_json

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var payload = new { phone = "841234567", digits = 6, expiry = 300 };
var response = await client.PostAsJsonAsync("https://api.mozesms.com/otp/send", payload);
import java.net.http.*; import java.net.URI;

var body = HttpRequest.BodyPublishers.ofString("{\"phone\":\"841234567\",\"digits\":6,\"expiry\":300}");
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/otp/send"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .header("Content-Type", "application/json")
    .POST(body).build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("bytes"; "encoding/json"; "net/http")

payload, _ := json.Marshal(map[string]interface{}{"phone": "841234567", "digits": 6, "expiry": 300})
req, _ := http.NewRequest("POST", "https://api.mozesms.com/otp/send", bytes.NewBuffer(payload))
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
{
  "success": true,
  "message": "OTP code sent successfully",
  "data": {
    "session_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "phone":      "+258841234567",
    "channel":    "sms",
    "expires_at": "2026-04-07 12:30:00",
    "expires_in": 300
  }
}
OTP/Verificar OTP
POST/otp/verify
Verificar OTP
Valida o código OTP introduzido pelo utilizador. Cada código só pode ser verificado uma vez. Códigos expirados ou inválidos retornam erro.
Body parameters
CampoTipoDescrição
session_idobrigatório string ID de sessão retornado no envio do OTP.
codeobrigatório string Código OTP introduzido pelo utilizador.
Ex: "384729"
Respostas
200Código verificado com sucesso.
400Parâmetros inválidos ou em falta.
401Token inválido ou ausente.
422Código errado. Inclui tentativas restantes.
curl -X POST "https://api.mozesms.com/otp/verify" \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "code": "384729"}'
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/otp/verify',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'session_id' => 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6',
    'code'       => '384729',
  ]),
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/otp/verify', {
  method: 'POST',
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
    'Content-Type'  : 'application/json',
  },
  body: JSON.stringify({
    session_id: 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6',
    code:       '384729',
  }),
});
const data = await res.json();
console.log(data);
import requests

res = requests.post(
    'https://api.mozesms.com/otp/verify',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
    json={
        'session_id' : 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6',
        'code'       : '384729',
    },
)
print(res.json())
require 'net/http'
require 'json'

uri = URI('https://api.mozesms.com/otp/verify')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'
req['Content-Type'] = 'application/json'
req.body = {
  'session_id' => 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6',
  'code'       => '384729',
}.to_json

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var payload = new { session_id = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", code = "384729" };
var response = await client.PostAsJsonAsync("https://api.mozesms.com/otp/verify", payload);
import java.net.http.*; import java.net.URI;

var body = HttpRequest.BodyPublishers.ofString("{\"session_id\":\"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6\",\"code\":\"384729\"}");
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/otp/verify"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .header("Content-Type", "application/json")
    .POST(body).build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("bytes"; "encoding/json"; "net/http")

payload, _ := json.Marshal(map[string]string{"session_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "code": "384729"})
req, _ := http.NewRequest("POST", "https://api.mozesms.com/otp/verify", bytes.NewBuffer(payload))
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
{
  "success": true,
  "message": "Code verified successfully",
  "verified_at": "2026-04-07 12:27:41",
  "ref_id": "order_123"
}
{
  "success": false,
  "error": "WRONG_CODE",
  "message": "Invalid code. Remaining attempts: 2",
  "remaining_attempts": 2
}
OTP/Reenviar OTP
POST/otp/resend
Reenviar OTP
Gera e reenvia um novo código OTP para o número especificado, invalidando imediatamente qualquer código anterior ainda activo.
Body parameters
CampoTipoDescrição
phoneobrigatório string Número que irá receber o novo código.
Respostas
200Novo OTP enviado. Código anterior invalidado.
400Parâmetro phone ausente ou inválido.
401Token inválido ou ausente.
402Créditos insuficientes.
429Rate limit excedido.
curl -X POST "https://api.mozesms.com/otp/resend" \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"phone": "841234567"}'
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/otp/resend',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS     => json_encode([
    'phone' => '841234567',
  ]),
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/otp/resend', {
  method: 'POST',
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
    'Content-Type'  : 'application/json',
  },
  body: JSON.stringify({
    phone: '841234567',
  }),
});
const data = await res.json();
console.log(data);
import requests

res = requests.post(
    'https://api.mozesms.com/otp/resend',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
    json={
        'phone' : '841234567',
    },
)
print(res.json())
require 'net/http'
require 'json'

uri = URI('https://api.mozesms.com/otp/resend')
req = Net::HTTP::Post.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'
req['Content-Type'] = 'application/json'
req.body = {
  'phone' => '841234567',
}.to_json

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var payload = new { phone = "841234567" };
var response = await client.PostAsJsonAsync("https://api.mozesms.com/otp/resend", payload);
import java.net.http.*; import java.net.URI;

var body = HttpRequest.BodyPublishers.ofString("{\"phone\":\"841234567\"}");
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/otp/resend"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .header("Content-Type", "application/json")
    .POST(body).build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("bytes"; "encoding/json"; "net/http")

payload, _ := json.Marshal(map[string]string{"phone": "841234567"})
req, _ := http.NewRequest("POST", "https://api.mozesms.com/otp/resend", bytes.NewBuffer(payload))
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
req.Header.Set("Content-Type", "application/json")
resp, _ := (&http.Client{}).Do(req)
{
  "success": true,
  "message": "OTP code sent successfully",
  "data": {
    "session_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
    "phone":      "+258841234567",
    "channel":    "sms",
    "expires_at": "2026-04-07 12:30:00",
    "expires_in": 300
  }
}
Conta/Consultar saldo
GET/account/balance
Consultar saldo
Retorna o saldo disponível em MZN, a moeda e o plano activo da conta.

Este endpoint não requer parâmetros. Apenas o token Bearer no header Authorization.

Respostas
200Saldo actual, moeda e plano da conta.
401Token inválido ou ausente.
curl -X GET "https://api.mozesms.com/account/balance" \
  -H "X-API-Key: SUA_API_KEY" \
  -H "X-API-Secret: SUA_API_SECRET"
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => 'https://api.mozesms.com/account/balance',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => [
    'X-API-Key: SUA_API_KEY',
    'X-API-Secret: SUA_API_SECRET',
  ],
]);
$res = curl_exec($ch);
curl_close($ch);
echo $res;
const res = await fetch('https://api.mozesms.com/account/balance', {
  headers: {
    'X-API-Key'    : 'SUA_API_KEY',
    'X-API-Secret' : 'SUA_API_SECRET',
  },
});
const data = await res.json();
console.log(data);
import requests

res = requests.get(
    'https://api.mozesms.com/account/balance',
    headers={
        'X-API-Key'    : 'SUA_API_KEY',
        'X-API-Secret': 'SUA_API_SECRET',
    },
)
print(res.json())
require 'net/http'

uri = URI('https://api.mozesms.com/account/balance')
req = Net::HTTP::Get.new(uri)
req['X-API-Key'   ] = 'SUA_API_KEY'
req['X-API-Secret'] = 'SUA_API_SECRET'

puts Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }.body
using System.Net.Http;
using System.Net.Http.Json;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "SUA_API_KEY");
client.DefaultRequestHeaders.Add("X-API-Secret", "SUA_API_SECRET");

var response = await client.GetAsync("https://api.mozesms.com/account/balance");
var data = await response.Content.ReadFromJsonAsync<dynamic>();
import java.net.http.*; import java.net.URI;

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.mozesms.com/account/balance"))
    .header("X-API-Key", "SUA_API_KEY")
    .header("X-API-Secret", "SUA_API_SECRET")
    .GET().build();
var resp = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
package main

import ("io"; "net/http")

req, _ := http.NewRequest("GET", "https://api.mozesms.com/account/balance", nil)
req.Header.Set("X-API-Key", "SUA_API_KEY")
req.Header.Set("X-API-Secret", "SUA_API_SECRET")
resp, _ := (&http.Client{}).Do(req)
defer resp.Body.Close()
{
  "success": true,
  "data": {
    "balance":              45.50,
    "currency":             "MZN",
    "sms_credits":          33,
    "unit_price_mzn":       1.35,
    "estimated_sms_remaining": 33,
    "plan": {
      "name":                "Business",
      "price_per_sms":       1.20,
      "discount_percentage": 11.1
    }
  }
}