Verificación OTP

Verificación en dos pasos (OTP): códigos de un solo uso por SMS, WhatsApp o llamada de voz, con una sola integración para los tres canales.

1

Solicitas el código

Tu backend llama a POST /v2/otp con el número del usuario, tu company y tu message. La API genera el código, lo inserta en tu mensaje y lo envía por el canal que elijas (channel: sms, whatsapp o voice) — solo cambias channel.

2

El usuario lo recibe e ingresa

El usuario recibe el código y lo captura en tu app. Por WhatsApp sale por nuestra integración oficial: sin cuenta de WhatsApp Business ni verificación de Meta.

3

Verificas el código

Tu backend llama a POST /v2/otp/verify con el número y el código capturado. Si el status HTTP es 200, el número quedó verificado. No tiene costo.

4

Si tu usuario no recibió el código

El clic en "No recibí el código" de tu app repite el mismo POST /v2/otp con otro channel (por ejemplo voice o whatsapp). Sale el mismo código, la verificación no se reinicia y cuesta lo mismo: un mensaje de tu saldo.

Usar estos métodos no tiene costo extra: solo se descuenta un mensaje de tu saldo cuando la API le envía el código al usuario por SMS, WhatsApp o llamada de voz (POST /v2/otp, ya sea al crear, reenviar o rotar), sin importar el canal. Verificar el código, consultar su estado o invalidarlo nunca tienen costo, sin importar cuántas veces los llames.

Los otros dos endpoints son auxiliares; en un flujo normal no los necesitas:

  • GET /v2/otp/status: solo lectura, para tu UI (contador de reenvío, intentos restantes).
  • DELETE /v2/otp: cancela una verificación en curso (limpieza en tests, cambio de número). No envía nada.

Usa estos endpoints para códigos de login, verificación de cuenta, 2FA y recuperación de contraseña. No construyas un sistema OTP manual sobre /sms/send: perderías la generación, expiración, validación y control de intentos que este flujo ya maneja. Guía completa en Verificación OTP.

Enviar Código de Verificación (OTP)
POST /v2/otp
Verificar Código (OTP)
POST /v2/otp/verify
Consultar Estado de Verificación (OTP)
GET /v2/otp/status
Invalidar Verificación (OTP)
DEL /v2/otp

Enviar Código de Verificación (OTP)

POST https://api.smsmasivos.com.mx/v2/otp
Genera y envía un código de verificación por SMS, WhatsApp o llamada de voz. Este único endpoint maneja todo el ciclo: la primera llamada crea el código, y el botón "No recibí el código" de tu app se resuelve repitiendo la misma llamada, incluso con otro channel: el mismo código sale por el canal nuevo y cubres los números que no reciben SMS. La respuesta siempre te dice qué hizo (action) y qué sigue (next).

Para probar, basta con phone_number, country_code, company y message; el código llega por SMS en segundos. Con code_in_response: true la respuesta incluye el código y puedes automatizar la prueba sin leer el teléfono.

Cuerpo de la solicitud
phone_numberstringrequerido
Número de teléfono a verificar (hasta 10 dígitos, sin código de país). En México siempre son 10 dígitos.
country_codestringrequerido
Código de país, numérico. Para México envía 52.
channelenumopcional

Canal de entrega del código:

  • sms (default): mensaje de texto.
  • whatsapp: mensaje de WhatsApp oficial con plantilla fija (ignora message y company). No requiere cuenta de WhatsApp Business ni verificación de Meta: sale por nuestra integración oficial (remitente Authenticate).
  • voice: llamada que dicta el código dígito por dígito.

En un reenvío puedes cambiar de canal: el mismo código pendiente sale por el canal nuevo (fallback).

smswhatsappvoice
companystringrequerido

Nombre de tu empresa o app. Se inserta en message mediante el placeholder {{company}}.

Si channel es whatsapp, se ignora: la plantilla oficial de WhatsApp es fija (recibes un warning avisando).

messagestringrequerido

Texto que recibe el usuario. Debe incluir los placeholders {{code}} y {{company}}, usar solo caracteres GSM-7 (sin emojis ni acentos raros) y no exceder 160 caracteres ya con el código y la empresa sustituidos.

