Acesso programático a números virtuais, pedidos e saldo da conta.
Recomendado
⟩Comece com os SDKs oficiais
Use o SDK TypeScript/JavaScript ou Python para novas integrações. Ambos usam por padrão a API pública /v2, preservam chaves de idempotência em tentativas seguras, expõem erros tipados e mantêm o ciclo de vida do OTP consistente.
Crie um pedido com product_id para um slot de faixa exato e estável, ou com catalog_product_id, operator_id opcional, min_price/max_price e uma chave de idempotência para chamadas pagas roteadas seguras em novas tentativas.
catalog_product_idmax_priceIdempotency-Key
02
Use o OTP
Aguarde o OTP, envie-o no app de destino e depois chame finish para fechar o pedido.
waitForOtpwait_for_otpfinish
03
Reenvie só quando necessário
Depois de reenviar, aguarde um novo OTP com afterCode em TypeScript ou after_code em Python.
can_resendresend_available_at
import { SmscodeClient, OtpTimeoutError } from "@smscode/sdk";const client = new SmscodeClient({ token: process.env.SMSCODE_TOKEN! });let orderId: number | undefined;try { const created = await client.orders.create({ catalog_product_id: Number(process.env.SMSCODE_CATALOG_PRODUCT_ID), max_price: "0.50", quantity: 1, }); const order = created.orders[0]!; orderId = order.id; const first = await client.orders.waitForOtp(orderId, { timeoutMs: 120_000 }); console.log(first.otpCode); // Envie este OTP no app de destino. await client.orders.finish(orderId);} catch (err) { if (err instanceof OtpTimeoutError && orderId !== undefined) { const current = await client.orders.get(orderId); if (current.can_cancel) await client.orders.cancel(orderId); } throw err;}
import osfrom smscode import OtpTimeoutError, SmscodeClientwith SmscodeClient(token=os.environ["SMSCODE_TOKEN"]) as client: created = client.orders.create( catalog_product_id=int(os.environ["SMSCODE_CATALOG_PRODUCT_ID"]), max_price="0.50", quantity=1, ) order = created.orders[0] order_id = int(order["id"]) try: first = client.orders.wait_for_otp(order_id, timeout_ms=120_000) print(first.otp_code) # Envie este OTP no app de destino. client.orders.finish(order_id) except OtpTimeoutError: current = client.orders.get(order_id) if current["can_cancel"]: client.orders.cancel(order_id) raise
Use can_resend e resend_available_at para o timing de reenvio. Timestamps de reenvio de baixo nível são internos e não são campos públicos de resposta.
⟩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:
Authorization:Bearer YOUR_API_TOKEN
Requisições sem um token válido recebem uma resposta 401 UNAUTHORIZED.
⟩URL Base
Todos os caminhos de endpoints abaixo são relativos a:
https://api.smscode.gg/v1
⟩Formato de Resposta
Toda resposta retorna JSON com um envelope consistente. Todas as respostas incluem um cabeçalho x-request-id para depuração.
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.
Retorna as operadoras selecionáveis para um país + serviço. Se houver operadoras reais e estoque Any disponíveis, a resposta inclui uma linha Any com operator_id null; se não houver produtos específicos de operadora, a lista fica vazia.
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 de produto de slot de faixa exato e estável para pedir diretamente. Envie ESTE ou catalog_product_id, não ambos.
catalog_product_id
integer
Não
ID umbrella roteado de país+plataforma. O servidor escolhe uma faixa atual correspondente. Envie este ou product_id.
operator_id
integer
Não
ID de operadora opcional de /catalog/operators. Válido somente com catalog_product_id; omita para Any.
min_price
integer
Não
Piso de preço opcional. Inteiro em IDR. Válido somente com catalog_product_id.
max_price
integer
Não
Teto de preço opcional. Inteiro em IDR. Válido somente com catalog_product_id.
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 route by catalog_product_id — the server picks a current tier.# product_id is the stable exact tier-slot id. catalog_product_id is the# country+platform umbrella for routed ordering. Optional min_price/max_price# bound the tier; operator_id scopes to a carrier from /catalog/operators.# 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,"min_price":12000,"max_price":20000,"operator_id":433}'
Reativa um número concluído — pede novamente o mesmo número para outro código de verificação, sem alugar um novo. Apenas um pedido concluído cujo número suporte reativação é elegível (verifique can_reactivate no pedido ou veja uma prévia com reactivate-options). O filho reativado é um pedido NOVO, retornado no mesmo formato que create; o saldo é debitado automaticamente.
Corpo da Requisição
Nome
Tipo
Obrigatório
Descrição
id
integer
Sim
O pedido concluído a ser reativado.
max_price
integer
Não
Teto de custo opcional. Inteiro em IDR. A reativação é recusada com 422 VALIDATION_ERROR se o custo atual o exceder.
Assim como create, esta é uma mutação de dinheiro — envie um cabeçalho Idempotency-Key para repetir com segurança (um create e um reactivate nunca colidem na mesma chave). Reutilizar uma chave com um corpo diferente retorna 422 IDEMPOTENCY_KEY_REUSED, e uma solicitação ainda em processamento com essa chave retorna 409 REQUEST_IN_PROGRESS. Um número que não pode ser reativado retorna 409 CONFLICT; saldo baixo demais retorna 409 INSUFFICIENT_BALANCE.
Mostra uma prévia de quanto uma reativação custaria agora. Somente leitura — não consome nenhum Idempotency-Key e não cria nada. Retorna o custo como um inteiro em IDR. Disponível apenas para um pedido concluído cujo número suporte reativação.
Parâmetros de Caminho
Nome
Tipo
Obrigatório
Descrição
id
integer
Sim
ID do pedido para o qual visualizar a prévia do custo de reativação (parâmetro de caminho).
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)
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();
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
Novo SMS entregue; o código detectado pode ser null
order.completed
Pedido marcado como concluído (manualmente ou por expiração)
order.expired
Pedido expirado antes de receber um SMS (saldo reembolsado)
order.canceled
Pedido cancelado pelo usuário (saldo reembolsado)
Cada SMS novo emite este evento. otp_code pode ser null enquanto otp_message está presente. Vários eventos SMS podem chegar fora de ordem; use sms_revision para ignorar um par agregado mais antigo.
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.
⟩Limites de requisições
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.
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 details: cause_counts (pedidos com product_id legado — uma contagem agrupada por causa) ou attempts (pedidos com catalog_product_id — resultados por tentativa), usando os valores ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error.
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.
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.
Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
GET/catalog/operators
Retorna as operadoras selecionáveis para um país + serviço. Se houver operadoras reais e estoque Any disponíveis, a resposta inclui uma linha Any com operator_id null; se não houver produtos específicos de operadora, a lista fica vazia.
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.
GET/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.
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.
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.
GET/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)
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.
GET/orders/{id}
Retorna um único pedido por ID. Retorna apenas pedidos do usuário autenticado.
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.
GET/orders/active
Lista todos os pedidos ativos no momento (ACTIVE + OTP_RECEIVED). Use para verificar atualizações de status de OTP.
v2: este endpoint não envolve valores monetários — ele não retorna amount nem meta.fx (mesma estrutura da v1, sob /v2).
POST/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 de produto de slot de faixa exato e estável para pedir diretamente. Envie ESTE ou catalog_product_id, não ambos.
catalog_product_id
integer
Não
ID umbrella roteado de país+plataforma. O servidor escolhe uma faixa atual correspondente. Envie este ou product_id.
operator_id
integer
Não
ID de operadora opcional de /catalog/operators. Válido somente com catalog_product_id; omita para Any.
min_price
string
Não
Piso de preço opcional. String decimal em USD (ex.: "0.30"). Válido somente com catalog_product_id.
max_price
string
Não
Teto de preço opcional. String decimal em USD (ex.: "0.50"). Válido somente com catalog_product_id.
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.
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.
POST/orders/cancel
Cancela um pedido ativo. O custo do aluguel é reembolsado ao saldo da sua conta.
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.
POST/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.
Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
POST/orders/reactivate
Reativa um número concluído — pede novamente o mesmo número para outro código de verificação, sem alugar um novo. Apenas um pedido concluído cujo número suporte reativação é elegível (verifique can_reactivate no pedido ou veja uma prévia com reactivate-options). O filho reativado é um pedido NOVO, retornado no mesmo formato que create; o saldo é debitado automaticamente.
Corpo da Requisição
Nome
Tipo
Obrigatório
Descrição
id
integer
Sim
O pedido concluído a ser reativado.
max_price
string
Não
Teto de custo opcional. String decimal em USD (ex.: "0.50"). A reativação é recusada com 422 VALIDATION_ERROR se o custo atual o exceder.
Assim como create, esta é uma mutação de dinheiro — envie um cabeçalho Idempotency-Key para repetir com segurança (um create e um reactivate nunca colidem na mesma chave). Reutilizar uma chave com um corpo diferente retorna 422 IDEMPOTENCY_KEY_REUSED, e uma solicitação ainda em processamento com essa chave retorna 409 REQUEST_IN_PROGRESS. Um número que não pode ser reativado retorna 409 CONFLICT; saldo baixo demais retorna 409 INSUFFICIENT_BALANCE.
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.
GET/orders/{id}/reactivate-options
Mostra uma prévia de quanto uma reativação custaria agora. Somente leitura — não consome nenhum Idempotency-Key e não cria nada. Retorna o custo como um objeto monetário em USD com um recibo de FX. Disponível apenas para um pedido concluído cujo número suporte reativação.
Parâmetros de Caminho
Nome
Tipo
Obrigatório
Descrição
id
integer
Sim
ID do pedido para o qual visualizar a prévia do custo de reativação (parâmetro de caminho).
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.
GET/webhook
Retorna a configuração atual de notificação por webhook.
Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
PATCH/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)
Idêntico à v1 — apenas o caminho base muda (/v1 → /v2).
POST/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();
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
Novo SMS entregue; o código detectado pode ser null
order.completed
Pedido marcado como concluído (manualmente ou por expiração)
order.expired
Pedido expirado antes de receber um SMS (saldo reembolsado)
order.canceled
Pedido cancelado pelo usuário (saldo reembolsado)
Cada SMS novo emite este evento. otp_code pode ser null enquanto otp_message está presente. Vários eventos SMS podem chegar fora de ordem; use sms_revision para ignorar um par agregado mais antigo.
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.
⟩Limites de requisições
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.
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 details: cause_counts (pedidos com product_id legado — uma contagem agrupada por causa) ou attempts (pedidos com catalog_product_id — resultados por tentativa), usando os valores ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error.
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.
v1 → v2
⟩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.
product_id é o ID estável do slot de faixa da SMSCode. Guarde-o quando quiser pedir exatamente essa faixa; preço e disponibilidade podem mudar na mesma linha. catalog_product_id é o umbrella estável de país+plataforma para pedidos roteados; use-o com operator_id, min_price, max_price, prefer_provider e policy opcionais quando quiser que o servidor escolha uma faixa atual correspondente.
Faça o parse dos campos monetários como objetos — leia amount como uma string decimal; currency é "USD".
Para reconciliação do livro-razão use canonical_amount (IDR exato); o amount em USD é uma projeção em tempo de renderização e a rate é divulgada uma vez em meta.fx.
Trate o novo FX_RATE_UNAVAILABLE (503) — tente novamente após o Retry-After. A v1 nunca retorna isso.