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",
  "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 el challenge usando el verificationToken en Authorization: Bearer.

Bearer temporal token (verificationToken)
Request Body
{
  "code": "123456"
}
json

Para verificationType=code (default), code es requerido.

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)
GET/v1/challenges/{id}501 Not Implemented
POST/v1/challenges/{id}/cancel501 Not Implemented

Flows

Flujo recomendado de integracion: Backend + Frontend + Assertion.

1
Backend

POST /v1/challenges/send

Tu backend crea el challenge con HMAC + Idempotency-Key

2
Usuario

Recibe OTP

El usuario recibe el codigo por email, SMS o WhatsApp

3
Frontend

POST /v1/challenges/exchange

Frontend envia challengeId + code para obtener verificationToken

4
Frontend

POST /v1/challenges/verify

Frontend verifica con Bearer token y recibe assertionToken

5
Frontend

Envia assertionToken

Frontend envia el assertionToken a tu backend

6
Backend

POST /v1/challenges/validate-assertion

Backend valida el assertionToken y crea sesion en tu sistema

Security

Mecanismos de idempotencia, proteccion anti-replay y checklist de seguridad.

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

Ejemplo end-to-end rapido con cURL para probar el flujo completo.

1

Send (HMAC + Idempotency)

1. Create Challenge
curl -X POST "https://api.example.com/v1/challenges/send" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: sha256=$SIG" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"channel":"email","destination":"user@example.com","purpose":"login"}'
bash
2

Exchange (Frontend)

2. Exchange OTP
curl -X POST "https://api.example.com/v1/challenges/exchange" \
  -H "Content-Type: application/json" \
  -H "Origin: https://verify.example.com" \
  -d '{"challengeId":"chg_...","code":"123456"}'
bash
3

Verify (Bearer Token)

3. Verify Challenge
curl -X POST "https://api.example.com/v1/challenges/verify" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VERIFICATION_TOKEN" \
  -d '{"code":"123456"}'
bash
4

Validate Assertion (Backend)

4. Validate Assertion
curl -X POST "https://api.example.com/v1/challenges/validate-assertion" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: sha256=$SIG" \
  -d '{"assertionToken":"'$ASSERTION_TOKEN'"}'
bash