Un OTP nunca se envía partido en dos SMS: a partir de 161 caracteres la operadora fragmenta el mensaje y eso rompe el autocompletado de códigos del teléfono. Por eso el request falla en vez de enviarse. El límite se evalúa en cada envío con la longitud real del código que va en ese mensaje. En un reenvío se manda el código ya guardado, que puede ser más largo que el code_length de ese request: si creaste con code_length: 10 o con un code_custom largo, dimensiona el texto del reenvío contra esa longitud. El rechazo no altera la verificación en curso.

Ejemplo armado (con code: "483920" y company: "Mi Empresa"): 483920 es tu codigo de verificacion de Mi Empresa. No lo compartas.

Si channel es whatsapp, se ignora: el texto sale por la plantilla oficial fija de WhatsApp (recibes un warning avisando).

code_customstringopcional

Tu propio código, si ya tienes un sistema que los genera y quieres seguir usándolo. Nosotros solo lo enviamos y lo verificamos.

Debe cumplir las mismas reglas que un código generado por nosotros: de 4 a 10 caracteres, solo letras y/o números, sin espacios ni signos.

  • Se guarda y se verifica tal cual lo envías: la comparación distingue mayúsculas de minúsculas, así que si mandas a3f9k2 tu usuario debe capturar exactamente a3f9k2.
  • No se combina con code_format ni code_length (el formato y la longitud los define tu código). Si envías cualquiera de los dos junto con code_custom, responde 400 otp_invalid_param.
  • Si ya hay una verificación pendiente y el código que envías es distinto al vigente, se inicia una ronda nueva (reemplaza el código y reinicia los 7 intentos). Si es el mismo, es un reenvío normal. En ambos casos aplica el cooldown de 30s.
  • Con code_custom, lo que decide si se rota es el código, no code_rotate. Mandar el mismo código no reinicia los intentos aunque agregues code_rotate: true: si la verificación está locked, responde 429 otp_max_attempts. Para obtener 7 intentos nuevos manda un código distinto.
  • Con sandbox: true no necesitas code_in_response: true: ya conoces el código.
  • Si el envío falla (402 otp_insufficient_credits o 502 otp_send_failed), el código queda registrado pero no se entregó nada: reintenta el envío, no verifiques.
code_formatenumopcional

Formato del código generado:

  • numeric (default): solo números.
  • alphanumeric: letras y números.
  • letters: solo letras.

No aplica si envías code_custom (el formato lo define tu código).

numericalphanumericletters
code_lengthintegeropcional
Longitud del código (4 a 10 caracteres). Por defecto 6. No aplica si envías code_custom (la longitud la define tu código).
code_rotatebooleanopcional
true descarta el código vigente y genera uno nuevo (nueva ronda con 7 intentos). Sin él, un código vigente se reenvía tal cual. El cooldown de 30s aplica igual.
code_in_responsebooleanopcional

Devuelve el código generado en el campo code de la respuesta:

  • false (default): no devolver.
  • true: devolver (pruebas/automatización del lado del servidor).
sandboxbooleanopcional
Modo prueba. Con true el flujo se ejecuta completo (genera y registra el código) pero el SMS no se entrega y no se descuenta saldo. Como el usuario nunca recibe el mensaje, debes enviar también code_in_response: true para obtener el código en la respuesta; si falta, responde 400 otp_missing_param. La excepción es code_custom: si tú pusiste el código, ya lo conoces y code_in_response no hace falta.
expiration_datestringopcional
Fecha de expiración del código, formato YYYY-MM-DD HH:mm:ss (futura). Por defecto 24 horas; máximo 3 días (si envías más, se ajusta al máximo).

Qué hace cada llamada

El endpoint decide según el estado del número; tú solo repites el POST:

Estado del númeroQué haceHTTPaction
Sin verificación activa (o expirada)Crea y envía un código nuevo.201created
Código vigenteReenvía el mismo código.200resent
Código vigente + code_rotate: trueDescarta el anterior y genera uno nuevo (7 intentos frescos).200restarted
Ya verificadoInicia un ciclo nuevo.201restarted

Entre envíos aplica un cooldown de 30 segundos (con o sin code_rotate); si no esperas, recibes 429 otp_throttled con el tiempo restante.

