Receive SMS online
Compra un número para una verificación SMS única, alquila uno por meses y recibe un webhook en el momento en que llega un código. Todo lo que hace el sitio web, a través de HTTPS.
Documentación
Contenidos
Introducción
La API de SMSZ te da acceso programático a todo lo que hace el sitio web: comprar un número para una verificación SMS única, alquilar un número por días o meses, leer mensajes entrantes y recibir un webhook en el momento en que llega un código.
URL base
https://www.smsz.net/api/v1
Todo es JSON sobre HTTPS. Todos los montos están en USD, todas las marcas de tiempo son ISO 8601 en UTC, y cada compra se carga contra el saldo de tu cuenta — recárgalo desde el sitio web antes de tu primera llamada.
La versión actual de la API es 2026-07-01, devuelta en cada respuesta como el encabezado SMSZ-Version. Los cambios aditivos (nuevos campos, nuevos endpoints, nuevos tipos de eventos) se publican sin cambiar la versión, así que escribe clientes que ignoren campos desconocidos.
Dos productos
| Activaciones | Alquileres | |
|---|---|---|
| Propósito | Un código de verificación | Uso continuo de un número |
| Duración | ~15-20 minutos | 1 día a 12 meses |
| Endpoint | POST /activations | POST /rentals |
| Mensajes | Normalmente uno | Ilimitados durante el período |
Autenticación
Cada solicitud lleva una clave de API como token de portador:
Authorization: Bearer smsz_live_9f2a1c4d_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Crear una clave. Inicia sesión en smsz.net, abre el menú de la cuenta y elige API keys. La clave completa se muestra una sola vez, al crearla — solo almacenamos un hash de ella, así que si la pierdes debes crear una nueva.
Ámbitos. Cada clave lleva una lista explícita de permisos. Otorga solo lo que una integración necesita: un servidor que solo compra números no tiene razón para tener webhooks:write.
account:readactivations:readactivations:writerentals:readrentals:writewebhooks:readwebhooks:write
Una llamada que no tiene el ámbito necesario falla con insufficient_scope y nombra el ámbito faltante.
Lista de IP permitidas. Una clave se puede fijar a una o más direcciones IPv4 o rangos CIDR. Las solicitudes desde cualquier otro lugar se rechazan con ip_not_allowed. Vale la pena hacerlo para claves que viven en un servidor fijo.
Mantener las claves seguras. Las claves son credenciales de portador — cualquiera que tenga una puede gastar tu saldo. Mantenlas en el lado del servidor. Nunca las incluyas en un paquete de navegador, una aplicación móvil o un repositorio público. Si una clave se filtra, revócala en el panel; la revocación tiene efecto inmediato.
Verifica una nueva clave con:
curl https://www.smsz.net/api/v1/ping \
-H "Authorization: Bearer $SMSZ_API_KEY"
Inicio rápido
Comprar un número y leer su código requiere dos llamadas.
1. Compra el número
curl -X POST https://www.smsz.net/api/v1/activations \
-H "Authorization: Bearer $SMSZ_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"country": "US", "service": "telegram"}'
{
"id": "cmg7x2k9a0001l208hq3v7bqz",
"object": "activation",
"status": "pending",
"phone_number": "+12025550147",
"price": 0.62,
"expires_at": "2026-07-24T10:30:03.000Z",
"messages": []
}
Usa phone_number dondequiera que te estés registrando. Tu saldo se debita inmediatamente; si el proveedor no puede completar el pedido, no se te cobra nada.
2. Obtén el código
curl https://www.smsz.net/api/v1/activations/cmg7x2k9a0001l208hq3v7bqz/messages \
-H "Authorization: Bearer $SMSZ_API_KEY"
{
"object": "list",
"data": [
{
"id": "cmg7xb1s70006l208r9y2mnop",
"object": "message",
"sender": "Telegram",
"text": "Telegram code 51284",
"code": "51284",
"received_at": "2026-07-24T10:16:44.000Z"
}
],
"has_more": false,
"total": 1
}
Consulta cada 3-5 segundos hasta que data no esté vacío, o usa un webhook y omite la consulta por completo. code son los dígitos que extrajimos; text es el mensaje completo si la extracción falla.
3. Finaliza
curl -X POST https://www.smsz.net/api/v1/activations/cmg7x2k9a0001l208hq3v7bqz/finish \
-H "Authorization: Bearer $SMSZ_API_KEY"
No es obligatorio, pero libera el número antes. Si nunca llegó un código, finalizar te reembolsa — igual que dejar que la activación expire por sí sola.
Un ejemplo completo
const API = "https://www.smsz.net/api/v1";
const headers = {
Authorization: `Bearer ${process.env.SMSZ_API_KEY}`,
"Content-Type": "application/json",
};
async function getVerificationCode(country, service) {
const created = await fetch(`${API}/activations`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({ country, service }),
});
if (!created.ok) {
const { error } = await created.json();
throw new Error(`${error.code}: ${error.message}`);
}
const activation = await created.json();
console.log("Use this number:", activation.phone_number);
const deadline = Date.parse(activation.expires_at);
while (Date.now() < deadline) {
await new Promise((r) => setTimeout(r, 4000));
const res = await fetch(`${API}/activations/${activation.id}/messages`, { headers });
const { data } = await res.json();
if (data.length > 0) return data[0].code;
}
throw new Error("No SMS arrived before the activation expired");
}
import os, time, uuid, requests
API = "https://www.smsz.net/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['SMSZ_API_KEY']}"
def get_verification_code(country: str, service: str) -> str:
response = session.post(
f"{API}/activations",
json={"country": country, "service": service},
headers={"Idempotency-Key": str(uuid.uuid4())},
)
if not response.ok:
error = response.json()["error"]
raise RuntimeError(f"{error['code']}: {error['message']}")
activation = response.json()
print("Use this number:", activation["phone_number"])
for _ in range(60):
time.sleep(4)
messages = session.get(f"{API}/activations/{activation['id']}/messages").json()
if messages["data"]:
return messages["data"][0]["code"]
raise TimeoutError("No SMS arrived before the activation expired")
Activaciones
Una activación es un número alquilado para una verificación única. Vive aproximadamente 15-20 minutos según el proveedor, y expires_at te dice exactamente cuándo.
Elegir qué comprar. country acepta un código ISO, nuestro slug o el nombre del país — US, united-states y United States funcionan todos. service es un slug de GET /services. Verifica el precio y el stock primero con GET /pricing/activations?country=US&service=telegram.
Omite operator y elegimos el más barato con stock. Omite también provider — fijar un proveedor solo reduce de dónde podemos completar el pedido.
Estado.
| Estado | Significado |
|---|---|
| pending | Activo y esperando un SMS |
| completed | Llegó un mensaje, o lo finalizaste |
| expired | La ventana se cerró sin mensaje — reembolsado |
| cancelled | Lo cancelaste |
| refunded | El cargo fue devuelto |
Los reembolsos son automáticos. Nunca se te cobra por una activación que no recibió nada. Si la ventana se cierra vacía, el saldo vuelve por sí solo. Cancelar antes hace lo mismo más pronto. Una vez que llega un mensaje, la activación ha hecho su trabajo y no es reembolsable.
Errores que vale la pena manejar. insufficient_balance (402) significa recargar. number_unavailable (409) significa que ese par de país y servicio no tiene stock en este momento — prueba otro país, o consulta GET /pricing/activations para ver lo que realmente está disponible.
Alquileres
Un alquiler mantiene un número durante días o meses y recibe mensajes ilimitados durante el período.
Pedido. Los alquileres se compran contra una *oferta* de GET /pricing/rentals, porque la disponibilidad y el precio varían según el país y la duración:
curl "https://www.smsz.net/api/v1/pricing/rentals?country=GB&days=30" \
-H "Authorization: Bearer $SMSZ_API_KEY"
{
"object": "list",
"data": [
{
"offer_id": "o1.Xn9pQ2s.7Kd1fA.k3mZq0vR8tYw",
"country": "GB",
"country_name": "United Kingdom",
"duration_days": 30,
"price": 14.5,
"currency": "USD",
"available": 62
}
]
}
Pasa el offer_id de esa fila directamente — ya fija el país, la duración y el precio:
curl -X POST https://www.smsz.net/api/v1/rentals \
-H "Authorization: Bearer $SMSZ_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"offer_id": "o1.Xn9pQ2s.7Kd1fA.k3mZq0vR8tYw"}'
offer_id es opaco y de corta duración: trátalo como un token para ir y volver, no como un valor para parsear o almacenar. Expira después de 30 minutos, así que obtén ofertas justo antes de pedir en lugar de guardarlas en caché. Un token expirado o alterado se rechaza con invalid_parameter — obtén uno nuevo y reintenta.
Alquileres por servicio. Agregar service alquila un servicio en el número en lugar de todo el número. Más barato cuando solo necesitas una plataforma.
Extender. POST /rentals/{id}/extend con {"days": 30} agrega tiempo y carga tu saldo. Extiende antes de expires_at — un alquiler expirado no se puede revivir, solo reemplazar.
Cancelar. Los alquileres son reembolsables dentro de los 120 minutos posteriores a la compra y solo si no se ha recibido ningún mensaje — esa es la ventana que nos dan nuestros proveedores, así que es la ventana que podemos ofrecer. Fuera de ella, la llamada devuelve not_cancellable.
Mensajes. Usa GET /rentals/{id}/messages para el historial completo, o suscríbete a rental.message.received y recibe cada uno. Los alquileres suelen durar semanas, así que los webhooks son muy preferibles a la consulta aquí.
Webhooks
Los webhooks envían eventos a tu servidor a medida que ocurren, para que no tengas que consultar. Esta es la forma recomendada de consumir la API.
Registrar un endpoint
curl -X POST https://www.smsz.net/api/v1/webhooks/endpoints \
-H "Authorization: Bearer $SMSZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/smsz",
"events": ["activation.message.received", "rental.message.received"]
}'
La respuesta contiene un secret que comienza con whsec_. Esta es la única vez que se devuelve. Guárdalo — es lo que demuestra que una entrega vino de nosotros.
Suscríbete a ["*"] para todo, o usa prefijos como ["rental.*"] para una familia de eventos.
Forma del payload
{
"id": "cmg7xh2k4000cl208a1b2c3d4",
"object": "event",
"type": "activation.message.received",
"api_version": "2026-07-01",
"created": 1784889404,
"data": {
"id": "cmg7x2k9a0001l208hq3v7bqz",
"object": "activation",
"status": "completed",
"phone_number": "+12025550147",
"messages": [
{ "id": "cmg7xb1s70006l208r9y2mnop", "object": "message", "sender": "Telegram", "text": "Telegram code 51284", "code": "51284", "received_at": "2026-07-24T10:16:44.000Z" }
]
}
}
data es el mismo objeto que devuelven los endpoints REST, así que un solo deserializador maneja ambos.
Verificación de firmas
Cada entrega lleva un encabezado SMSZ-Signature:
SMSZ-Signature: t=1784889404,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
v1 es HMAC-SHA256 de {timestamp}.{raw request body}, con tu secreto de endpoint como clave. Verifícalo en el cuerpo crudo, antes de cualquier parseo JSON — re-serializar cambia los bytes y la firma no coincidirá.
import crypto from "node:crypto";
function verifySmszWebhook(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=").map((s) => s.trim()))
);
// Reject old deliveries so a captured payload cannot be replayed later.
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
// Express — note express.raw(), not express.json()
app.post("/hooks/smsz", express.raw({ type: "application/json" }), (req, res) => {
if (!verifySmszWebhook(req.body.toString(), req.get("SMSZ-Signature"), process.env.SMSZ_WEBHOOK_SECRET)) {
return res.status(400).send("bad signature");
}
const event = JSON.parse(req.body.toString());
// Acknowledge first, work afterwards: we retry anything that is not a 2xx.
res.status(200).send("ok");
handleEvent(event).catch(console.error);
});
import hmac, hashlib, time
from flask import Flask, request, abort
def verify_smsz_webhook(raw_body: bytes, signature_header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.strip().split("=", 1) for p in signature_header.split(","))
# Reject old deliveries so a captured payload cannot be replayed later.
if abs(time.time() - int(parts["t"])) > tolerance:
return False
expected = hmac.new(
secret.encode(), f"{parts['t']}.{raw_body.decode()}".encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
@app.post("/hooks/smsz")
def smsz_webhook():
if not verify_smsz_webhook(request.get_data(), request.headers["SMSZ-Signature"], SECRET):
abort(400)
event = request.get_json()
enqueue(event) # acknowledge fast, process out of band
return "", 200
Reglas de entrega
- Cualquier 2xx es un acuse de recibo. Cualquier otra cosa se reintenta.
- Reintentos: 8 intentos a 10s → 30s → 2m → 10m → 30m → 2h → 6h → 12h — poco más de 24 horas en total.
- Las entregas expiran después de 10 segundos, así que acusa recibo inmediatamente y haz el trabajo de forma asíncrona.
- Las redirecciones no se siguen. Si tu URL cambia, actualiza el endpoint.
- Después de 20 fallos consecutivos un endpoint se desactiva. Vuelve a habilitarlo con
PATCH /webhooks/endpoints/{id}y{"status": "active"}. - La entrega es al menos una vez y el orden no está garantizado. Deduplica en el evento
id, y prefiere el estado endataen lugar de inferirlo de la secuencia de eventos.
Depuración. POST /webhooks/endpoints/{id}/test envía un evento real firmado con datos obviamente falsos e informa lo que respondió tu servidor. GET /webhooks/deliveries es el registro de entrega: estado, código de respuesta, número de intentos y próximo reintento.
¿Sin endpoint público? Cada evento también se puede leer desde GET /events. Consúltalo con after configurado al último id de evento que manejaste. Los eventos se conservan durante 30 días.
Tipos de eventos
activation.created— Se compró una activación y su número está listo para recibir SMS.activation.message.received— Llegó un SMS a una activación. El código de verificación extraído está endata.messages[0].codecuando se pudo parsear.activation.completed— Una activación se marcó como finalizada y no recibirá más mensajes.activation.cancelled— Una activación se canceló antes de su uso y el saldo fue reembolsado.activation.expired— Una activación alcanzó su ventana de expiración sin recibir un SMS.activation.refunded— El saldo de una activación fue devuelto a la cuenta.rental.created— Se solicitó un alquiler a largo plazo. Puede que aún se esté aprovisionando.rental.activated— Un alquiler terminó el aprovisionamiento y ahora está activo.rental.message.received— Llegó un SMS a un número de alquiler.rental.extended— Un alquiler fue extendido y su expiración se movió hacia adelante.rental.expiring— Un alquiler expira dentro de 24 horas.rental.expired— Un alquiler llegó al final de su período y dejó de recibir mensajes.rental.cancelled— Un alquiler fue cancelado.balance.updated— El saldo de la cuenta cambió.
Errores
Cada fallo devuelve el mismo sobre con un estado HTTP convencional:
{
"error": {
"type": "invalid_request_error",
"code": "insufficient_balance",
"message": "Insufficient balance. Required: 0.62, Available: 0.10",
"doc_url": "https://www.smsz.net/api#errors",
"request_id": "req_4f1c8a90b2d34e5f6a7b8c9d"
}
}
Rama en code, no en message. Los códigos son estables; el texto no lo es. param nombra el campo infractor cuando el error se refiere a uno.
Cada respuesta — éxito o fallo — lleva un encabezado SMSZ-Request-Id. Regístralo. Citarlo permite que el soporte encuentre la solicitud exacta en segundos.
Qué errores reintentar
| Estado | ¿Reintentar? |
|---|---|
| 400, 401, 402, 403, 404, 422 | No. Corrige la solicitud, la clave o tu saldo. |
| 409 | Solo idempotency_request_in_progress. El resto son conflictos de estado. |
| 429 | Sí, después de Retry-After. |
| 5xx | Sí, con retroceso exponencial — y reutiliza la misma Idempotency-Key. |
Límites de tasa
Los límites son por clave de API, por minuto:
| Cubo | Límite | Se aplica a |
|---|---|---|
| General | 120/min | Lecturas y llamadas de catálogo |
| Compra | 20/min | Crear, cancelar y extender |
| Prueba de webhook | 10/min | POST /webhooks/endpoints/{id}/test |
Cada respuesta lleva tu estado actual:
RateLimit-Limit: 120
RateLimit-Remaining: 117
RateLimit-Reset: 1784889460
Exceder un límite devuelve 429 con Retry-After en segundos. Espera ese tiempo en lugar de reintentar más seguido — golpear un 429 solo lo extiende.
¿Necesitas más? Envía un correo a support@smsz.net con el nombre de tu clave y el volumen esperado; el límite general es ajustable por clave.
Idempotencia
Los fallos de red son ambiguos: una solicitud que expira puede o no haber comprado un número. Envía un Idempotency-Key en cualquier cosa que gaste dinero y reintentar se vuelve seguro.
curl -X POST https://www.smsz.net/api/v1/activations \
-H "Authorization: Bearer $SMSZ_API_KEY" \
-H "Idempotency-Key: 3f9a1b7c-5d2e-4a8f-9c1b-2e7d4a6f8b3c" \
-H "Content-Type: application/json" \
-d '{"country": "US", "service": "telegram"}'
Reintenta con la misma clave y obtienes la respuesta original reproducida, marcada como Idempotent-Replayed: true. Sin segunda compra, sin segundo cargo.
- Usa un UUID nuevo por operación lógica. Las claves se recuerdan durante 24 horas.
- Reutilizar una clave con un cuerpo *diferente* devuelve
idempotency_key_reused(422) — eso casi siempre es un error donde se usó una constante en lugar de un valor por solicitud. - Reintentar mientras el primer intento aún está en ejecución devuelve
idempotency_request_in_progress(409). Espera un momento e inténtalo de nuevo. - Solo se almacenan respuestas exitosas. Una solicitud fallida se puede reintentar con la misma clave.
Compatible con POST /activations, POST /rentals y POST /rentals/{id}/extend.
Paginación
Los endpoints de listado devuelven un envoltorio consistente:
{
"object": "list",
"data": [],
"has_more": true,
"total": 214
}
Pagina con limit (1-100, por defecto 25) y offset:
curl "https://www.smsz.net/api/v1/activations?limit=50&offset=50" \
-H "Authorization: Bearer $SMSZ_API_KEY"
Las listas siempre van de más reciente a más antiguo. GET /events además acepta after=<event id>, que es la forma correcta de consumirlo como un flujo: los offsets se desplazan cuando llegan nuevos eventos, pero un cursor no.
Servidor MCP
Todo lo de esta página también está disponible como servidor Model Context Protocol, de modo que un asistente de IA puede comprar números y leer códigos de verificación en una conversación en lugar de mediante código.
https://www.smsz.net/api/mcp
Es un servidor MCP remoto sobre Streamable HTTP, autenticado con la misma clave de API que esta API: mismos alcances, mismos límites de tasa, mismo saldo. No hay nada que instalar ni nada que ejecutar localmente.
La mayoría de los clientes solo necesitan la URL anterior y una cabecera Authorization: Bearer:
La referencia completa (cada herramienta, instrucciones de conexión para cada cliente y las reglas de seguridad que importan cuando un modelo de lenguaje es quien gasta tu saldo) está en /mcp.
Antes de conectar una clave que pueda comprar cualquier cosa, lee Spending safely. La versión corta: limita la clave al conjunto más reducido que cubra la tarea, porque una herramienta para la que la clave no tiene alcance ni siquiera se muestra al asistente, y esa es la única salvaguarda que no depende de que un modelo se comporte bien.
Para agentes de IA
Si estás conectando un asistente en lugar de escribir un cliente, usa el servidor MCP: expone todo esto como herramientas, con las reglas de seguridad ya escritas en las descripciones de las herramientas.
Descripciones legibles por máquina de esta API:
| Recurso | URL |
|---|---|
| Servidor MCP | /mcp — https://www.smsz.net/api/mcp |
| Especificación OpenAPI 3.1 | /api/v1/openapi.json |
| Estos documentos como un solo archivo Markdown | /api/llms-full.txt |
| Índice breve para contexto de modelo | /llms.txt |
El documento OpenAPI no requiere autenticación, por lo que los generadores de clientes y los agentes pueden leerlo antes de que exista una clave.
Notas para llamadores autónomos
- Lee
GET /pricing/activationsantes de comprar. Los precios y el stock cambian constantemente; no asumas que un par de país y servicio está disponible. - Envía siempre un
Idempotency-Keyen las compras. Si una llamada falla de forma ambigua, reintenta con la *misma* clave en lugar de emitir una compra nueva. - Trata el 402
insufficient_balancecomo terminal: un humano tiene que recargar. No lo reintentes. - Respeta
Retry-Afteren 429. No reintentes errores 4xx que no sean 429. - Una activación cuesta dinero real en cada
POST /activationsexitoso. No hay modo de prueba; no lo llames para "comprobar si funciona": usaGET /pingpara eso.
Endpoints de cuenta
GET/ping
Verifica tu clave de API — Confirma que una clave es válida e informa qué cuenta y alcances lleva. Haz esta tu primera llamada al configurar una integración.
Alcance: account:read
Respuesta:
{
"object": "ping",
"ok": true,
"api_version": "2026-07-01",
"account_email": "you@example.com",
"key_name": "Production server",
"scopes": [
"account:read",
"activations:write"
]
}
GET/account
Recupera tu cuenta — Devuelve tu saldo actual y cuántas activaciones y alquileres están activos.
Respuesta:
{
"object": "account",
"id": "cmg7w1a2b0000l208ff11aaaa",
"email": "you@example.com",
"username": null,
"balance": 42.75,
"currency": "USD",
"created_at": "2026-01-04T08:00:00.000Z",
"active_activations": 1,
"active_rentals": 2
}
GET/transactions
Lista transacciones — Tu libro mayor: compras, reembolsos y depósitos, de más reciente a más antiguo.
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| type | query | no | Filtra por tipo de transacción. |
| status | query | no | Filtra por estado. |
| limit | query | no | Tamaño de página, 1-100. |
| offset | query | no | Filas a omitir. |
Respuesta:
{
"object": "list",
"data": [
{
"id": "cmg7xd4u90008l208p7q1rstu",
"object": "transaction",
"type": "sms_purchase",
"status": "completed",
"amount": -0.62,
"currency": "USD",
"description": "SMS purchase for telegram (US)",
"created_at": "2026-07-24T10:15:03.000Z"
}
],
"has_more": false,
"total": 1
}
Endpoints de catálogo
GET/countries
Lista países — Todos los países en los que vendemos números. Usa code al crear una activación.
Alcance: activations:read
Respuesta:
{
"object": "list",
"data": [
{
"code": "US",
"name": "United States",
"full_name": "United States of America",
"slug": "united-states",
"phone_code": "+1",
"continent": "north_america"
}
],
"has_more": false,
"total": 1
}
GET/services
Lista servicios — Todos los servicios que puedes verificar. Usa slug al crear una activación.
Respuesta:
{
"object": "list",
"data": [
{
"slug": "telegram",
"name": "Telegram",
"full_name": "Telegram Messenger",
"popular": true
}
],
"has_more": false,
"total": 1
}
GET/pricing/activations
Precios de activación en vivo — Precios y stock actuales, consultados en vivo a nuestros proveedores. Debes pasar country, service, o ambos: un barrido sin filtrar cotizaría cada país contra cada servicio.
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| country | query | no | Código de país, slug o nombre, p. ej. US. |
| service | query | no | Slug de servicio, p. ej. telegram. |
Respuesta:
{
"object": "list",
"data": [
{
"country": "US",
"country_name": "United States",
"service": "telegram",
"service_name": "Telegram",
"price": 0.62,
"currency": "USD",
"available": 418,
"success_rate": 92
}
],
"has_more": false,
"total": 1
}
GET/pricing/rentals
Ofertas de alquiler en vivo — Ofertas de alquiler a largo plazo comprables. Pasa el offer_id de una oferta directamente a POST /rentals: es un token opaco y de corta duración que lleva todo lo necesario para completar el pedido.
Alcance: rentals:read
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| country | query | no | Filtra por código de país ISO. |
| days | query | no | Filtra por duración del alquiler en días. |
Respuesta:
{
"object": "list",
"data": [
{
"offer_id": "o1.Xn9pQ2s.7Kd1fA.k3mZq0vR8tYw",
"country": "GB",
"country_name": "United Kingdom",
"duration_days": 30,
"price": 14.5,
"currency": "USD",
"available": 62
}
],
"has_more": false,
"total": 1
}
Endpoints de activaciones
POST/activationsidempotent
Compra una activación — Compra un número para una verificación y debita tu saldo. Si el proveedor no puede completar el pedido, no se cobra nada. Consulta /activations/{id}/messages para el código, o suscríbete a activation.message.received.
Alcance: activations:write · Soporta Idempotency-Key · Límite de tasa de compra
| Campo del cuerpo | Tipo | Requerido | Descripción |
|---|---|---|---|
| country | string | sí | Código de país, slug o nombre, p. ej. US. |
| service | string | sí | Slug de servicio, p. ej. telegram. |
| operator | string | no | Operador opcional. Omítelo para que elijamos el más barato disponible. |
Solicitud:
{
"country": "US",
"service": "telegram"
}
Respuesta:
{
"id": "cmg7x2k9a0001l208hq3v7bqz",
"object": "activation",
"status": "pending",
"phone_number": "+12025550147",
"country": "US",
"country_name": "United States",
"service": "telegram",
"service_name": "Telegram",
"operator": "any",
"price": 0.62,
"currency": "USD",
"created_at": "2026-07-24T10:15:03.000Z",
"expires_at": "2026-07-24T10:30:03.000Z",
"messages": []
}
GET/activations
Lista activaciones — Tus activaciones, de más reciente a más antigua, cada una con los mensajes recibidos.
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| status | query | no | Filtra por estado. |
| limit | query | no | Tamaño de página, 1-100. |
| offset | query | no | Filas a omitir. |
Respuesta:
{
"object": "list",
"data": [
{
"id": "cmg7x2k9a0001l208hq3v7bqz",
"object": "activation",
"status": "pending",
"phone_number": "+12025550147",
"country": "US",
"country_name": "United States",
"service": "telegram",
"service_name": "Telegram",
"operator": "any",
"price": 0.62,
"currency": "USD",
"created_at": "2026-07-24T10:15:03.000Z",
"expires_at": "2026-07-24T10:30:03.000Z",
"messages": []
}
],
"has_more": false,
"total": 1
}
GET/activations/{id}
Recupera una activación — Una activación y sus mensajes.
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| id | path | sí | Id de activación. |
Respuesta:
GET/activations/{id}/messages
Consulta el código — Pregunta directamente al proveedor, de modo que un mensaje que aún no nos ha llegado por webhook igualmente aparece. Consulta cada 3-5 segundos; los webhooks son la mejor integración si puedes alojar un endpoint.
Respuesta:
POST/activations/{id}/cancel
Cancela una activación — Cancela una activación no utilizada y la reembolsa. Una vez que ha llegado un mensaje, la activación ha entregado aquello para lo que se compró, así que la cancelación tiene éxito con refund_amount: 0.
Alcance: activations:write · Límite de tasa de compra
Respuesta:
{
"id": "cmg7x2k9a0001l208hq3v7bqz",
"object": "activation",
"status": "refunded",
"phone_number": "+12025550147",
"country": "US",
"country_name": "United States",
"service": "telegram",
"service_name": "Telegram",
"operator": "any",
"price": 0.62,
"currency": "USD",
"created_at": "2026-07-24T10:15:03.000Z",
"expires_at": "2026-07-24T10:30:03.000Z",
"messages": [],
"refund_amount": 0.62,
"refund_reason": "Full refund - No SMS received"
}
POST/activations/{id}/finish
Finaliza una activación — Cierra una activación una vez que has usado el código, devolviendo el número al proveedor. Si nunca llegó ningún mensaje, finalizar también reembolsa la compra.
Alcance: activations:write
Respuesta:
{
"id": "cmg7x2k9a0001l208hq3v7bqz",
"object": "activation",
"status": "completed",
"phone_number": "+12025550147",
"country": "US",
"country_name": "United States",
"service": "telegram",
"service_name": "Telegram",
"operator": "any",
"price": 0.62,
"currency": "USD",
"created_at": "2026-07-24T10:15:03.000Z",
"expires_at": "2026-07-24T10:30:03.000Z",
"messages": [],
"refund_amount": 0
}
Endpoints de alquileres
POST/rentalsidempotent
Pide un alquiler — Alquila un número por días o meses. Toma un offer_id de GET /pricing/rentals y pásalo de vuelta: la oferta ya fija el país, la duración y el precio.
Alcance: rentals:write · Soporta Idempotency-Key · Límite de tasa de compra
| Campo del cuerpo | Tipo | Requerido | Descripción |
|---|---|---|---|
| offer_id | string | sí | El offer_id de GET /pricing/rentals. Las ofertas caducan a los 30 minutos: obtén una nueva si la tuya es rechazada. |
| service | string | no | Opcional. Alquila un servicio en el número en lugar del número completo, que es más barato. |
| auto_renew | boolean | no | Opcional. Renueva automáticamente al vencimiento, cuando la oferta lo soporte. |
Solicitud:
{
"offer_id": "o1.Xn9pQ2s.7Kd1fA.k3mZq0vR8tYw"
}
Respuesta:
{
"id": "cmg7x9p2r0004l208d1w4kzab",
"object": "rental",
"status": "active",
"phone_number": "+447700900123",
"country": "GB",
"country_name": "United Kingdom",
"service": "full",
"service_name": "Full Rent",
"nickname": null,
"auto_renew": false,
"price": 14.5,
"currency": "USD",
"created_at": "2026-07-24T10:20:11.000Z",
"expires_at": "2026-08-23T10:20:11.000Z",
"messages": []
}
GET/rentals
Lista alquileres — Tus alquileres a largo plazo, de más reciente a más antiguo.
Respuesta:
{
"object": "list",
"data": [
{
"id": "cmg7x9p2r0004l208d1w4kzab",
"object": "rental",
"status": "active",
"phone_number": "+447700900123",
"country": "GB",
"country_name": "United Kingdom",
"service": "full",
"service_name": "Full Rent",
"nickname": null,
"auto_renew": false,
"price": 14.5,
"currency": "USD",
"created_at": "2026-07-24T10:20:11.000Z",
"expires_at": "2026-08-23T10:20:11.000Z",
"messages": []
}
],
"has_more": false,
"total": 1
}
GET/rentals/{id}
Recupera un alquiler — Un alquiler y sus mensajes.
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| id | path | sí | Id de alquiler. |
Respuesta:
GET/rentals/{id}/messages
Lista mensajes de alquiler — Todos los mensajes recibidos en el alquiler, de más reciente a más antiguo. Consulta al proveedor antes de responder.
Respuesta:
POST/rentals/{id}/extendidempotent
Extiende un alquiler — Añade tiempo a un alquiler activo y carga tu saldo. Los alquileres vencidos no se pueden extender: pide uno nuevo.
| Campo del cuerpo | Tipo | Requerido | Descripción |
|---|---|---|---|
| days | integer | sí | Días a añadir, 1-365. |
| auto_renew | boolean | no | Opcional. Renueva automáticamente al vencimiento, cuando el alquiler lo soporte. |
Solicitud:
{
"days": 30
}
Respuesta:
{
"id": "cmg7x9p2r0004l208d1w4kzab",
"object": "rental",
"status": "active",
"phone_number": "+447700900123",
"country": "GB",
"country_name": "United Kingdom",
"service": "full",
"service_name": "Full Rent",
"nickname": null,
"auto_renew": false,
"price": 14.5,
"currency": "USD",
"created_at": "2026-07-24T10:20:11.000Z",
"expires_at": "2026-08-23T10:20:11.000Z",
"messages": [],
"extended_hours": 720,
"amount_charged": 14.5
}
POST/rentals/{id}/cancel
Cancela un alquiler — Cancela y reembolsa un alquiler. Solo es posible dentro de los 120 minutos posteriores a la compra y solo si no se ha recibido ningún mensaje: esa es la ventana que nos dan nuestros proveedores.
Alcance: rentals:write · Límite de tasa de compra
Respuesta:
{
"id": "cmg7x9p2r0004l208d1w4kzab",
"object": "rental",
"status": "refunded",
"phone_number": "+447700900123",
"country": "GB",
"country_name": "United Kingdom",
"service": "full",
"service_name": "Full Rent",
"nickname": null,
"auto_renew": false,
"price": 14.5,
"currency": "USD",
"created_at": "2026-07-24T10:20:11.000Z",
"expires_at": "2026-08-23T10:20:11.000Z",
"messages": [],
"refund_amount": 14.5,
"refund_reason": "Full refund - No SMS received"
}
Endpoints de webhooks
POST/webhooks/endpoints
Crea un endpoint de webhook — Registra una URL HTTPS para recibir eventos. La respuesta contiene el secret de firma: esta es la única vez que se devuelve, así que guárdalo ahora.
Alcance: webhooks:write
| Campo del cuerpo | Tipo | Requerido | Descripción |
|---|---|---|---|
| url | string | sí | URL HTTPS a la que entregar. Debe ser accesible públicamente. |
| events | array | no | Tipos de evento a los que suscribirse. Por defecto ["*"] (todo). |
| description | string | no | Etiqueta opcional, hasta 160 caracteres. |
Solicitud:
{
"url": "https://example.com/hooks/smsz",
"events": [
"activation.message.received",
"rental.message.received"
],
"description": "Production listener"
}
Respuesta:
{
"id": "cmg7xf7w1000al208z3a5vwxy",
"object": "webhook_endpoint",
"url": "https://example.com/hooks/smsz",
"description": "Production listener",
"events": [
"activation.message.received",
"rental.message.received"
],
"status": "active",
"api_version": "2026-07-01",
"secret": "whsec_p9Qk…",
"created_at": "2026-07-24T10:30:00.000Z",
"last_success_at": null,
"last_error_at": null,
"last_error": null
}
GET/webhooks/endpoints
Lista endpoints de webhook — Tus endpoints configurados. Los secretos nunca se incluyen.
Alcance: webhooks:read
Respuesta:
{
"object": "list",
"data": [],
"has_more": false,
"total": 0
}
GET/webhooks/endpoints/{id}
Recupera un endpoint de webhook — Un endpoint, incluido cuándo tuvo su último éxito o fallo.
| Parámetro | En | Requerido | Descripción |
|---|---|---|---|
| id | path | sí | Id de endpoint. |
PATCH/webhooks/endpoints/{id}
Actualiza un endpoint de webhook — Cambia la URL, las suscripciones o la descripción. Envía status: "active" para reactivar un endpoint que deshabilitamos tras fallos repetidos: esto también restablece su contador de fallos.
| Campo del cuerpo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| url | string | no | Nueva URL HTTPS. |
| events | array | no | Lista de suscripción de reemplazo. |
| description | string | no | Nueva etiqueta. |
| status | string | no | Habilitar o deshabilitar el endpoint. Uno de: active, disabled. |
Solicitud:
{
"events": [
"*"
]
}
DELETE/webhooks/endpoints/{id}
Eliminar un endpoint de webhook — Elimina el endpoint y cualquier entrega que aún esté en cola para él.
Respuesta:
{
"id": "cmg7xf7w1000al208z3a5vwxy",
"object": "webhook_endpoint",
"deleted": true
}
POST/webhooks/endpoints/{id}/test
Enviar un evento de prueba — Entrega un evento real y correctamente firmado con datos obviamente falsos, e informa exactamente qué respondió tu endpoint. Úsalo para verificar la comprobación de firmas antes de salir a producción.
Respuesta:
{
"object": "webhook_test",
"endpoint_id": "cmg7xf7w1000al208z3a5vwxy",
"delivered": true,
"response_status": 200,
"duration_ms": 143,
"error": null
}
GET/webhooks/deliveries
Listar entregas — Qué se envió, qué respondió tu endpoint, cuántos intentos tomó y cuándo vence el próximo reintento. Empieza aquí cuando los eventos no llegan.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
| endpoint_id | query | no | Filtrar por un endpoint. |
| status | query | no | Filtrar por estado de entrega. |
| limit | query | no | Tamaño de página, 1-100. |
| offset | query | no | Filas a omitir. |
GET/events
Listar eventos — Cada evento en tu cuenta, tengas o no un endpoint de webhook. Consulta esto con after configurado al último id de evento que procesaste si no puedes alojar un endpoint. Se conservan durante 30 días.
| Parámetro | En | Obligatorio | Descripción |
|---|---|---|---|
| type | query | no | Filtrar por tipo de evento. |
| object_id | query | no | Filtrar por una activación o alquiler. |
| after | query | no | Devolver solo eventos después de este id. |
| limit | query | no | Tamaño de página, 1-100. |
| offset | query | no | Filas a omitir. |
Códigos de error
| Código | HTTP | Significado |
|---|---|---|
| missing_api_key | 401 | No se envió ningún encabezado Authorization. |
| invalid_api_key | 401 | La clave no es reconocida. |
| expired_api_key | 401 | La clave superó su fecha de vencimiento. |
| revoked_api_key | 401 | La clave fue revocada en el panel de control. |
| insufficient_scope | 403 | La clave carece del alcance que este endpoint necesita. |
| ip_not_allowed | 403 | La IP que llama no está en la lista de permitidos de la clave. |
| account_blocked | 403 | La cuenta no puede usar la API. |
| invalid_body | 400 | El cuerpo de la solicitud no era JSON válido. |
| missing_parameter | 400 | Faltaba un campo obligatorio. Ver param\. |
| invalid_parameter | 400 | Un campo no era utilizable. Ver param\. |
| insufficient_balance | 402 | Tu saldo no cubre la compra. |
| resource_not_found | 404 | No existe tal objeto en esta cuenta. |
| number_unavailable | 409 | No hay stock para ese país y servicio en este momento. |
| not_cancellable | 409 | Fuera de la ventana de cancelación, o ya usado. |
| not_extendable | 409 | Esa duración de extensión no se ofrece para este alquiler. |
| resource_conflict | 409 | El objeto no está en un estado que permita esto. |
| idempotency_request_in_progress | 409 | La misma Idempotency-Key sigue en ejecución. Reintenta en breve. |
| idempotency_key_reused | 422 | Esa Idempotency-Key se usó con un cuerpo diferente. |
| rate_limit_exceeded | 429 | Demasiadas solicitudes. Ver Retry-After\. |
| internal_error | 500 | Culpa nuestra. Cita el request\_id\ al soporte. |
| provider_rejected | 502 | Un proveedor upstream rechazó el pedido. |
| provider_unavailable | 503 | Un proveedor upstream está temporalmente caído. |
¿Falta algo?
Envía un correo a support@smsz.net con tu id de solicitud y lo revisaremos. Incluye el encabezado SMSZ-Request-Id de la respuesta fallida: apunta directamente a la solicitud en nuestros registros.