Documentação da API
Acesso programático a números virtuais, pedidos e saldo da conta.
Visao Geral
Todos os campos monetários na API /v1 estão em IDR (Rupia indonésia), como unidades inteiras — por exemplo, "price": 15000 e "balance": 500000 significam Rp 15.000 e Rp 500.000. Para uma projeção nativa em USD do mesmo livro-razão, mude para a API v2 usando o seletor de versão acima.
⟩Autenticação
Todas as requisições da API exigem um Bearer token. Gere um nas Configurações da Conta no painel, e inclua-o em cada requisição:
Requisições sem um token válido recebem uma resposta 401 UNAUTHORIZED.
⟩URL Base
Todos os caminhos de endpoints abaixo são relativos a:
⟩Formato de Resposta
Toda resposta retorna JSON com um envelope consistente. Todas as respostas incluem um cabeçalho x-request-id para depuração.
{
"success": true,
"data": { ... }
} {
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
} Todos os campos monetários na API /v1 estão em IDR (Rupia indonésia), como unidades inteiras — por exemplo, "price": 15000 e "balance": 500000 significam Rp 15.000 e Rp 500.000. Para uma projeção nativa em USD do mesmo livro-razão, mude para a API v2 usando o seletor de versão acima.
/catalog/countries Retorna uma lista de todos os países disponíveis.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v1/catalog/countries \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/catalog/countries", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/countries",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 6,
"code": "ID",
"name": "Indonesia",
"dial_code": "+62",
"emoji": "🇮🇩",
"active": true
}
]
} /catalog/services Retorna uma lista de serviços (plataformas) disponíveis. Opcionalmente filtre por país.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
country_id | integer | Não | Filtrar serviços disponíveis para este país |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v1/catalog/services?country_id=6" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/catalog/services?country_id=6", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/services",
params={"country_id": 6},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 3,
"code": "wa",
"name": "WhatsApp",
"active": true
}
]
} /catalog/products Retorna uma lista paginada de produtos disponíveis. Filtre por país e/ou plataforma.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
country_id | integer | Não | Filtrar por ID do país |
platform_id | integer | Não | Filtrar por ID da plataforma/serviço |
sort | string | Não | Ordenação: price_asc (padrão), price_desc, available_asc, available_desc, name_asc, name_desc |
limit | integer | Não | Resultados por página (1-10.000, padrão 1.000) |
page | integer | Não | Número da página (mín. 1, padrão 1) |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v1/catalog/products?country_id=6&platform_id=3&limit=10&page=1" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
country_id: "6", platform_id: "3", limit: "10", page: "1",
});
const res = await fetch(`https://api.smscode.gg/v1/catalog/products?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/products",
params={"country_id": 6, "platform_id": 3, "limit": 10, "page": 1},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": 15000,
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1 }
} /catalog/exchange-rate Retorna a taxa de câmbio USD/IDR atual usada para conversão de moeda.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
pair | string | Não | Par de moedas (padrão: USD/IDR) |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v1/catalog/exchange-rate?pair=USD/IDR" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/catalog/exchange-rate?pair=USD/IDR", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/exchange-rate",
params={"pair": "USD/IDR"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"pair": "USD/IDR",
"base_currency": "USD",
"quote_currency": "IDR",
"rate": 16250
}
} /balance Retorna o saldo da conta do usuário autenticado.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v1/balance \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/balance", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/balance",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"currency": "IDR",
"balance": 500000
}
} /orders Retorna uma lista dos pedidos do usuário autenticado, ordenados do mais recente. Suporta filtragem por status e paginação via offset.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
limit | integer | Não | Máximo de resultados (1-100, padrão 20) |
offset | integer | Não | Número de resultados a pular (padrão 0) |
status | string | Não | Filtrar por status: ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (sem distinção de maiúsculas) |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v1/orders?limit=5&status=ACTIVE" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
limit: "5", status: "ACTIVE", offset: "0",
});
const res = await fetch(`https://api.smscode.gg/v1/orders?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/orders",
params={"limit": 5, "status": "ACTIVE", "offset": 0},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 1001,
"status": "ACTIVE",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": 15000,
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
}
]
} /orders/{id} Retorna um único pedido por ID. Retorna apenas pedidos do usuário autenticado.
Parâmetros de Caminho
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido (parâmetro de caminho) |
Exemplo de Requisição
curl -s https://api.smscode.gg/v1/orders/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/orders/1001", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/orders/1001",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"id": 1001,
"status": "OTP_RECEIVED",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": 15000,
"otp_code": "123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
}
} /orders/active Lista todos os pedidos ativos no momento (ACTIVE + OTP_RECEIVED). Use para verificar atualizações de status de OTP.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v1/orders/active \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/orders/active", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/orders/active",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 1001,
"status": "OTP_RECEIVED",
"otp_code": "123456",
"otp_message": "Your verification code is 123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"failed_reason": null
},
{
"id": 1002,
"status": "ACTIVE",
"otp_code": null,
"otp_message": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
]
} /orders/create Cria um novo pedido de número virtual. Debita o saldo automaticamente. Suporta um cabeçalho Idempotency-Key opcional para evitar pedidos duplicados em retentativas de rede.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
product_id | integer | Não | ID do produto a ser pedido. Informe APENAS UM: product_id OU catalog_product_id, não ambos. |
catalog_product_id | integer | Não | ID de produto estável — o servidor escolhe a melhor oferta atual (mais barata, considerando a confiabilidade). Informe este ou product_id. |
max_price | integer · string | Não | Limite de preço opcional. v1: inteiro em IDR. v2: string decimal em USD (ex.: "0.50"). |
prefer_provider | string | Não | Código de provedor opcional a preferir quando as ofertas empatam. |
policy | string | Não | Política de roteamento opcional, válida apenas com catalog_product_id. Valores: cheapest (padrão) escolhe a oferta saudável mais barata; best_success ordena as ofertas pelo sucesso de entrega recente primeiro. O best_success pontua cada provedor pela proporção de pedidos que receberam OTP nos últimos 30 dias completos, em faixas de 10%, e só conta um provedor depois que ele tem pelo menos 20 pedidos nesse período — provedores abaixo desse limite ou sem histórico são tratados como neutros, de modo que ofertas novas nunca ficam sem chance (opcional; o sinal começa neutro). Quando prefer_provider também é informado, o provedor preferido ainda fica em primeiro lugar. |
quantity | integer | Não | Quantidade de itens (1-100, padrão 1) |
Envie um cabeçalho Idempotency-Key para repetir solicitações com segurança sem criar pedidos duplicados. A chave pode conter letras, dígitos, hífen e sublinhado (A-Z a-z 0-9 _ -), até 128 caracteres; uma chave inválida é rejeitada com 422 VALIDATION_ERROR. Repetir com a mesma chave e o mesmo corpo reproduz o resultado original (incluindo o failed_count de um sucesso parcial). Uma retentativa que chega ao provedor mas falha é registrada e reproduz esse mesmo erro — use uma chave NOVA para tentar de novo. Falhas sem efeitos colaterais (saldo insuficiente, nenhuma oferta disponível) liberam a chave, então você pode adicionar saldo e repetir com a mesma chave. Reutilizar uma chave com um corpo diferente retorna 422 IDEMPOTENCY_KEY_REUSED, e uma solicitação ainda em andamento com essa chave retorna 409 REQUEST_IN_PROGRESS. O campo failed_reason nas respostas de create é sempre null — ele só é preenchido na consulta/listagem de pedidos.
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v1/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{"product_id":142,"quantity":1}'
# Or order by stable catalog_product_id — the server picks the best current offer
# (cheapest, reliability-aware). Optional max_price caps the price; on v1 it is an
# IDR integer, on v2 it is a USD decimal string (e.g. "0.50"). prefer_provider
# (provider code) is a soft tie-breaker. Pass EITHER product_id OR catalog_product_id.
curl -s -X POST https://api.smscode.gg/v1/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-124" \
-d '{"catalog_product_id":87,"max_price":20000}'const res = await fetch("https://api.smscode.gg/v1/orders/create", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
"Idempotency-Key": "unique-request-id-123",
},
body: JSON.stringify({ product_id: 142, quantity: 1 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/create",
json={"product_id": 142, "quantity": 1},
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Idempotency-Key": "unique-request-id-123",
})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"orders": [
{
"id": 1002,
"status": "ACTIVE",
"product_id": 142,
"catalog_product_id": 87,
"amount": 15000,
"phone_number": "+6281234567891",
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
],
"failed_count": 0
}
} /orders/cancel Cancela um pedido ativo. O custo do aluguel é reembolsado ao saldo da sua conta.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido a cancelar |
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v1/orders/cancel \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v1/orders/cancel", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/cancel",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": 15000,
"new_balance": 515000
}
} /orders/finish Marca um pedido como concluído após receber o OTP. Isso libera o número imediatamente em vez de aguardar a expiração.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido a finalizar |
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v1/orders/finish \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v1/orders/finish", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/finish",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"order_id": 1001,
"status": "COMPLETED"
}
} /orders/resend Solicita que a plataforma reenvie o SMS para o número alugado. Nem todas as plataformas suportam reenvio — verifique o campo resent na resposta.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido para reenviar SMS |
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v1/orders/resend \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v1/orders/resend", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/resend",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"order_id": 1001,
"status": "ACTIVE",
"resent": true
}
} /webhook Retorna a configuração atual de notificação por webhook.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v1/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/webhook", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/webhook",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} /webhook Atualiza a URL e/ou o segredo do webhook. Um segredo é gerado automaticamente quando você define uma URL pela primeira vez. Envie uma string vazia para limpar. A URL deve usar HTTPS.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
webhook_url | string | Não | URL HTTPS para receber eventos de webhook (string vazia para limpar) |
webhook_secret | string | Não | Segredo compartilhado para assinatura HMAC-SHA256 (gerado automaticamente se omitido na primeira configuração) |
Pelo menos um campo é obrigatório.
Exemplo de Requisição
curl -s -X PATCH https://api.smscode.gg/v1/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"webhook_url":"https://example.com/webhook"}'const res = await fetch("https://api.smscode.gg/v1/webhook", {
method: "PATCH",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ webhook_url: "https://example.com/webhook" }),
});
const data = await res.json();import requests
res = requests.patch("https://api.smscode.gg/v1/webhook",
json={"webhook_url": "https://example.com/webhook"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} /webhook/test Envia um evento de teste para a URL de webhook configurada. Retorna o código de status HTTP do seu servidor. Útil para verificar se o seu endpoint está funcionando antes de começar a usar.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v1/webhook/test \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/webhook/test", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/webhook/test",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"status_code": 200
}
} ⟩Notificações por Webhook
Configure uma URL de webhook para receber notificações push em tempo real sobre eventos de pedidos em vez de fazer polling. Esta é a abordagem recomendada para scripts de bot.
Eventos
| Evento | Gatilho |
|---|---|
order.otp_received | Código OTP entregue ao número alugado |
order.completed | Pedido marcado como concluído (manualmente ou por expiração) |
order.expired | Pedido expirado sem OTP (saldo reembolsado) |
order.canceled | Pedido cancelado pelo usuário (saldo reembolsado) |
Payload
{
"event": "order.otp_received",
"timestamp": "2026-02-25T12:00:00+00:00",
"data": {
"order_id": 1001,
"phone_number": "+628123456789",
"otp_code": "1234",
"otp_message": "Your verification code is 1234",
"product_id": 42,
"catalog_product_id": 87,
"country": "Indonesia",
"platform": "WhatsApp"
}
} Verificação de Assinatura
Cada requisição de webhook inclui um cabeçalho X-Webhook-Signature com uma assinatura HMAC-SHA256 do corpo da requisição, usando seu webhook_secret como chave:
Verifique esta assinatura no seu servidor para garantir que a requisição é autêntica. A entrega é do tipo disparar e esquecer, com timeout de 3 segundos e sem retentativas.
⟩Rate Limits
As requisições da API possuem limites por grupo de endpoint. Exceder o limite retorna 429 Too Many Requests com um cabeçalho Retry-After indicando quantos segundos aguardar.
| Grupo de Endpoints | Limite | Janela |
|---|---|---|
| Catálogo (countries, services, products, exchange-rate) | 5.000 requisições | 60 segundos |
| Saldo | 600 requisições | 60 segundos |
| Consulta de pedidos (list, get, active) | 5.000 requisições | 60 segundos |
| Criação de pedido | 3.000 requisições | 60 segundos |
| Cancelamento de pedido | 1.000 requisições | 60 segundos |
| Ações de pedido (finish, resend) | 1.000 requisições | 60 segundos |
| Config de webhook (get, update) | 600 requisições | 60 segundos |
| Teste de webhook | 10 requisições | 60 segundos |
⟩Códigos de Erro
Respostas de erro incluem um destes códigos em error.code:
| Código | HTTP | Descrição |
|---|---|---|
UNAUTHORIZED | 401 | Token de API ausente ou inválido |
FORBIDDEN | 403 | Acesso negado |
NOT_FOUND | 404 | Recurso não encontrado (pedido, taxa de câmbio, etc.) |
CONFLICT | 409 | Requisição duplicada ou conflito de recurso |
INSUFFICIENT_BALANCE | 409 | Saldo insuficiente para criar o pedido |
VALIDATION_ERROR | 422 | Os parâmetros da requisição falharam na validação |
RATE_LIMIT_EXCEEDED | 429 | Muitas requisições (verifique o cabeçalho Retry-After) |
INTERNAL_ERROR | 500 | Erro interno do servidor |
PROVIDER_ERROR | 422 | O provedor de SMS upstream rejeitou a requisição. Em falhas de criação de pedido, o erro pode incluir <code>details</code>: <code>cause_counts</code> (pedidos com <code>product_id</code> legado — uma contagem agrupada por causa) ou <code>attempts</code> (pedidos com <code>catalog_product_id</code> — resultados por tentativa), usando os valores <code>ok</code>, <code>no_numbers</code>, <code>insufficient_balance</code>, <code>price_rejected</code>, <code>provider_unavailable</code>, <code>provider_account_balance</code>, <code>provider_error</code>. |
NO_OFFER_AVAILABLE | 422 | Nenhuma oferta ativa corresponde ao produto e à política solicitados (limite de preço, disponibilidade). |
CANCEL_TOO_EARLY | 409 | Pedido muito recente para cancelar — aguarde 2 minutos |
REQUEST_IN_PROGRESS | 409 | Uma solicitação de criação com esta chave de idempotência ainda está em andamento |
IDEMPOTENCY_KEY_REUSED | 422 | Esta chave de idempotência já foi usada com um corpo de solicitação diferente |
SERVICE_UNAVAILABLE | 503 | Serviço temporariamente indisponível (manutenção) |
Visao Geral
Todos os campos monetários na API /v2 estão em USD, retornados como um objeto monetário — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount é uma string decimal; canonical_amount é o valor exato em IDR do livro-razão (use-o para reconciliação). A rate USD/IDR aplicada é divulgada uma vez por resposta em meta.fx. A v2 é uma projeção em USD em tempo de renderização sobre o mesmo livro-razão em IDR da v1 — ela nunca armazena nem transaciona USD.
⟩Autenticação
Todas as requisições da API exigem um Bearer token. Gere um nas Configurações da Conta no painel, e inclua-o em cada requisição:
Requisições sem um token válido recebem uma resposta 401 UNAUTHORIZED.
⟩URL Base
Todos os caminhos de endpoints abaixo são relativos a:
⟩Formato de Resposta
Toda resposta retorna JSON com um envelope consistente. Todas as respostas incluem um cabeçalho x-request-id para depuração.
{
"success": true,
"data": { ... }
} {
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
} Erro v2: nenhuma taxa de câmbio utilizável (503)
{
"success": false,
"error": { "code": "FX_RATE_UNAVAILABLE", "message": "USD/IDR exchange rate is unavailable" }
} Todos os campos monetários na API /v2 estão em USD, retornados como um objeto monetário — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount é uma string decimal; canonical_amount é o valor exato em IDR do livro-razão (use-o para reconciliação). A rate USD/IDR aplicada é divulgada uma vez por resposta em meta.fx. A v2 é uma projeção em USD em tempo de renderização sobre o mesmo livro-razão em IDR da v1 — ela nunca armazena nem transaciona USD.
/catalog/countries Retorna uma lista de todos os países disponíveis.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v2/catalog/countries \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/catalog/countries", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/countries",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 6,
"code": "ID",
"name": "Indonesia",
"dial_code": "+62",
"emoji": "🇮🇩",
"active": true
}
]
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
/catalog/services Retorna uma lista de serviços (plataformas) disponíveis. Opcionalmente filtre por país.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
country_id | integer | Não | Filtrar serviços disponíveis para este país |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v2/catalog/services?country_id=6" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/catalog/services?country_id=6", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/services",
params={"country_id": 6},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 3,
"code": "wa",
"name": "WhatsApp",
"active": true
}
]
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
/catalog/products Retorna uma lista paginada de produtos disponíveis. Filtre por país e/ou plataforma.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
country_id | integer | Não | Filtrar por ID do país |
platform_id | integer | Não | Filtrar por ID da plataforma/serviço |
sort | string | Não | Ordenação: price_asc (padrão), price_desc, available_asc, available_desc, name_asc, name_desc |
limit | integer | Não | Resultados por página (1-10.000, padrão 1.000) |
page | integer | Não | Número da página (mín. 1, padrão 1) |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v2/catalog/products?country_id=6&platform_id=3&limit=10&page=1" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
country_id: "6", platform_id: "3", limit: "10", page: "1",
});
const res = await fetch(`https://api.smscode.gg/v2/catalog/products?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/products",
params={"country_id": 6, "platform_id": 3, "limit": 10, "page": 1},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1, "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2: os campos monetários são objetos monetários em USD e a resposta carrega um único meta.fx { pair, rate, rate_as_of }. rate é o valor inteiro em IDR por 1 USD, portanto USD = canonical_amount / rate. Os totais usam 2 casas decimais; preços/reembolsos por item usam 4. Um valor estritamente positivo nunca é arredondado para 0.00. rate_as_of é o timestamp RFC3339 da taxa (no formato +00:00) ou null quando nenhum timestamp é registrado.
Apenas v2: se não existir uma taxa USD/IDR utilizável, os endpoints monetários retornam 503 FX_RATE_UNAVAILABLE com um cabeçalho Retry-After em vez de um corpo monetário. A v1 nunca retorna isso.
/catalog/exchange-rate Retorna a taxa de câmbio USD/IDR atual usada para conversão de moeda.
Parâmetros
Nenhum — a v2 sempre retorna USD/IDR; o parâmetro ?pair da v1 é ignorado.
Exemplo de Requisição
curl -s "https://api.smscode.gg/v2/catalog/exchange-rate?pair=USD/IDR" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/catalog/exchange-rate?pair=USD/IDR", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/exchange-rate",
params={"pair": "USD/IDR"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" }
} v2: retorna { pair, rate, rate_as_of } (sem base_currency/quote_currency, sem o invólucro meta — a taxa é o dado). ?pair é ignorado — a v2 sempre retorna USD/IDR (a v1 respeita ?pair). Retorna 503 FX_RATE_UNAVAILABLE se não existir uma taxa utilizável.
/balance Retorna o saldo da conta do usuário autenticado.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v2/balance \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/balance", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/balance",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"balance": { "amount": "30.77", "currency": "USD", "canonical_amount": 500000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2: os campos monetários são objetos monetários em USD e a resposta carrega um único meta.fx { pair, rate, rate_as_of }. rate é o valor inteiro em IDR por 1 USD, portanto USD = canonical_amount / rate. Os totais usam 2 casas decimais; preços/reembolsos por item usam 4. Um valor estritamente positivo nunca é arredondado para 0.00. rate_as_of é o timestamp RFC3339 da taxa (no formato +00:00) ou null quando nenhum timestamp é registrado.
Apenas v2: se não existir uma taxa USD/IDR utilizável, os endpoints monetários retornam 503 FX_RATE_UNAVAILABLE com um cabeçalho Retry-After em vez de um corpo monetário. A v1 nunca retorna isso.
/orders Retorna uma lista dos pedidos do usuário autenticado, ordenados do mais recente. Suporta filtragem por status e paginação via offset.
Parâmetros de Consulta
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
limit | integer | Não | Máximo de resultados (1-100, padrão 20) |
offset | integer | Não | Número de resultados a pular (padrão 0) |
status | string | Não | Filtrar por status: ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (sem distinção de maiúsculas) |
Exemplo de Requisição
curl -s "https://api.smscode.gg/v2/orders?limit=5&status=ACTIVE" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
limit: "5", status: "ACTIVE", offset: "0",
});
const res = await fetch(`https://api.smscode.gg/v2/orders?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/orders",
params={"limit": 5, "status": "ACTIVE", "offset": 0},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 1001,
"status": "ACTIVE",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
}
],
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2: os campos monetários são objetos monetários em USD e a resposta carrega um único meta.fx { pair, rate, rate_as_of }. rate é o valor inteiro em IDR por 1 USD, portanto USD = canonical_amount / rate. Os totais usam 2 casas decimais; preços/reembolsos por item usam 4. Um valor estritamente positivo nunca é arredondado para 0.00. rate_as_of é o timestamp RFC3339 da taxa (no formato +00:00) ou null quando nenhum timestamp é registrado.
Apenas v2: se não existir uma taxa USD/IDR utilizável, os endpoints monetários retornam 503 FX_RATE_UNAVAILABLE com um cabeçalho Retry-After em vez de um corpo monetário. A v1 nunca retorna isso.
/orders/{id} Retorna um único pedido por ID. Retorna apenas pedidos do usuário autenticado.
Parâmetros de Caminho
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido (parâmetro de caminho) |
Exemplo de Requisição
curl -s https://api.smscode.gg/v2/orders/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/orders/1001", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/orders/1001",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"id": 1001,
"status": "OTP_RECEIVED",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"otp_code": "123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2: os campos monetários são objetos monetários em USD e a resposta carrega um único meta.fx { pair, rate, rate_as_of }. rate é o valor inteiro em IDR por 1 USD, portanto USD = canonical_amount / rate. Os totais usam 2 casas decimais; preços/reembolsos por item usam 4. Um valor estritamente positivo nunca é arredondado para 0.00. rate_as_of é o timestamp RFC3339 da taxa (no formato +00:00) ou null quando nenhum timestamp é registrado.
Apenas v2: se não existir uma taxa USD/IDR utilizável, os endpoints monetários retornam 503 FX_RATE_UNAVAILABLE com um cabeçalho Retry-After em vez de um corpo monetário. A v1 nunca retorna isso.
/orders/active Lista todos os pedidos ativos no momento (ACTIVE + OTP_RECEIVED). Use para verificar atualizações de status de OTP.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v2/orders/active \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/orders/active", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/orders/active",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": [
{
"id": 1001,
"status": "OTP_RECEIVED",
"otp_code": "123456",
"otp_message": "Your verification code is 123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"failed_reason": null
},
{
"id": 1002,
"status": "ACTIVE",
"otp_code": null,
"otp_message": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
]
} v2: este endpoint não envolve valores monetários — ele não retorna amount nem meta.fx (mesma estrutura da v1, sob /v2).
/orders/create Cria um novo pedido de número virtual. Debita o saldo automaticamente. Suporta um cabeçalho Idempotency-Key opcional para evitar pedidos duplicados em retentativas de rede.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
product_id | integer | Não | ID do produto a ser pedido. Informe APENAS UM: product_id OU catalog_product_id, não ambos. |
catalog_product_id | integer | Não | ID de produto estável — o servidor escolhe a melhor oferta atual (mais barata, considerando a confiabilidade). Informe este ou product_id. |
max_price | integer · string | Não | Limite de preço opcional. v1: inteiro em IDR. v2: string decimal em USD (ex.: "0.50"). |
prefer_provider | string | Não | Código de provedor opcional a preferir quando as ofertas empatam. |
policy | string | Não | Política de roteamento opcional, válida apenas com catalog_product_id. Valores: cheapest (padrão) escolhe a oferta saudável mais barata; best_success ordena as ofertas pelo sucesso de entrega recente primeiro. O best_success pontua cada provedor pela proporção de pedidos que receberam OTP nos últimos 30 dias completos, em faixas de 10%, e só conta um provedor depois que ele tem pelo menos 20 pedidos nesse período — provedores abaixo desse limite ou sem histórico são tratados como neutros, de modo que ofertas novas nunca ficam sem chance (opcional; o sinal começa neutro). Quando prefer_provider também é informado, o provedor preferido ainda fica em primeiro lugar. |
quantity | integer | Não | Quantidade de itens (1-100, padrão 1) |
Envie um cabeçalho Idempotency-Key para repetir solicitações com segurança sem criar pedidos duplicados. A chave pode conter letras, dígitos, hífen e sublinhado (A-Z a-z 0-9 _ -), até 128 caracteres; uma chave inválida é rejeitada com 422 VALIDATION_ERROR. Repetir com a mesma chave e o mesmo corpo reproduz o resultado original (incluindo o failed_count de um sucesso parcial). Uma retentativa que chega ao provedor mas falha é registrada e reproduz esse mesmo erro — use uma chave NOVA para tentar de novo. Falhas sem efeitos colaterais (saldo insuficiente, nenhuma oferta disponível) liberam a chave, então você pode adicionar saldo e repetir com a mesma chave. Reutilizar uma chave com um corpo diferente retorna 422 IDEMPOTENCY_KEY_REUSED, e uma solicitação ainda em andamento com essa chave retorna 409 REQUEST_IN_PROGRESS. O campo failed_reason nas respostas de create é sempre null — ele só é preenchido na consulta/listagem de pedidos.
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v2/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{"product_id":142,"quantity":1}'
# Or order by stable catalog_product_id — the server picks the best current offer
# (cheapest, reliability-aware). Optional max_price caps the price; on v1 it is an
# IDR integer, on v2 it is a USD decimal string (e.g. "0.50"). prefer_provider
# (provider code) is a soft tie-breaker. Pass EITHER product_id OR catalog_product_id.
curl -s -X POST https://api.smscode.gg/v2/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-124" \
-d '{"catalog_product_id":87,"max_price":20000}'const res = await fetch("https://api.smscode.gg/v2/orders/create", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
"Idempotency-Key": "unique-request-id-123",
},
body: JSON.stringify({ product_id: 142, quantity: 1 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/create",
json={"product_id": 142, "quantity": 1},
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Idempotency-Key": "unique-request-id-123",
})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"orders": [
{
"id": 1002,
"status": "ACTIVE",
"product_id": 142,
"catalog_product_id": 87,
"amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"phone_number": "+6281234567891",
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
],
"failed_count": 0
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2: os campos monetários são objetos monetários em USD e a resposta carrega um único meta.fx { pair, rate, rate_as_of }. rate é o valor inteiro em IDR por 1 USD, portanto USD = canonical_amount / rate. Os totais usam 2 casas decimais; preços/reembolsos por item usam 4. Um valor estritamente positivo nunca é arredondado para 0.00. rate_as_of é o timestamp RFC3339 da taxa (no formato +00:00) ou null quando nenhum timestamp é registrado.
Apenas v2: se não existir uma taxa USD/IDR utilizável, os endpoints monetários retornam 503 FX_RATE_UNAVAILABLE com um cabeçalho Retry-After em vez de um corpo monetário. A v1 nunca retorna isso.
/orders/cancel Cancela um pedido ativo. O custo do aluguel é reembolsado ao saldo da sua conta.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido a cancelar |
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v2/orders/cancel \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v2/orders/cancel", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/cancel",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"new_balance": { "amount": "31.69", "currency": "USD", "canonical_amount": 515000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2: os campos monetários são objetos monetários em USD e a resposta carrega um único meta.fx { pair, rate, rate_as_of }. rate é o valor inteiro em IDR por 1 USD, portanto USD = canonical_amount / rate. Os totais usam 2 casas decimais; preços/reembolsos por item usam 4. Um valor estritamente positivo nunca é arredondado para 0.00. rate_as_of é o timestamp RFC3339 da taxa (no formato +00:00) ou null quando nenhum timestamp é registrado.
Apenas v2: se não existir uma taxa USD/IDR utilizável, os endpoints monetários retornam 503 FX_RATE_UNAVAILABLE com um cabeçalho Retry-After em vez de um corpo monetário. A v1 nunca retorna isso.
/orders/finish Marca um pedido como concluído após receber o OTP. Isso libera o número imediatamente em vez de aguardar a expiração.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido a finalizar |
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v2/orders/finish \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v2/orders/finish", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/finish",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"order_id": 1001,
"status": "COMPLETED"
}
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
/orders/resend Solicita que a plataforma reenvie o SMS para o número alugado. Nem todas as plataformas suportam reenvio — verifique o campo resent na resposta.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
id | integer | Sim | ID do pedido para reenviar SMS |
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v2/orders/resend \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v2/orders/resend", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/resend",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"order_id": 1001,
"status": "ACTIVE",
"resent": true
}
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
/webhook Retorna a configuração atual de notificação por webhook.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s https://api.smscode.gg/v2/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/webhook", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/webhook",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
/webhook Atualiza a URL e/ou o segredo do webhook. Um segredo é gerado automaticamente quando você define uma URL pela primeira vez. Envie uma string vazia para limpar. A URL deve usar HTTPS.
Corpo da Requisição
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
webhook_url | string | Não | URL HTTPS para receber eventos de webhook (string vazia para limpar) |
webhook_secret | string | Não | Segredo compartilhado para assinatura HMAC-SHA256 (gerado automaticamente se omitido na primeira configuração) |
Pelo menos um campo é obrigatório.
Exemplo de Requisição
curl -s -X PATCH https://api.smscode.gg/v2/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"webhook_url":"https://example.com/webhook"}'const res = await fetch("https://api.smscode.gg/v2/webhook", {
method: "PATCH",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ webhook_url: "https://example.com/webhook" }),
});
const data = await res.json();import requests
res = requests.patch("https://api.smscode.gg/v2/webhook",
json={"webhook_url": "https://example.com/webhook"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
/webhook/test Envia um evento de teste para a URL de webhook configurada. Retorna o código de status HTTP do seu servidor. Útil para verificar se o seu endpoint está funcionando antes de começar a usar.
Parâmetros
Nenhum
Exemplo de Requisição
curl -s -X POST https://api.smscode.gg/v2/webhook/test \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/webhook/test", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/webhook/test",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()Exemplo de Resposta
{
"success": true,
"data": {
"status_code": 200
}
} Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
⟩Notificações por Webhook
Configure uma URL de webhook para receber notificações push em tempo real sobre eventos de pedidos em vez de fazer polling. Esta é a abordagem recomendada para scripts de bot.
Eventos
| Evento | Gatilho |
|---|---|
order.otp_received | Código OTP entregue ao número alugado |
order.completed | Pedido marcado como concluído (manualmente ou por expiração) |
order.expired | Pedido expirado sem OTP (saldo reembolsado) |
order.canceled | Pedido cancelado pelo usuário (saldo reembolsado) |
Payload
{
"event": "order.otp_received",
"timestamp": "2026-02-25T12:00:00+00:00",
"data": {
"order_id": 1001,
"phone_number": "+628123456789",
"otp_code": "1234",
"otp_message": "Your verification code is 1234",
"product_id": 42,
"catalog_product_id": 87,
"country": "Indonesia",
"platform": "WhatsApp"
}
} Verificação de Assinatura
Cada requisição de webhook inclui um cabeçalho X-Webhook-Signature com uma assinatura HMAC-SHA256 do corpo da requisição, usando seu webhook_secret como chave:
Verifique esta assinatura no seu servidor para garantir que a requisição é autêntica. A entrega é do tipo disparar e esquecer, com timeout de 3 segundos e sem retentativas.
⟩Rate Limits
As requisições da API possuem limites por grupo de endpoint. Exceder o limite retorna 429 Too Many Requests com um cabeçalho Retry-After indicando quantos segundos aguardar.
| Grupo de Endpoints | Limite | Janela |
|---|---|---|
| Catálogo (countries, services, products, exchange-rate) | 5.000 requisições | 60 segundos |
| Saldo | 600 requisições | 60 segundos |
| Consulta de pedidos (list, get, active) | 5.000 requisições | 60 segundos |
| Criação de pedido | 3.000 requisições | 60 segundos |
| Cancelamento de pedido | 1.000 requisições | 60 segundos |
| Ações de pedido (finish, resend) | 1.000 requisições | 60 segundos |
| Config de webhook (get, update) | 600 requisições | 60 segundos |
| Teste de webhook | 10 requisições | 60 segundos |
⟩Códigos de Erro
Respostas de erro incluem um destes códigos em error.code:
| Código | HTTP | Descrição |
|---|---|---|
UNAUTHORIZED | 401 | Token de API ausente ou inválido |
FORBIDDEN | 403 | Acesso negado |
NOT_FOUND | 404 | Recurso não encontrado (pedido, taxa de câmbio, etc.) |
CONFLICT | 409 | Requisição duplicada ou conflito de recurso |
INSUFFICIENT_BALANCE | 409 | Saldo insuficiente para criar o pedido |
VALIDATION_ERROR | 422 | Os parâmetros da requisição falharam na validação |
RATE_LIMIT_EXCEEDED | 429 | Muitas requisições (verifique o cabeçalho Retry-After) |
INTERNAL_ERROR | 500 | Erro interno do servidor |
PROVIDER_ERROR | 422 | O provedor de SMS upstream rejeitou a requisição. Em falhas de criação de pedido, o erro pode incluir <code>details</code>: <code>cause_counts</code> (pedidos com <code>product_id</code> legado — uma contagem agrupada por causa) ou <code>attempts</code> (pedidos com <code>catalog_product_id</code> — resultados por tentativa), usando os valores <code>ok</code>, <code>no_numbers</code>, <code>insufficient_balance</code>, <code>price_rejected</code>, <code>provider_unavailable</code>, <code>provider_account_balance</code>, <code>provider_error</code>. |
NO_OFFER_AVAILABLE | 422 | Nenhuma oferta ativa corresponde ao produto e à política solicitados (limite de preço, disponibilidade). |
CANCEL_TOO_EARLY | 409 | Pedido muito recente para cancelar — aguarde 2 minutos |
REQUEST_IN_PROGRESS | 409 | Uma solicitação de criação com esta chave de idempotência ainda está em andamento |
IDEMPOTENCY_KEY_REUSED | 422 | Esta chave de idempotência já foi usada com um corpo de solicitação diferente |
SERVICE_UNAVAILABLE | 503 | Serviço temporariamente indisponível (manutenção) |
FX_RATE_UNAVAILABLE | 503 | Taxa de câmbio USD/IDR indisponível (endpoints monetários da v2) — retorna 503 com um cabeçalho Retry-After. |
⟩Migrando da v1 para a v2
A v1 serve IDR; a v2 serve USD. Ambas as versões coexistem permanentemente — não há descontinuação. Escolha uma versão por integração; não misture caminhos base. A v2 é idêntica à v1, exceto na forma como o dinheiro é representado.
| Aspecto | v1 · IDR | v2 · USD |
|---|---|---|
| Campos monetários | IDR inteiro, ex. 15000 | Objeto monetário { amount, currency, canonical_amount, canonical_currency } |
meta.fx | Ausente | Obrigatório em toda resposta que contém valores monetários |
| Moeda | IDR | USD (fixo no código) |
FX_RATE_UNAVAILABLE | — | Novo 503 + Retry-After quando não há taxa utilizável |
| Precisão | — | Totais 2 casas, preços/reembolsos 4 casas, arredondamento para cima em positivos |
GET /catalog/exchange-rate | {pair, base_currency, quote_currency, rate}; respeita ?pair | {pair, rate, rate_as_of}; ?pair ignorado (apenas USD/IDR) |
Exemplos lado a lado
{
"success": true,
"data": {
"currency": "IDR",
"balance": 500000
}
}{
"success": true,
"data": {
"balance": { "amount": "30.77", "currency": "USD", "canonical_amount": 500000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
}{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": 15000,
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1 }
}{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1, "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
}catalog_product_id é a identidade ESTÁVEL do produto (uma por país + plataforma) — pode ser armazenada com segurança nos seus sistemas E também serve para pedir diretamente: faça o pedido com catalog_product_id (mais os opcionais max_price e prefer_provider) e o servidor resolve a oferta ativa para você. O caminho pelo product_id por oferta continua suportado e retrocompatível — esse id é volátil e muda sempre que uma faixa de preço do provedor se altera, então a recomendação de buscar o catálogo novamente antes de pedir vale apenas para esse caminho.
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": 15000,
"new_balance": 515000
}
}{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"new_balance": { "amount": "31.69", "currency": "USD", "canonical_amount": 515000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
}Checklist de migração
- Troque o caminho base
/v1→/v2. - Faça o parse dos campos monetários como objetos — leia
amountcomo uma string decimal;currencyé"USD". - Para reconciliação do livro-razão use
canonical_amount(IDR exato); oamountem USD é uma projeção em tempo de renderização e arateé divulgada uma vez emmeta.fx. - Trate o novo
FX_RATE_UNAVAILABLE(503) — tente novamente após oRetry-After. A v1 nunca retorna isso.