Dependencias entre parámetros

  • company y message son obligatorios en toda petición, sin importar el canal — mándalos siempre igual y solo cambia channel. message debe incluir {{code}} y {{company}}.
  • channel: whatsapp reemplaza company y message por su plantilla oficial fija (recibes un warning avisando que se ignoraron); en sms y voice se usan tal cual los envías.
  • sandbox: true (modo prueba: no entrega el SMS ni descuenta saldo) requiere code_in_response: true; sin él, 400 otp_missing_param.

code_in_response: true está pensado para pruebas y automatización del lado del servidor. Nunca reenvíes esa respuesta al navegador o a la app del usuario: si el código regresa al mismo cliente que lo va a capturar, el flujo deja de probar la posesión del teléfono.

Cómo leer los errores

Cada error llega con su status HTTP real, un slug estable en error, una sugerencia accionable en hint y los endpoints sugeridos en next:

HTTPerrorCuándo ocurreQué hacer
400otp_missing_param / otp_invalid_paramFalta un parámetro, es inválido, o enviaste uno de la API anterior (voice, whatsapp, template).El hint indica el parámetro exacto.
400otp_invalid_channelchannel no es sms, whatsapp ni voice.Corrige el valor.
400otp_message_invalidmessage sin {{code}}/{{company}}, con caracteres fuera de GSM-7, o que excede 160 caracteres ya expandido.El hint detalla la regla incumplida.
402otp_insufficient_creditsSin saldo para enviar (incluye code: sms_07).Recarga saldo.
429otp_throttledCooldown de reenvío activo.Espera resend_available_in segundos (también en el header Retry-After).
429otp_max_attemptsLa ronda está bloqueada por intentos agotados y no enviaste code_rotate.Rota el código con code_rotate: true.
502otp_send_failedEl canal no pudo entregar el mensaje.Reintenta; si persiste, contacta a soporte con el request_id.

Los errores de autenticación (auth_*) vienen de la capa común de la API y llegan con HTTP 200 y el detalle en el body; consulta Errores y límites.

curl -X POST https://api.smsmasivos.com.mx/v2/otp \
  -H "Content-Type: application/json" \
  -H "apikey: TU_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52",
    "company": "Mi Empresa",
    "message": "{{code}} es tu codigo de verificacion de {{company}}. No lo compartas."
  }'
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.smsmasivos.com.mx/v2/otp');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'apikey: ' . getenv('API_KEY')
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'phone_number' => '5512345678',
    'country_code' => '52',
    'company' => 'Mi Empresa',
    'message' => '{{code}} es tu codigo de verificacion de {{company}}. No lo compartas.'
]));
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo $status . ' ' . $response;
import requests

response = requests.post(
    'https://api.smsmasivos.com.mx/v2/otp',
    headers={
        'Content-Type': 'application/json',
        'apikey': 'TU_API_KEY'
    },
    json={
        'phone_number': '5512345678',
        'country_code': '52',
        'company': 'Mi Empresa',
        'message': '{{code}} es tu codigo de verificacion de {{company}}. No lo compartas.'
    }
)
print(response.status_code, response.json())
const response = await fetch('https://api.smsmasivos.com.mx/v2/otp', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'apikey': process.env.API_KEY
  },
  body: JSON.stringify({
    phone_number: '5512345678',
    country_code: '52',
    company: 'Mi Empresa',
    message: '{{code}} es tu codigo de verificacion de {{company}}. No lo compartas.'
  })
});
const data = await response.json();
console.log(response.status, data);
const axios = require('axios');

const { status, data } = await axios.post(
  'https://api.smsmasivos.com.mx/v2/otp',
  {
    phone_number: '5512345678',
    country_code: '52',
    company: 'Mi Empresa',
    message: '{{code}} es tu codigo de verificacion de {{company}}. No lo compartas.'
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'apikey': process.env.API_KEY
    },
    validateStatus: () => true
  }
);
console.log(status, data);
require 'net/http'
require 'json'

uri = URI('https://api.smsmasivos.com.mx/v2/otp')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.path, {
  'Content-Type' => 'application/json',
  'apikey' => ENV['API_KEY']
})
request.body = {
  phone_number: '5512345678',
  country_code: '52',
  company: 'Mi Empresa',
  message: '{{code}} es tu codigo de verificacion de {{company}}. No lo compartas.'
}.to_json

