# API para desenvolvedores · SMSVEX > Números para verificação, direto do seu código. A mesma loja, pelo seu bot: cote, compre e leia o código em três chamadas. O estorno automático e a proteção contra compra repetida valem igual. Base: `https://api.smsvex.com/api/v1/integration`. Todo valor em dinheiro é em centavos, inteiro. Toda data é ISO 8601, em UTC. ## Do zero ao primeiro código em três chamadas Crie a conta, confirme o e-mail e gere a chave em **Automações**. Ela vai no cabeçalho `X-API-Key` de toda chamada. 1. **Escolha e cote** (`GET /quote`): Pegue o `slug` em `GET /services` e pergunte o preço de agora. A cotação traz o `band`, o código da faixa. 2. **Compre** (`POST /orders`): Mande o preço e a faixa da cotação, com uma `Idempotency-Key` nova. Se a rede cair, repita a mesma: a loja não cobra duas vezes. 3. **Leia o código** (`GET /orders/{order_id}`): Consulte o pedido a cada 5 segundos. Sem SMS em 18 minutos, o valor volta sozinho ao saldo. O fluxo inteiro, pronto para copiar: ```bash # 1. Cote curl "https://api.smsvex.com/api/v1/integration/quote?service_slug=whatsapp-wa" \ -H "X-API-Key: $SMSVEX_API_KEY" # 2. Compre (Idempotency-Key nova a cada tentativa) curl -X POST "https://api.smsvex.com/api/v1/integration/orders" \ -H "X-API-Key: $SMSVEX_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"service_slug":"whatsapp-wa","expected_price_cents":890,"band":"3f9a1c07b2e4d856"}' # 3. Leia o código curl "https://api.smsvex.com/api/v1/integration/orders/48213" \ -H "X-API-Key: $SMSVEX_API_KEY" ``` ```python import os, time, uuid, requests API = "https://api.smsvex.com/api/v1/integration" H = {"X-API-Key": os.environ["SMSVEX_API_KEY"]} # 1. Cote: o preço de agora e o código da faixa cota = requests.get(f"{API}/quote", params={"service_slug": "whatsapp-wa"}, headers=H).json() # 2. Compre com uma Idempotency-Key nova (repita a MESMA se a rede cair) compra = requests.post(f"{API}/orders", headers={**H, "Idempotency-Key": str(uuid.uuid4())}, json={"service_slug": "whatsapp-wa", "expected_price_cents": cota["price_cents"], "band": cota["band"]}).json() pedido = compra["orders"][0] print("Número:", pedido["phone"]) # 3. Leia o código while True: p = requests.get(f"{API}/orders/{pedido['id']}", headers=H).json() if p["codes"]: print("Código:", p["codes"][-1]); break time.sleep(5) ``` ```javascript import { randomUUID } from "node:crypto"; const API = "https://api.smsvex.com/api/v1/integration"; const H = { "X-API-Key": process.env.SMSVEX_API_KEY }; // 1. Cote const cota = await (await fetch(`${API}/quote?service_slug=whatsapp-wa`, { headers: H })).json(); // 2. Compre, com uma Idempotency-Key nova const compra = await (await fetch(`${API}/orders`, { method: "POST", headers: { ...H, "Content-Type": "application/json", "Idempotency-Key": randomUUID() }, body: JSON.stringify({ service_slug: "whatsapp-wa", expected_price_cents: cota.price_cents, band: cota.band }), })).json(); const pedido = compra.orders[0]; // 3. Leia o código for (;;) { const p = await (await fetch(`${API}/orders/${pedido.id}`, { headers: H })).json(); if (p.codes.length) { console.log("Código:", p.codes.at(-1)); break; } await new Promise((r) => setTimeout(r, 5000)); } ``` ```php $api = "https://api.smsvex.com/api/v1/integration"; $h = ["X-API-Key: " . getenv("SMSVEX_API_KEY")]; // 1. Cote $cota = json_decode(file_get_contents("$api/quote?service_slug=whatsapp-wa", false, stream_context_create(["http" => ["header" => $h]])), true); // 2. Compre, com uma Idempotency-Key nova $corpo = json_encode(["service_slug" => "whatsapp-wa", "expected_price_cents" => $cota["price_cents"], "band" => $cota["band"]]); $compra = json_decode(file_get_contents("$api/orders", false, stream_context_create(["http" => [ "method" => "POST", "content" => $corpo, "header" => array_merge($h, ["Content-Type: application/json", "Idempotency-Key: " . bin2hex(random_bytes(16))])]])), true); ``` ## Mostre as faixas de preço para quem usa o seu bot O mesmo serviço sai por preços diferentes, e cada preço é um grupo diferente de números. O mais barato entrega na maioria das vezes, mas não sempre: quando um número não recebe o SMS, trocar de faixa costuma resolver. A cotação traz todas elas. - **Comece pela padrão** (`is_default`): é a mais barata que tem números para a quantidade pedida. - **Deixe o usuário trocar**: mostre o preço e o estoque de cada faixa e compre com o `band` da escolhida. - **Se o SMS não chegar**, o valor volta sozinho; ofereça a próxima faixa na nova compra. - **Não guarde o `band`**: cote de novo antes de cada compra. Se a faixa acabar, a compra recusa com `BAND_GONE` e nada é cobrado. ## As 9 rotas #### Conta ### Ver o saldo `GET /balance` O saldo da conta dona da chave, em centavos. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. **Resposta** (exemplo) ```json { "balance_cents": 12450 } ``` #### Catálogo ### Listar serviços `GET /services` Os serviços à venda agora, com o preço a partir de. O slug é o que as outras rotas pedem. **Parâmetros** - `q` (texto): Filtra por nome. Ex.: whats **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. **Resposta** (exemplo) ```json { "services": [ { "slug": "whatsapp-wa", "name": "Whatsapp", "country": "BR", "price_cents": 890 }, { "slug": "mercado-cq", "name": "Mercado", "country": "BR", "price_cents": 40 }, ... ] } ``` #### Compra ### Cotar um serviço `GET /quote` Todas as faixas de preço do serviço agora, com estoque. Não cobra nem reserva nada. **Parâmetros** - `service_slug` (texto, obrigatório): O serviço, como aparece no catálogo. Ex.: whatsapp-wa - `quantity` (inteiro): Quantos números. Padrão 1. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `NOT_FOUND` (404): O serviço ou o pedido não existe nesta conta. - `BAND_GONE` (409): A faixa da cotação acabou. - `TOO_MANY_OPEN_ORDERS` (409): Limite de números abertos atingido. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. - `PROVIDER_ERROR` (502): A rede de números falhou. - `SERVICE_UNAVAILABLE` (503): O serviço está fora do ar agora. **Resposta** (exemplo) ```json { "service_slug": "mercado-cq", "quantity": 1, "price_cents": 40, "band": "3f9a1c07b2e4d856", "total_cents": 40, "bands": [ { "price_cents": 40, "band": "3f9a1c07b2e4d856", "stock": 13995, "is_default": true, "atende": true }, { "price_cents": 42, "band": "a81d09c4e2f7b350", "stock": 6902, "is_default": false, "atende": true }, { "price_cents": 187, "band": "5c2e7f10d9ab4468", "stock": 1968, "is_default": false, "atende": true } ] } ``` ### Comprar números `POST /orders` Cobra o saldo e reserva o número. Mande o preço e a faixa que a cotação devolveu. **Cabeçalhos** - `Idempotency-Key` (obrigatório): Um valor novo a cada tentativa de compra (8 a 100 caracteres). Repetir a mesma chave devolve a mesma resposta, sem comprar de novo. **Corpo (JSON)** - `service_slug` (texto, obrigatório): O mesmo da cotação. - `quantity` (inteiro): Padrão 1. - `expected_price_cents` (inteiro, obrigatório): O price_cents da cotação. Se o preço mudou, a compra recusa. - `band` (texto, obrigatório): O band da cotação. Se a faixa acabou, a compra recusa; nunca troca de faixa sozinha. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `INSUFFICIENT_BALANCE` (402): Saldo insuficiente. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `NOT_FOUND` (404): O serviço ou o pedido não existe nesta conta. - `BAND_GONE` (409): A faixa da cotação acabou. - `IDEMPOTENCY_IN_PROGRESS` (409): A compra com essa chave ainda está em andamento. - `IDEMPOTENCY_KEY_REUSED` (409): Essa Idempotency-Key já foi usada em outra compra. - `LOW_CONFIRMATION_RATE` (409): Muitos números sem uso recente. - `PRICE_CHANGED` (409): O preço mudou desde a cotação. - `TOO_MANY_OPEN_ORDERS` (409): Limite de números abertos atingido. - `IDEMPOTENCY_KEY_REQUIRED` (422): Faltou o cabeçalho Idempotency-Key. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. - `PROVIDER_ERROR` (502): A rede de números falhou. - `PROVIDER_OUT_OF_FUNDS` (502): Esta compra está indisponível na loja agora. - `SERVICE_UNAVAILABLE` (503): O serviço está fora do ar agora. **Resposta** (exemplo) ```json { "batch_id": null, "orders": [ { "id": 48213, "phone": "5511987654321", "status": "active", ... } ], "failures": [], "charged_cents": 890, "refunded_cents": 0 } ``` #### Pedidos ### Listar pedidos `GET /orders` Os pedidos da conta. Com desfecho=aberto, só os que ainda esperam ou já têm código. **Parâmetros** - `desfecho` (texto): aberto, com_codigo, esperando, recebido, expirado, estornado ou cancelado. - `page` / `per_page` (inteiro): Página e tamanho (até 100). - `de` / `ate` (data): Início e fim do período, em AAAA-MM-DD, no horário de Brasília. - `q` (texto): Busca por número de telefone ou #pedido. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. **Resposta** (exemplo) ```json { "orders": [ { "id": 48213, "service_slug": "whatsapp-wa", "phone": "5511987654321", "status": "received", "codes": ["482913"], "expires_at": "2026-09-30T17:42:10+00:00" } ], "total": 1 } ``` ### Ver um pedido `GET /orders/{order_id}` Um pedido, com os códigos que chegaram e a hora de cada um. **Parâmetros** - `order_id` (inteiro, obrigatório): O id do pedido. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `NOT_FOUND` (404): O serviço ou o pedido não existe nesta conta. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. **Resposta** (exemplo) ```json { "id": 48213, "status": "received", "codes": ["482913"], "code_times": ["2026-09-30T17:24:55+00:00"], "can_retry": true } ``` ### Cancelar e estornar `POST /orders/{order_id}/cancel` Cancela um número sem código e devolve o valor ao saldo. **Parâmetros** - `order_id` (inteiro, obrigatório): O id do pedido. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `NOT_FOUND` (404): O serviço ou o pedido não existe nesta conta. - `ORDER_NOT_CANCELLABLE` (409): Este pedido não pode mais ser cancelado. - `CANCEL_TOO_EARLY` (422): Ainda não dá para cancelar. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. - `PROVIDER_ERROR` (502): A rede de números falhou. - `SERVICE_UNAVAILABLE` (503): O serviço está fora do ar agora. **Resposta** (exemplo) ```json { "id": 48213, "status": "cancelled", ... } ``` ### Pedir outro SMS `POST /orders/{order_id}/retry` Grátis. Pede outro código no mesmo número. **Parâmetros** - `order_id` (inteiro, obrigatório): O id do pedido. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `NOT_FOUND` (404): O serviço ou o pedido não existe nesta conta. - `ORDER_NOT_RETRIABLE` (409): Este pedido não aceita outro SMS. - `RETRY_BEFORE_FIRST_CODE` (422): Outro SMS só depois do primeiro código. - `RETRY_WINDOW_CLOSED` (422): O prazo para pedir outro SMS acabou. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. - `PROVIDER_ERROR` (502): A rede de números falhou. - `SERVICE_UNAVAILABLE` (503): O serviço está fora do ar agora. **Resposta** (exemplo) ```json { "id": 48213, "status": "active", ... } ``` ### Concluir o pedido `POST /orders/{order_id}/confirm` Diz que o código funcionou. Libera a vaga de número aberto. **Parâmetros** - `order_id` (inteiro, obrigatório): O id do pedido. **Erros desta rota** - `UNAUTHORIZED` (401): Chave ausente, errada ou desligada. - `ACCOUNT_DISABLED` (403): A conta está desativada. - `EMAIL_NOT_VERIFIED` (403): O e-mail da conta não foi confirmado. - `NOT_FOUND` (404): O serviço ou o pedido não existe nesta conta. - `ORDER_NOT_CONFIRMABLE` (409): Este pedido não pode ser concluído. - `VALIDATION_ERROR` (422): Algum campo veio fora do formato. - `RATE_LIMITED` (429): Mais de 120 chamadas no minuto. - `INTERNAL_ERROR` (500): Erro inesperado na loja. **Resposta** (exemplo) ```json { "id": 48213, "status": "completed", ... } ``` ## Decida pelo `code`, nunca pelo texto Todo erro chega como `{"error": {"code", "message"}}`. O texto pode mudar; o `code`, não. - **120** chamadas por minuto, por chave - **10** números abertos ao mesmo tempo - **18 min** para o SMS chegar; depois, o valor volta | code | HTTP | O que houve | O que fazer | | --- | --- | --- | --- | | `UNAUTHORIZED` | 401 | Chave ausente, errada ou desligada. | Confira o cabeçalho X-API-Key. Se gerou outra chave, a anterior parou de valer. | | `ACCOUNT_DISABLED` | 403 | A conta está desativada. | Fale com o suporte. | | `EMAIL_NOT_VERIFIED` | 403 | O e-mail da conta não foi confirmado. | Confirme o e-mail pelo site. | | `RATE_LIMITED` | 429 | Mais de 120 chamadas no minuto. | Espere os segundos do cabeçalho Retry-After. | | `VALIDATION_ERROR` | 422 | Algum campo veio fora do formato. | Confira o tipo e o tamanho de cada campo desta rota. | | `NOT_FOUND` | 404 | O serviço ou o pedido não existe nesta conta. | Confira o slug ou o id. | | `SERVICE_UNAVAILABLE` | 503 | O serviço está fora do ar agora. | Tente de novo em alguns minutos. | | `INTERNAL_ERROR` | 500 | Erro inesperado na loja. | Numa consulta, tente de novo. Numa compra, não repita antes de conferir em GET /orders se ela entrou. | | `INSUFFICIENT_BALANCE` | 402 | Saldo insuficiente. | Recarregue pelo site e tente de novo. | | `PRICE_CHANGED` | 409 | O preço mudou desde a cotação. | Cote de novo e compre com o preço novo. | | `BAND_GONE` | 409 | A faixa da cotação acabou. | Cote de novo. A compra nunca troca de faixa sozinha. | | `TOO_MANY_OPEN_ORDERS` | 409 | Limite de números abertos atingido. | Conclua ou cancele algum antes de comprar. | | `LOW_CONFIRMATION_RATE` | 409 | Muitos números sem uso recente. | Conclua os pedidos em que o código funcionou. | | `IDEMPOTENCY_KEY_REQUIRED` | 422 | Faltou o cabeçalho Idempotency-Key. | Mande um valor novo a cada tentativa. | | `IDEMPOTENCY_KEY_REUSED` | 409 | Essa Idempotency-Key já foi usada em outra compra. | Gere outra para a compra nova. | | `IDEMPOTENCY_IN_PROGRESS` | 409 | A compra com essa chave ainda está em andamento. | Antes de repetir, confira em GET /orders se ela entrou. | | `PROVIDER_ERROR` | 502 | A rede de números falhou. | Nada foi cobrado. Tente de novo. | | `PROVIDER_OUT_OF_FUNDS` | 502 | Esta compra está indisponível na loja agora. | Nada foi cobrado. Tente de novo mais tarde. | | `CANCEL_TOO_EARLY` | 422 | Ainda não dá para cancelar. | Espere o tempo da mensagem. | | `ORDER_NOT_CANCELLABLE` | 409 | Este pedido não pode mais ser cancelado. | Ele já tem código ou já fechou. | | `RETRY_BEFORE_FIRST_CODE` | 422 | Outro SMS só depois do primeiro código. | Espere o primeiro chegar. | | `RETRY_WINDOW_CLOSED` | 422 | O prazo para pedir outro SMS acabou. | Compre um número novo. | | `ORDER_NOT_RETRIABLE` | 409 | Este pedido não aceita outro SMS. | Ele já fechou. | | `ORDER_NOT_CONFIRMABLE` | 409 | Este pedido não pode ser concluído. | Só pedido com código pode. | ## Leve para a sua ferramenta As duas saem do mesmo código da loja: nunca ficam para trás da documentação. - [Coleção do Postman](https://smsvex.com/desenvolvedores/postman.json) - [OpenAPI (Insomnia, Swagger)](https://smsvex.com/desenvolvedores/openapi.json)