Challenges API
Envia codigos de verificacion por email, SMS o WhatsApp a cualquier dispositivo del mundo con maxima entrega, cero spam y los mejores precios del mercado.
Seguro
HMAC server-to-server, tokens temporales y comparacion en tiempo constante.
RESTful
Endpoints claros y predecibles con JSON, idempotency y rate limits integrados.
Rapido
Flujo separado backend/frontend que minimiza latencia y evita replay/tampering.
Arquitectura del flujo
- 1Tu backend crea el challenge (POST /v1/challenges/send) con autenticacion segura.
- 2El usuario recibe un OTP (email/sms).
- 3El frontend hace exchange del OTP para obtener un verificationToken temporal.
- 4El frontend verifica el challenge con Bearer token.
- 5El backend valida el assertionToken recibido y crea sesion en tu sistema.
Authentication
Dos metodos de autenticacion soportados segun el contexto de la llamada.
HMAC (Server-to-Server)
Para llamadas desde tu backend. Requiere firmar cada request con tu API secret.
Que es HMAC y por que lo usamos?
HMAC (Hash-based Message Authentication Code) es un mecanismo que permite verificar que un request fue enviado por alguien que conoce el API Secret, sin necesidad de enviarlo en texto plano. Tu backend firma el contenido del request usando el secret como clave y envia esa firma en un header. El servidor de Trumi recalcula la firma con el mismo secret y si ambas coinciden, el request es autentico.
Headers requeridos
X-API-Key: <api_key>
X-Signature: sha256=<hex_hmac>
X-Timestamp: <unix_epoch_seconds>
Content-Type: application/jsonhttpString to Sign
{METHOD}\n{PATH}\n{TIMESTAMP}\n{BODY_SHA256_HEX}textNode.js Example
import crypto from 'crypto';
function buildHmacHeaders(
method: string,
path: string,
bodyText: string,
apiKey: string,
apiSecret: string
) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const bodyHash = crypto
.createHash('sha256')
.update(bodyText)
.digest('hex');
const stringToSign = `${method}\n${path}\n${timestamp}\n${bodyHash}`;
const signature = crypto
.createHmac('sha256', apiSecret)
.update(stringToSign)
.digest('hex');
return {
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Signature': `sha256=${signature}`,
'Content-Type': 'application/json'
};
}typescriptValidaciones de seguridad
- Ventana de timestamp: +/- 5 minutos.
- Comparacion de firma en tiempo constante.
- Integridad de body via hash SHA-256.
- Secretos cifrados en KMS + envelope encryption.
Bearer Token (Frontend)
Token temporal usado en POST /v1/challenges/verify desde el navegador.
Authorization: Bearer <verificationToken>
Content-Type: application/jsonhttpEndpoints
Referencia completa de los endpoints de la API de Challenges.
/v1/challenges/sendSend Challenge
Crea y envia un challenge OTP al usuario. Soporta idempotency via header Idempotency-Key.
{
"channel": "email",
"destination": "user@example.com",
"purpose": "login",
"verificationType": "code",
"callbackUrl": "https://developers.tu-dominio.com/docs#quickstart",
"state": "opaque-client-state",
"preferredLanguages": ["es", "en"]
}json/v1/challenges/exchangeExchange Code
Intercambia challengeId + code por un verificationToken temporal de corta vida. One-time: el codigo se marca como usado.
{
"challengeId": "chg_...",
"code": "123456"
}json/v1/challenges/verifyVerify Challenge
Verifica un challenge con token temporal (Bearer) o desde backend firmado (HMAC).
{
"challengeId": "chg_...",
"code": "123456"
}jsonPara verificationType=code (default), code es requerido.
Con Bearer auth (verificationToken), challengeId es opcional — ya viene embebido en el token.
Con HMAC, challengeId + code se envian en el body.
Para variantes temporales/magic-link, el backend puede permitir validacion solo con token.
/v1/challenges/validate-assertionValidate Assertion
Valida en tu backend un assertionToken emitido por /verify. Uso recomendado: operaciones sensibles (fraude, account changes, pagos).
{
"assertionToken": "<payload.signature>"
}jsonManagement Endpoints
/v1/challengesListado paginado (JWT admin)/v1/challenges/{id}/resendReenviar OTP (HMAC/JWT)/v1/challenges/{id}/cancelCancelar challenge (HMAC/JWT)/v1/challenges/{id}Detalle de challenge con contextToken (landing)Flows
Flujo recomendado de integracion: Backend + Frontend + Assertion.
Elegi un caso de uso y segui los pasos de arriba hacia abajo. Cada paso indica claramente quien actua: Backend, Frontend o Usuario.
Validar inicio de sesion
Caso recomendado cuando queres verificar identidad antes de crear la sesion del usuario.
POST /v1/challenges/send
Tu backend crea el challenge OTP con HMAC.
Recibe codigo OTP
El usuario recibe el codigo por email, SMS o WhatsApp.
POST /v1/challenges/exchange
Frontend envia challengeId + code y obtiene verificationToken.
POST /v1/challenges/verify
Frontend verifica el codigo y recibe assertionToken.
POST /v1/challenges/validate-assertion
Tu backend valida assertionToken y crea sesion.
Security
Mecanismos de idempotencia, proteccion anti-replay y checklist de seguridad.
Token Model
Que representa cada token/campo, quien lo usa y que controles de seguridad aplicar.
state
Valor opaco para correlacion UX. Nunca se usa para autorizar acciones y siempre se valida en backend.
contextToken
Token de contexto para leer challenge en landing. Vida corta y nunca sirve como sesion de usuario.
verificationToken
Bearer temporal para /verify. Se mantiene solo en memoria del navegador.
assertionToken
Prueba de challenge verificado. Tu backend lo valida en /validate-assertion antes de habilitar la accion.
| Artefacto | Emisor | Consumidor | Regla clave |
|---|---|---|---|
| state | Frontend / Cliente | Backend integrador | Tratarlo como no confiable (solo correlacion). |
| contextToken | Trumi send/exchange | Landing / Frontend | No persistir; evitar logs completos. |
| verificationToken | Trumi exchange | Frontend (Bearer) | Solo memoria; nunca localStorage/sessionStorage. |
| assertionToken | Trumi verify | Backend integrador | Validar siempre en /validate-assertion. |
Controles operativos recomendados
- Referrer-Policy: no-referrer en landing y callback.
- Cache-Control: no-store en respuestas con tokens.
- Sanitizar logs (no registrar tokens completos).
- Validar callbackUrl contra allowlist por tenant.
- Sincronizar reloj de servidores (NTP) para ventanas de expiracion.
Idempotency
Proteccion contra envios duplicados usando el header Idempotency-Key.
Idempotency-Key: <uuid-v4-or-unique-client-key>http| Escenario | Comportamiento |
|---|---|
| Mismo key + mismo payload | Respuesta cacheada + X-Idempotency-Cached: true |
| Mismo key + payload distinto | 409 Idempotency-Key reused with different payload |
| Request en progreso | 202 Processing |
| Sin Idempotency-Key | Request normal (sin proteccion idempotente) |
TTL por defecto: 3600s (IDEMP_TTL_SEC)
Checklist de Seguridad
Rate Limits
Rate limiting multicapa para proteger el servicio contra abuso.
Capas de limitacion
Respuesta tipica
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"limit": 6,
"current": 7,
"resetTime": 1760400300
}jsonBuenas practicas
- Aplicar exponential backoff.
- Respetar Retry-After cuando exista.
- Evitar retries agresivos en send.
Errors
Catalogo completo de errores por endpoint.
| Status | Error Code | Endpoints |
|---|---|---|
| 400 | VALIDATION_ERROR | send, exchange, verify |
| 400 | INVALID_CALLBACK_URL | send |
| 400 | UNSUPPORTED_VERIFICATION_METHOD | exchange, verify |
| 400 | MISSING_TOKEN | validate-assertion |
| 401 | UNAUTHORIZED | send, verify |
| 401 | INVALID_CODE | exchange, verify |
| 401 | INVALID_TOKEN | validate-assertion |
| 403 | CODE_ALREADY_USED | exchange |
| 403 | CHALLENGE_FAILED | exchange, verify |
| 403 | CORS_ORIGIN_NOT_ALLOWED | exchange |
| 403 | CHALLENGE_NOT_VERIFIED | validate-assertion |
| 404 | MISSING_CHALLENGE | exchange, verify |
| 404 | CHALLENGE_NOT_FOUND | validate-assertion |
| 410 | CHALLENGE_EXPIRED | exchange, verify |
| 429 | RATE_LIMIT_EXCEEDED | send, exchange |
| 500 | INTERNAL_ERROR | send |
Quickstart
Probá el flujo completo directamente desde acá. Ingresá tus credenciales y ejecutá cada paso en orden — los requests se firman con HMAC-SHA256 en el servidor.
Cual uso?
Landing hosted por Trumi: queres integrar rapido y delegar la UX OTP en la landing.
SDK/UI propia (Exchange + Verify): queres control total del flujo en tu frontend.
Solo backend (HMAC): queres verificar completamente desde tu servidor.
Como queres implementar la verificacion?
Guia sugerida (soft)
Paso recomendado: ejecuta Send para generar challengeId/contextToken.
No bloquea pasos: es solo una referencia visual.
Enviar challenge (HMAC + Idempotency)
Crea un nuevo challenge y envia el codigo OTP al destinatario por el canal indicado. Este request requiere firma HMAC server-to-server.
/v1/challenges/sendHeadersno van en el body
UUID unico por intento. Generalo con el boton o ingresa uno propio.
↑ Ingresá tus credenciales arriba para probar este endpoint.
Abrir landing de verificacion
La landing hace internamente Exchange + Verify. Al completar, redirige al callbackUrl guardado en send.
URL final
https://landing-otp.itstrumi.com/?challengeId=chg_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&contextToken=ctx_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&tokenContext=ctx_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxNota: si queres redireccion automatica, envia callbackUrl en el paso 1 (send).
Validar assertion (Backend)
Tu servidor valida el assertion token para confirmar que el challenge fue completado correctamente. Requiere firma HMAC.
/v1/challenges/validate-assertion↑ Ingresá tus credenciales arriba para probar este endpoint.