response = http.request(request)
puts "#{response.code} #{JSON.parse(response.body)}"
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("apikey", "TU_API_KEY");

var payload = new
{
    phone_number = "5512345678",
    country_code = "52",
    company = "Mi Empresa",
    message = "{{code}} es tu codigo de verificacion de {{company}}. No lo compartas."
};

var response = await client.PostAsJsonAsync(
    "https://api.smsmasivos.com.mx/v2/otp",
    payload
);
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"{(int)response.StatusCode} {result}");
OkHttpClient client = new OkHttpClient();

String json = "{"
    + "\"phone_number\":\"5512345678\","
    + "\"country_code\":\"52\","
    + "\"company\":\"Mi Empresa\","
    + "\"message\":\"{{code}} es tu codigo de verificacion de {{company}}. No lo compartas.\""
    + "}";

Request request = new Request.Builder()
    .url("https://api.smsmasivos.com.mx/v2/otp")
    .post(RequestBody.create(json, MediaType.parse("application/json")))
    .addHeader("apikey", "TU_API_KEY")
    .build();

Response response = client.newCall(request).execute();
System.out.println(response.code() + " " + response.body().string());
Response
{
  "success": true,
  "state": "pending",
  "action": "created",
  "message": "Código OTP creado y enviado.",
  "hint": "El código llega en segundos. Reenvía con POST /v2/otp si no llegó.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 7,
  "resend_available_in": 30,
  "warning": "channel=whatsapp usa una plantilla oficial fija; se ignoraron message/company.",
  "code": "483920",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": true,
  "state": "pending",
  "action": "created",
  "message": "Código OTP creado y enviado.",
  "hint": "El código llega en segundos. Reenvía con POST /v2/otp si no llegó.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 7,
  "resend_available_in": 30,
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": false,
  "state": "pending",
  "error": "otp_incorrect_code",
  "message": "El código es incorrecto.",
  "hint": "Código incorrecto. Te quedan 3 intentos.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 3,
  "resend_available_in": 18,
  "code": "sms_07",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": false,
  "state": "none",
  "error": "otp_insufficient_credits",
  "message": "Saldo insuficiente para enviar el código.",
  "hint": "Recarga saldo para enviar el código.",
  "next": [],
  "code": "sms_07"
}
{
  "success": false,
  "state": "pending",
  "error": "otp_incorrect_code",
  "message": "El código es incorrecto.",
  "hint": "Código incorrecto. Te quedan 3 intentos.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 3,
  "resend_available_in": 18,
  "code": "sms_07",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": false,
  "error": "otp_internal",
  "message": "Ocurrió un error inesperado.",
  "hint": "Intenta de nuevo en unos momentos.",
  "next": []
}
{
  "success": false,
  "state": "none",
  "error": "otp_send_failed",
  "message": "No se pudo enviar el código. Intenta de nuevo.",
  "hint": "No se pudo enviar el código. Intenta de nuevo.",
  "next": []
}

Verificar Código (OTP)

POST https://api.smsmasivos.com.mx/v2/otp/verify
Comprueba el código que capturó el usuario. Si coincide, el número queda verificado (state: verified). Cada código admite 7 intentos; al agotarlos la verificación se bloquea (locked) hasta rotar el código con POST /v2/otp y code_rotate: true.

Llama a este endpoint cuando el usuario capture el código que recibió. Envía el mismo phone_number y country_code que usaste al crear el código: si el status HTTP es 200, el número quedó verificado.

Cuerpo de la solicitud
phone_numberstringrequerido
El mismo número usado al enviar el código.
country_codestringrequerido
El mismo código de país usado al enviar. Para México 52.
codestringrequerido
El código que capturó el usuario.

Intentos y bloqueo

Cada código admite 7 intentos de verificación. Al agotarlos, la verificación pasa a locked (HTTP 429) y la única salida es generar un código nuevo con POST /v2/otp y code_rotate: true. Muestra attempts_remaining al usuario para que sepa cuántos intentos le quedan, y no implementes reintentos automáticos.

Cómo leer los errores

El status HTTP distingue cada caso; puedes ramificar tu código directamente sobre él:

