apumail / blog

Verificación por email y SMS para agentes de IA

Al agente no le importa si el código llegó por SMTP o por la red de un operador. Le importa que son seis dígitos y que tiene noventa segundos.

14 de julio de 2026

La verificación se divide más o menos por la mitad. Las herramientas para desarrolladores y el SaaS suelen enviar un código por email. Todo lo que toca dinero, identidad o una cuenta atada a un teléfono envía un SMS. Un agente que solo puede hacer una de las dos se traba en la mitad de internet.

La respuesta habitual son dos proveedores: una API de email y una API de SMS. Eso son dos modelos de autenticación, dos formatos de respuesta, dos estrategias de sondeo, dos modos de falla y un montón de pegamento cuyo único trabajo es hacer que se vean iguales para el agente. El pegamento es el problema: es la parte que nadie quiere mantener y que todos tienen que escribir.

La misma forma, ambos canales

apumail pasa los dos por una sola capa. El flujo de email:

# provision an inbox — no auth
INBOX=$(curl -sS -X POST https://api.apumail.com/api/v1/inboxes)
ADDR=$(echo "$INBOX" | jq -r .address)
TOK=$(echo "$INBOX" | jq -r .token)

# block until it lands, read the extracted code
curl -sS -H "Authorization: Bearer $TOK" \
  "https://api.apumail.com/api/v1/inbox/$ADDR/wait?timeout=120" \
  | jq -r '.messages[0].otp'

Y el flujo de SMS:

# buy a real dedicated number, on demand
PHONE=$(curl -sS -X POST https://api.apumail.com/api/v1/phones \
  -H "Authorization: Bearer $TOK" \
  -d '{"country_code":"US"}')

# same wait, same extract, different channel

Crear, esperar, extraer. Los mismos verbos, la misma autenticación, el mismo JSON. En MCP la simetría es literal: wait_for_mail / extract_latest_otp junto a wait_for_sms / extract_latest_sms_otp. Un agente que aprendió uno aprendió el otro.

Números bajo demanda

Lo que hace incómodo el SMS para agentes en otros lugares es la adquisición. La mayoría de los flujos asumen un humano: elegir un número de un pool, esperar disponibilidad, escribirle a soporte para una región nueva. Un solo POST /api/v1/phones con un country_code compra un número dedicado real en el acto: sin pool que esperar, sin humano a quien escribirle. Es el número del agente mientras esté pagado, no uno compartido en el que caen los códigos de otro.

Ese también es el trade-off honesto: es un plan pago. Un número cuesta desde $20/año en EE. UU./Canadá, con precio por país ($16 en $PROWL), y enviar cuesta $0.07/SMS desde crédito prepago. Las bandejas de email son gratis; los números de teléfono cuestan dinero porque los operadores cobran. Si solo necesitas códigos por email, nunca tocas esto y nunca pagas.

Enviar, no solo recibir

Ambos canales son bidireccionales, y eso importa más de lo que parece. La verificación no siempre es un código: a veces es responder para confirmar, y a veces el flujo que estás automatizando sigue por el mismo canal después del código. Un agente que solo puede recibir tiene que pasarle el control a un humano en la primera respuesta. send_mail y send_sms lo mantienen en el ciclo, con el from fijado a la dirección o número del propio agente para que un token filtrado no sirva para suplantar a nadie.

En qué quedas

Un solo proveedor para un problema que suele ser doble no es una virtud en sí misma: es una virtud cuando las dos mitades realmente comparten forma, y aquí la comparten. Empieza con el flujo de email, que es gratis y requiere un solo POST. Agrega un número cuando un flujo lo exija.

Pruébalo, sin registro

Un solo POST sin autenticación devuelve una dirección funcional y su token.

curl -sS -X POST https://api.apumail.com/api/v1/inboxes