v1 API Reference

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

  1. 1Tu backend crea el challenge (POST /v1/challenges/send) con autenticacion segura.
  2. 2El usuario recibe un OTP (email/sms).
  3. 3El frontend hace exchange del OTP para obtener un verificationToken temporal.
  4. 4El frontend verifica el challenge con Bearer token.
  5. 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.

Integridad: si alguien modifica el body o los headers en transito, la firma ya no coincide.
Replay protection: el timestamp evita que un request capturado pueda reenviarse despues (ventana de 5 min).
Server-only: la firma se hace en tu backend, nunca expongas el API Secret en frontend.

Headers requeridos

X-API-Key: <api_key>
X-Signature: sha256=<hex_hmac>
X-Timestamp: <unix_epoch_seconds>
Content-Type: application/json
http

String to Sign

{METHOD}\n{PATH}\n{TIMESTAMP}\n{BODY_SHA256_HEX}
text

Node.js Example

buildHmacHeaders.ts
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'
  };
}
typescript

Validaciones 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/json
http

Endpoints

Referencia completa de los endpoints de la API de Challenges.

POST/v1/challenges/send

Send Challenge

Crea y envia un challenge OTP al usuario. Soporta idempotency via header Idempotency-Key.

HMAC (integradores externos) o JWT (uso admin interno)
Request Body
{
  "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
POST/v1/challenges/exchange

Exchange Code

Intercambia challengeId + code por un verificationToken temporal de corta vida. One-time: el codigo se marca como usado.

Publica (sin HMAC/JWT), protegida por validacion de Origin + rate limits
Request Body
{
  "challengeId": "chg_...",
  "code": "123456"
}
json
POST/v1/challenges/verify

Verify Challenge

Verifica un challenge con token temporal (Bearer) o desde backend firmado (HMAC).

Bearer temporal token (verificationToken) o HMAC
Request Body
{
  "challengeId": "chg_...",
  "code": "123456"
}
json

Para 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.

POST/v1/challenges/validate-assertion

Validate Assertion

Valida en tu backend un assertionToken emitido por /verify. Uso recomendado: operaciones sensibles (fraude, account changes, pagos).

HMAC o JWT
Request Body
{
  "assertionToken": "<payload.signature>"
}
json

Management Endpoints

GET/v1/challengesListado paginado (JWT admin)
POST/v1/challenges/{id}/resendReenviar OTP (HMAC/JWT)
POST/v1/challenges/{id}/cancelCancelar challenge (HMAC/JWT)
GET/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.

Caso de uso
1
Backend

POST /v1/challenges/send

Tu backend crea el challenge OTP con HMAC.

2
Usuario

Recibe codigo OTP

El usuario recibe el codigo por email, SMS o WhatsApp.

3
Frontend

POST /v1/challenges/exchange

Frontend envia challengeId + code y obtiene verificationToken.

4
Frontend

POST /v1/challenges/verify

Frontend verifica el codigo y recibe assertionToken.

5
Backend

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.

ArtefactoEmisorConsumidorRegla clave
stateFrontend / ClienteBackend integradorTratarlo como no confiable (solo correlacion).
contextTokenTrumi send/exchangeLanding / FrontendNo persistir; evitar logs completos.
verificationTokenTrumi exchangeFrontend (Bearer)Solo memoria; nunca localStorage/sessionStorage.
assertionTokenTrumi verifyBackend integradorValidar 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.

Header
Idempotency-Key: <uuid-v4-or-unique-client-key>
http
EscenarioComportamiento
Mismo key + mismo payloadRespuesta cacheada + X-Idempotency-Cached: true
Mismo key + payload distinto409 Idempotency-Key reused with different payload
Request en progreso202 Processing
Sin Idempotency-KeyRequest normal (sin proteccion idempotente)

TTL por defecto: 3600s (IDEMP_TTL_SEC)

Checklist de Seguridad

Firmar requests HMAC solo desde backend.
Nunca exponer apiSecret en frontend/mobile.
Usar HTTPS en todos los ambientes.
Sincronizar reloj del servidor (NTP).
Usar Idempotency-Key en send.
Tratar assertionToken como credencial de corta vida.
Minimizar logging de OTP/tokens y datos sensibles.
Rotar credenciales periodicamente.

Rate Limits

Rate limiting multicapa para proteger el servicio contra abuso.

Capas de limitacion

Por tenant
Por API client (cuando aplica)
Por destinatario (limite diario)
En exchange: por challenge y por origin/IP

Respuesta tipica

{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded",
  "limit": 6,
  "current": 7,
  "resetTime": 1760400300
}
json

Buenas practicas

  • Aplicar exponential backoff.
  • Respetar Retry-After cuando exista.
  • Evitar retries agresivos en send.

Errors

Catalogo completo de errores por endpoint.

StatusError CodeEndpoints
400VALIDATION_ERRORsend, exchange, verify
400INVALID_CALLBACK_URLsend
400UNSUPPORTED_VERIFICATION_METHODexchange, verify
400MISSING_TOKENvalidate-assertion
401UNAUTHORIZEDsend, verify
401INVALID_CODEexchange, verify
401INVALID_TOKENvalidate-assertion
403CODE_ALREADY_USEDexchange
403CHALLENGE_FAILEDexchange, verify
403CORS_ORIGIN_NOT_ALLOWEDexchange
403CHALLENGE_NOT_VERIFIEDvalidate-assertion
404MISSING_CHALLENGEexchange, verify
404CHALLENGE_NOT_FOUNDvalidate-assertion
410CHALLENGE_EXPIREDexchange, verify
429RATE_LIMIT_EXCEEDEDsend, exchange
500INTERNAL_ERRORsend

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 1 - Send challenge
Paso 2 - Completar landing
Paso 3 - Validate assertion

Paso recomendado: ejecuta Send para generar challengeId/contextToken.

No bloquea pasos: es solo una referencia visual.

1

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.

POST/v1/challenges/send

Headersno van en el body

UUID unico por intento. Generalo con el boton o ingresa uno propio.

↑ Ingresá tus credenciales arriba para probar este endpoint.

2

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-xxxxxxxxxxxx

Nota: si queres redireccion automatica, envia callbackUrl en el paso 1 (send).

3

Validar assertion (Backend)

Tu servidor valida el assertion token para confirmar que el challenge fue completado correctamente. Requiere firma HMAC.

POST/v1/challenges/validate-assertion

↑ Ingresá tus credenciales arriba para probar este endpoint.