HTTPerrorCuándo ocurreQué hacer
401otp_incorrect_codeEl código no coincide (quedan intentos).Permite reintentar; la respuesta trae attempts_remaining.
404otp_not_foundNo hay verificación activa para el número.Inicia el flujo con POST /v2/otp.
409otp_already_verifiedEl número ya estaba verificado.Trátalo como éxito, o invalida con DELETE /v2/otp para un nuevo ciclo.
410otp_code_expiredEl código venció.Genera uno nuevo con POST /v2/otp.
429otp_max_attemptsSe agotaron los 7 intentos de la ronda.Rota el código: POST /v2/otp con code_rotate: true.
400otp_missing_param / otp_invalid_paramFalta code, phone_number o country_code, o enviaste un parámetro que no aplica aquí.El hint indica el parámetro exacto.

Los errores de autenticación (auth_*) vienen de la capa común de la API y llegan con HTTP 200 y el detalle en el body; consulta Errores y límites.

curl -X POST https://api.smsmasivos.com.mx/v2/otp/verify \
  -H "Content-Type: application/json" \
  -H "apikey: TU_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52",
    "code": "483920"
  }'
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.smsmasivos.com.mx/v2/otp/verify');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'apikey: ' . getenv('API_KEY')
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'phone_number' => '5512345678',
    'country_code' => '52',
    'code' => '483920'
]));
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo $status . ' ' . $response;
import requests

response = requests.post(
    'https://api.smsmasivos.com.mx/v2/otp/verify',
    headers={
        'Content-Type': 'application/json',
        'apikey': 'TU_API_KEY'
    },
    json={
        'phone_number': '5512345678',
        'country_code': '52',
        'code': '483920'
    }
)
if response.status_code == 200:
    print('Número verificado')
else:
    print(response.status_code, response.json())
const response = await fetch('https://api.smsmasivos.com.mx/v2/otp/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'apikey': process.env.API_KEY
  },
  body: JSON.stringify({
    phone_number: '5512345678',
    country_code: '52',
    code: '483920'
  })
});
const data = await response.json();
console.log(response.status, data);
const axios = require('axios');

const { status, data } = await axios.post(
  'https://api.smsmasivos.com.mx/v2/otp/verify',
  {
    phone_number: '5512345678',
    country_code: '52',
    code: '483920'
  },
  {
    headers: {
      'Content-Type': 'application/json',
      'apikey': process.env.API_KEY
    },
    validateStatus: () => true
  }
);
console.log(status, data);
require 'net/http'
require 'json'

uri = URI('https://api.smsmasivos.com.mx/v2/otp/verify')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.path, {
  'Content-Type' => 'application/json',
  'apikey' => ENV['API_KEY']
})
request.body = {
  phone_number: '5512345678',
  country_code: '52',
  code: '483920'
}.to_json

response = http.request(request)
puts "#{response.code} #{JSON.parse(response.body)}"
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("apikey", "TU_API_KEY");

var payload = new
{
    phone_number = "5512345678",
    country_code = "52",
    code = "483920"
};

var response = await client.PostAsJsonAsync(
    "https://api.smsmasivos.com.mx/v2/otp/verify",
    payload
);
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"{(int)response.StatusCode} {result}");
OkHttpClient client = new OkHttpClient();

String json = "{"
    + "\"phone_number\":\"5512345678\","
    + "\"country_code\":\"52\","
    + "\"code\":\"483920\""
    + "}";

Request request = new Request.Builder()
    .url("https://api.smsmasivos.com.mx/v2/otp/verify")
    .post(RequestBody.create(json, MediaType.parse("application/json")))
    .addHeader("apikey", "TU_API_KEY")
    .build();

Response response = client.newCall(request).execute();
System.out.println(response.code() + " " + response.body().string());
Response
{
  "success": true,
  "state": "verified",
  "action": "verified",
  "message": "Código verificado correctamente.",
  "hint": "",
  "next": [],
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": false,
  "error": "otp_missing_param",
  "message": "Falta un parámetro obligatorio.",
  "hint": "Envía 'code' (el código que ingresó el usuario).",
  "next": []
}
{
  "success": false,
  "state": "pending",
  "error": "otp_incorrect_code",
  "message": "El código es incorrecto.",
  "hint": "Código incorrecto. Te quedan 3 intentos.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "attempts_remaining": 3
}
{
  "success": false,
  "state": "none",
  "error": "otp_not_found",
  "message": "No hay una verificación activa para este número.",
  "hint": "No hay una verificación activa. Inícia con POST /v2/otp.",
  "next": [
    "/v2/otp"
  ]
}
{
  "success": false,
  "state": "verified",
  "error": "otp_already_verified",
  "message": "Este número ya está verificado.",
  "hint": "Este número ya está verificado.",
  "next": []
}
{
  "success": false,
  "state": "expired",
  "error": "otp_code_expired",
  "message": "El código OTP expiró.",
  "hint": "El código expiró. Solicita uno nuevo con POST /v2/otp.",
  "next": [
    "/v2/otp"
  ]
}
{
  "success": false,
  "state": "locked",
  "error": "otp_max_attempts",
  "message": "Se superó el máximo de intentos de verificación.",
  "hint": "Se agotaron los intentos. Genera un código nuevo con POST /v2/otp (code_rotate=true).",
  "next": [
    "/v2/otp"
  ],
  "attempts_remaining": 0
}
{
  "success": false,
  "error": "otp_internal",
  "message": "Ocurrió un error inesperado.",
  "hint": "Intenta de nuevo en unos momentos.",
  "next": []
}

Consultar Estado de Verificación (OTP)

GET https://api.smsmasivos.com.mx/v2/otp/status
Devuelve el estado actual de la verificación de un número sin efectos secundarios: no envía mensajes, no consume intentos, no reinicia nada. Úsalo para decidir qué mostrar en tu UI (habilitar el botón de reenvío, mostrar intentos restantes, etc.).

Consulta de solo lectura: no envía mensajes, no consume intentos, no altera el estado. Ideal para construir tu UI de verificación:

  • resend_available_in te dice cuándo habilitar el botón de "Reenviar código" (0 = ya disponible).
  • attempts_remaining te dice cuántos intentos mostrarle al usuario.
  • state te dice qué pantalla corresponde: captura de código (pending), éxito (verified), pedir código nuevo (expired, locked) o iniciar flujo (none).

Los parámetros van como query string (es un GET).

Parámetros de consulta
phone_numberstringrequerido
Número cuyo estado quieres consultar.
country_codestringrequerido
Código de país, numérico. Para México 52.

Los errores de autenticación (auth_*) vienen de la capa común de la API y llegan con HTTP 200 y el detalle en el body; consulta Errores y límites.

curl -X GET "https://api.smsmasivos.com.mx/v2/otp/status?phone_number=5512345678&country_code=52" \
  -H "apikey: TU_API_KEY"
import requests

response = requests.get(
    'https://api.smsmasivos.com.mx/v2/otp/status',
    headers={'apikey': 'TU_API_KEY'},
    params={
        'phone_number': '5512345678',
        'country_code': '52'
    }
)
print(response.json())
const params = new URLSearchParams({
  phone_number: '5512345678',
  country_code: '52'
});
const response = await fetch(
  `https://api.smsmasivos.com.mx/v2/otp/status?${params}`,
  { headers: { apikey: process.env.API_KEY } }
);
const data = await response.json();
console.log(data);
Response
{
  "success": true,
  "state": "pending",
  "message": "Estado actual del OTP.",
  "hint": "Hay un código activo. Verifícalo con POST /v2/otp/verify o reenvíalo con POST /v2/otp.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 5,
  "resend_available_in": 12,
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": false,
  "error": "otp_missing_param",
  "message": "Falta un parámetro obligatorio.",
  "hint": "Envía 'phone_number' (hasta 10 dígitos).",
  "next": []
}
{
  "success": false,
  "error": "otp_internal",
  "message": "Ocurrió un error inesperado.",
  "hint": "Intenta de nuevo en unos momentos.",
  "next": []
}

Invalidar Verificación (OTP)

DEL https://api.smsmasivos.com.mx/v2/otp
Invalida la verificación activa del número (el código vigente deja de servir). No envía ningún mensaje. Útil para limpiar el estado antes de un nuevo ciclo o al dar de baja un flujo.

Este endpoint no envía ningún mensaje. Invalida el código vigente y borra el estado de verificación del número, dejándolo en none. El siguiente POST /v2/otp inicia un ciclo limpio.

Casos típicos: cancelar un flujo a petición del usuario, limpiar el estado tras un cambio de número, o resetear escenarios en tus pruebas automatizadas.

Cuerpo de la solicitud
phone_numberstringrequerido
Número cuya verificación quieres invalidar.
country_codestringrequerido
Código de país, numérico. Para México envía 52.

Si no había verificación activa, la respuesta sigue siendo 200 con action: deleted. La operación es idempotente desde el punto de vista de tu integración: después de llamarla, el estado del número siempre es none.

Los errores de autenticación (auth_*) vienen de la capa común de la API y llegan con HTTP 200 y el detalle en el body; consulta Errores y límites.

curl -X DELETE https://api.smsmasivos.com.mx/v2/otp \
  -H "Content-Type: application/json" \
  -H "apikey: TU_API_KEY" \
  -d '{
    "phone_number": "5512345678",
    "country_code": "52"
  }'
import requests

response = requests.delete(
    'https://api.smsmasivos.com.mx/v2/otp',
    headers={
        'Content-Type': 'application/json',
        'apikey': 'TU_API_KEY'
    },
    json={
        'phone_number': '5512345678',
        'country_code': '52'
    }
)
print(response.status_code, response.json())
const response = await fetch('https://api.smsmasivos.com.mx/v2/otp', {
  method: 'DELETE',
  headers: {
    'Content-Type': 'application/json',
    'apikey': process.env.API_KEY
  },
  body: JSON.stringify({
    phone_number: '5512345678',
    country_code: '52'
  })
});
const data = await response.json();
console.log(response.status, data);
Response
{
  "success": true,
  "state": "none",
  "action": "deleted",
  "message": "El OTP pendiente fue invalidado.",
  "hint": "No se envió ningún mensaje.",
  "next": [
    "/v2/otp"
  ],
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}
{
  "success": false,
  "error": "otp_missing_param",
  "message": "Falta un parámetro obligatorio.",
  "hint": "Envía 'phone_number' (hasta 10 dígitos).",
  "next": []
}
{
  "success": false,
  "error": "otp_internal",
  "message": "Ocurrió un error inesperado.",
  "hint": "No se pudo invalidar el OTP. Intenta de nuevo.",
  "next": []
}

Objeto de respuesta

Estructura de la respuesta que devuelven estos endpoints.

Atributos
successboolean
stateenum
Estado de la verificación después del envío. Siempre pending: hay un código activo esperando verificación.pending
actionenum

Qué hizo la API con tu solicitud:

  • created: primera verificación para el número (o la anterior ya había expirado). HTTP 201.
  • resent: reenvió el mismo código vigente. HTTP 200.
  • restarted: generó un código nuevo e inició otra ronda (con code_rotate: true, o tras una verificación completada). HTTP 200 o 201.
createdresentrestarted
messagestring
Descripción del resultado en español.
hintstring
Sugerencia accionable sobre el siguiente paso.
nextarray<string>
Endpoints sugeridos como siguiente paso del flujo.
expires_atstring
Fecha de expiración del código (YYYY-MM-DD HH:mm:ss, hora del centro de México).
attempts_remaininginteger
Intentos de verificación disponibles para este código (máximo 7 por ronda).
resend_available_ininteger
Segundos que deben pasar antes de poder reenviar o rotar el código (cooldown de 30s).
warningstring
Solo cuando aplica: aviso no bloqueante, por ejemplo al enviar message/company con channel: whatsapp (se ignoran, la plantilla de WhatsApp es fija).
codestring
El código generado. Solo cuando envías code_in_response: true (pruebas/automatización).
request_idstring
UUID para seguimiento de la petición.
Objeto de respuesta
{
  "success": true,
  "state": "pending",
  "action": "created",
  "message": "Código OTP creado y enviado.",
  "hint": "El código llega en segundos. Reenvía con POST /v2/otp si no llegó.",
  "next": [
    "/v2/otp/verify",
    "/v2/otp"
  ],
  "expires_at": "2026-07-21 12:00:00",
  "attempts_remaining": 7,
  "resend_available_in": 30,
  "warning": "channel=whatsapp usa una plantilla oficial fija; se ignoraron message/company.",
  "code": "483920",
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}