Skip to content

imagy-notifications — API Reference

Endpoints Publicos (via Gateway)

Todos requieren autenticacion JWT.

Envio de Notificaciones

MetodoEndpointRol requeridoDescripcion
POST/api/v1/notifications/sendoperator, systemEnviar notificacion por canal(es) especifico(s) (async)
POST/api/v1/notifications/dispatchoperator, systemEnviar notificacion con resolucion automatica de canales (async)
POST/api/v1/notifications/bulkoperator, systemEnviar notificacion masiva

OTP (One-Time Password)

MetodoEndpointRol requeridoDescripcion
POST/api/v1/notifications/otp/sendoperator, systemEnviar codigo OTP (sync)
POST/api/v1/notifications/otp/verifyoperator, systemVerificar codigo OTP (sync)

Plantillas

MetodoEndpointRol requeridoDescripcion
GET/api/v1/notifications/templatestenant_admin, operatorListar plantillas (paginado)
POST/api/v1/notifications/templatestenant_adminCrear plantilla
GET/api/v1/notifications/templates/{id}tenant_admin, operatorObtener plantilla
PATCH/api/v1/notifications/templates/{id}tenant_adminActualizar plantilla
DELETE/api/v1/notifications/templates/{id}tenant_adminDesactivar plantilla

Logs de Notificaciones

MetodoEndpointRol requeridoDescripcion
GET/api/v1/notifications/logstenant_admin, operatorConsultar notificaciones enviadas (paginado, filtrable)
GET/api/v1/notifications/logs/{id}tenant_admin, operatorDetalle de una notificacion enviada

Configuracion de Canales (Platform Admin)

MetodoEndpointRol requeridoDescripcion
GET/api/v1/notifications/channelsplatform_admin, tenant_adminListar canales configurados
POST/api/v1/notifications/channelsplatform_adminConfigurar canal para tenant
PATCH/api/v1/notifications/channels/{id}platform_adminActualizar configuracion de canal
DELETE/api/v1/notifications/channels/{id}platform_adminDesactivar canal
POST/api/v1/notifications/channels/{id}/testplatform_adminEnviar notificacion de prueba

Endpoints Internos (servicio-a-servicio)

Invocados por otros servicios de la plataforma con JWT propagado y header X-Internal-Service.

MetodoEndpointConsumidorDescripcion
POST/api/v1/notifications/sendImagFlow, ImagLend, ImagSignEnviar notificacion por canal(es) explicito(s)
POST/api/v1/notifications/dispatchImagFlow, ImagLend, ImagSignEnviar con resolucion automatica de canales
POST/api/v1/notifications/otp/sendImagSign, ImagLend, ImagIDEnviar OTP
POST/api/v1/notifications/otp/verifyImagSign, ImagLend, ImagIDVerificar OTP

Ejemplos de Request/Response

Enviar Notificacion — Canal Unico (modo basico)

Envio asincronico de una notificacion usando una plantilla configurada por un canal especifico. El caller indica exactamente que template, canal y destinatario usar.

http
POST /api/v1/notifications/send
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer {jwt}
json
{
  "template_code": "loan_approved",
  "channel": "sms",
  "recipient": "+593991234567",
  "variables": {
    "borrower_name": "Maria Garcia",
    "amount": "5,000.00",
    "currency": "USD",
    "loan_id": "LOAN-2026-001"
  },
  "metadata": {
    "origin_service": "imaglend",
    "origin_reference": "loan-uuid-001"
  }
}

Response 202:

json
{
  "data": {
    "id": "notif-uuid-001",
    "status": "queued",
    "template_code": "loan_approved",
    "channel": "sms",
    "recipient": "+593991234567",
    "queued_at": "2026-05-18T10:30:00Z"
  },
  "metadata": {
    "request_id": "req-abc123",
    "timestamp": "2026-05-18T10:30:00Z"
  }
}

Enviar Notificacion — Multi-Canal (canales explicitos)

El campo channel acepta un string (un canal) o un array (multiples canales). Cuando es array, el servicio busca la plantilla para cada canal y despacha a todos.

http
POST /api/v1/notifications/send
Content-Type: application/json
Idempotency-Key: 770e8400-e29b-41d4-a716-446655440010
Authorization: Bearer {jwt}
json
{
  "template_code": "loan_approved",
  "channel": ["sms", "email", "push"],
  "recipients": {
    "sms": "+593991234567",
    "email": "maria.garcia@email.com",
    "push": "device-token-fcm-xyz"
  },
  "variables": {
    "borrower_name": "Maria Garcia",
    "amount": "5,000.00",
    "currency": "USD",
    "loan_id": "LOAN-2026-001",
    "portal_url": "https://portal.fintech-abc.reimaginetech.io/loans/LOAN-2026-001"
  },
  "metadata": {
    "origin_service": "imaglend",
    "origin_reference": "loan-uuid-001"
  }
}

Response 202:

json
{
  "data": {
    "batch_id": "batch-uuid-010",
    "dispatches": [
      { "id": "notif-uuid-010", "channel": "sms", "status": "queued" },
      { "id": "notif-uuid-011", "channel": "email", "status": "queued" },
      { "id": "notif-uuid-012", "channel": "push", "status": "queued" }
    ],
    "queued_at": "2026-05-18T10:30:00Z"
  },
  "metadata": {
    "request_id": "req-multi-001",
    "timestamp": "2026-05-18T10:30:00Z"
  }
}

Reglas multi-canal:

  • Si no existe plantilla para un canal especificado, ese canal se salta (no falla el request completo)
  • Si el canal no esta activo para el tenant, se salta
  • El response indica el status de cada canal individualmente
  • Cada despacho genera su propio registro en notification_logs

Enviar Notificacion — Dispatch (resolucion automatica de canales)

El endpoint /dispatch es el modo declarativo: el caller dice "notifica a este usuario sobre este evento" y el servicio resuelve automaticamente por que canales enviar, basandose en la configuracion del tenant y las preferencias del destinatario.

http
POST /api/v1/notifications/dispatch
Content-Type: application/json
Idempotency-Key: 880e8400-e29b-41d4-a716-446655440020
Authorization: Bearer {jwt}
json
{
  "template_code": "loan_approved",
  "subject_id": "subj-uuid-001",
  "variables": {
    "borrower_name": "Maria Garcia",
    "amount": "5,000.00",
    "currency": "USD",
    "loan_id": "LOAN-2026-001",
    "portal_url": "https://portal.fintech-abc.reimaginetech.io/loans/LOAN-2026-001"
  },
  "metadata": {
    "origin_service": "imaglend",
    "origin_reference": "loan-uuid-001"
  }
}

Response 202:

json
{
  "data": {
    "dispatch_id": "dispatch-uuid-020",
    "resolved_channels": ["sms", "email"],
    "skipped_channels": {
      "push": "no_device_token",
      "whatsapp": "channel_not_configured"
    },
    "dispatches": [
      { "id": "notif-uuid-020", "channel": "sms", "recipient": "+593991234567", "status": "queued" },
      { "id": "notif-uuid-021", "channel": "email", "recipient": "maria.garcia@email.com", "status": "queued" }
    ],
    "queued_at": "2026-05-18T10:30:00Z"
  },
  "metadata": {
    "request_id": "req-dispatch-001",
    "timestamp": "2026-05-18T10:30:00Z"
  }
}

Logica de resolucion del dispatch:

Resolucion de destinatario por canal:

CanalFuente del destinatario
smssubject.phone o user.phone
emailsubject.email o user.email
whatsappsubject.phone (mismo que SMS)
pushsubject_devices con push_token activo
webhooknotification_channels.config.endpoint_url del tenant

Diferencias entre /send y /dispatch:

Aspecto/send/dispatch
CanalCaller lo especifica (string o array)Servicio lo resuelve automaticamente
DestinatarioCaller lo proporcionaServicio lo busca por subject_id
ControlTotal — el caller decide todoDelegado — el servicio decide canales
Cuando usarIntegraciones especificas, OTP, envios puntualesNotificaciones de negocio (aprobacion, rechazo, mora)
Fallo parcialFalla si el canal no existeSalta canales no disponibles (nunca falla por canal)

Enviar Notificacion por Email

http
POST /api/v1/notifications/send
Content-Type: application/json
Idempotency-Key: 660e8400-e29b-41d4-a716-446655440001
Authorization: Bearer {jwt}
json
{
  "template_code": "loan_approved",
  "channel": "email",
  "recipient": "maria.garcia@email.com",
  "variables": {
    "borrower_name": "Maria Garcia",
    "amount": "5,000.00",
    "currency": "USD",
    "loan_id": "LOAN-2026-001",
    "portal_url": "https://portal.fintech-abc.reimaginetech.io/loans/LOAN-2026-001"
  }
}

Response 202:

json
{
  "data": {
    "id": "notif-uuid-002",
    "status": "queued",
    "template_code": "loan_approved",
    "channel": "email",
    "recipient": "maria.garcia@email.com",
    "queued_at": "2026-05-18T10:31:00Z"
  },
  "metadata": {
    "request_id": "req-def456",
    "timestamp": "2026-05-18T10:31:00Z"
  }
}

Enviar OTP (Sincronico)

Genera un codigo OTP, lo envia por el canal indicado y retorna el identificador para posterior verificacion.

http
POST /api/v1/notifications/otp/send
Content-Type: application/json
Authorization: Bearer {jwt}
X-Internal-Service: imagy-sign
json
{
  "channel": "sms",
  "destination": "+593991234567",
  "purpose": "signature_otp",
  "origin_service": "imagsign",
  "origin_reference": "sr-uuid-001",
  "locale": "es"
}

Response 200:

json
{
  "data": {
    "otp_id": "otp-uuid-001",
    "channel": "sms",
    "masked_destination": "+5939912***67",
    "expires_in_seconds": 300,
    "max_attempts": 3
  },
  "metadata": {
    "request_id": "req-ghi789",
    "timestamp": "2026-05-18T10:32:00Z"
  }
}

Verificar OTP (Sincronico)

Valida el codigo ingresado por el usuario contra el OTP generado.

http
POST /api/v1/notifications/otp/verify
Content-Type: application/json
Authorization: Bearer {jwt}
X-Internal-Service: imagy-sign
json
{
  "otp_id": "otp-uuid-001",
  "code": "847291"
}

Response 200 (verificacion exitosa):

json
{
  "data": {
    "verified": true,
    "otp_id": "otp-uuid-001",
    "verified_at": "2026-05-18T10:33:00Z"
  },
  "metadata": {
    "request_id": "req-jkl012",
    "timestamp": "2026-05-18T10:33:00Z"
  }
}

Response 200 (codigo incorrecto):

json
{
  "data": {
    "verified": false,
    "otp_id": "otp-uuid-001",
    "attempts_remaining": 2,
    "reason": "invalid_code"
  },
  "metadata": {
    "request_id": "req-mno345",
    "timestamp": "2026-05-18T10:33:15Z"
  }
}

Response 200 (OTP expirado):

json
{
  "data": {
    "verified": false,
    "otp_id": "otp-uuid-001",
    "attempts_remaining": 0,
    "reason": "expired"
  },
  "metadata": {
    "request_id": "req-pqr678",
    "timestamp": "2026-05-18T10:40:00Z"
  }
}

Envio Masivo (Bulk)

Envia la misma plantilla a multiples destinatarios. Util para campanas o alertas masivas.

http
POST /api/v1/notifications/bulk
Content-Type: application/json
Idempotency-Key: 770e8400-e29b-41d4-a716-446655440002
Authorization: Bearer {jwt}
json
{
  "template_code": "payment_reminder",
  "channel": "sms",
  "recipients": [
    {
      "destination": "+593991234567",
      "variables": {
        "borrower_name": "Maria Garcia",
        "due_date": "2026-05-25",
        "amount": "250.00"
      }
    },
    {
      "destination": "+573001234567",
      "variables": {
        "borrower_name": "Juan Perez",
        "due_date": "2026-05-25",
        "amount": "180.00"
      }
    }
  ],
  "metadata": {
    "origin_service": "imaglend",
    "batch_id": "batch-2026-05-18"
  }
}

Response 202:

json
{
  "data": {
    "batch_id": "batch-uuid-001",
    "total_recipients": 2,
    "status": "processing",
    "queued_at": "2026-05-18T10:35:00Z"
  },
  "metadata": {
    "request_id": "req-stu901",
    "timestamp": "2026-05-18T10:35:00Z"
  }
}

Listar Plantillas

http
GET /api/v1/notifications/templates?channel=sms&locale=es&page=1&page_size=20
Authorization: Bearer {jwt}

Response 200:

json
{
  "data": [
    {
      "id": "tpl-uuid-001",
      "code": "loan_approved",
      "channel": "sms",
      "locale": "es",
      "subject": null,
      "body": "Hola {{borrower_name}}, tu credito por {{amount}} {{currency}} ha sido aprobado. Ref: {{loan_id}}",
      "variables": ["borrower_name", "amount", "currency", "loan_id"],
      "is_active": true,
      "created_at": "2026-01-15T08:00:00Z",
      "updated_at": "2026-03-10T14:20:00Z"
    },
    {
      "id": "tpl-uuid-002",
      "code": "payment_reminder",
      "channel": "sms",
      "locale": "es",
      "subject": null,
      "body": "Hola {{borrower_name}}, tu pago de {{amount}} vence el {{due_date}}. Evita recargos.",
      "variables": ["borrower_name", "amount", "due_date"],
      "is_active": true,
      "created_at": "2026-01-15T08:00:00Z",
      "updated_at": "2026-01-15T08:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total_items": 2,
    "total_pages": 1
  }
}

Crear Plantilla

http
POST /api/v1/notifications/templates
Content-Type: application/json
Authorization: Bearer {jwt}
json
{
  "code": "loan_disbursed",
  "channel": "email",
  "locale": "es",
  "subject": "Tu credito ha sido desembolsado - {{loan_id}}",
  "body": "<h1>Desembolso Exitoso</h1><p>Hola {{borrower_name}},</p><p>Tu credito por <strong>{{amount}} {{currency}}</strong> ha sido desembolsado a la cuenta {{account_number}}.</p>",
  "variables": ["borrower_name", "amount", "currency", "loan_id", "account_number"]
}

Response 201:

json
{
  "data": {
    "id": "tpl-uuid-003",
    "code": "loan_disbursed",
    "channel": "email",
    "locale": "es",
    "is_active": true,
    "created_at": "2026-05-18T10:40:00Z"
  },
  "metadata": {
    "request_id": "req-vwx234",
    "timestamp": "2026-05-18T10:40:00Z"
  }
}

Actualizar Plantilla

http
PATCH /api/v1/notifications/templates/tpl-uuid-001
Content-Type: application/json
Authorization: Bearer {jwt}
If-Match: "v3"
json
{
  "body": "Hola {{borrower_name}}, tu credito #{{loan_id}} por {{amount}} {{currency}} fue aprobado. Consulta detalles en {{portal_url}}.",
  "variables": ["borrower_name", "amount", "currency", "loan_id", "portal_url"]
}

Response 200:

json
{
  "data": {
    "id": "tpl-uuid-001",
    "code": "loan_approved",
    "channel": "sms",
    "locale": "es",
    "body": "Hola {{borrower_name}}, tu credito #{{loan_id}} por {{amount}} {{currency}} fue aprobado. Consulta detalles en {{portal_url}}.",
    "variables": ["borrower_name", "amount", "currency", "loan_id", "portal_url"],
    "row_version": 4,
    "updated_at": "2026-05-18T10:45:00Z"
  },
  "metadata": {
    "request_id": "req-yza567",
    "timestamp": "2026-05-18T10:45:00Z"
  }
}

Consultar Logs de Notificaciones

http
GET /api/v1/notifications/logs?channel=sms&status=delivered&from=2026-05-01&to=2026-05-18&page=1&page_size=50
Authorization: Bearer {jwt}

Response 200:

json
{
  "data": [
    {
      "id": "notif-uuid-001",
      "template_code": "loan_approved",
      "channel": "sms",
      "recipient": "+593991234567",
      "status": "delivered",
      "provider": "twilio",
      "provider_message_id": "SM1234567890abcdef",
      "sent_at": "2026-05-18T10:30:01Z",
      "delivered_at": "2026-05-18T10:30:03Z",
      "metadata": {
        "origin_service": "imaglend",
        "origin_reference": "loan-uuid-001"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 50,
    "total_items": 1,
    "total_pages": 1
  }
}

Formato de Entrega Webhook

Cuando el canal configurado es webhook, imagy-notifications realiza un HTTP POST al endpoint configurado por el tenant. El formato de entrega es el siguiente:

Request al Endpoint del Tenant

http
POST https://api.cliente.com/webhooks/notifications
Content-Type: application/json
X-Webhook-Signature: sha256=abc123def456...
X-Webhook-Timestamp: 1716030000
X-Webhook-Id: notif-uuid-003
json
{
  "id": "notif-uuid-003",
  "event_type": "notification.sent",
  "timestamp": "2026-05-18T10:30:00Z",
  "data": {
    "template_code": "loan_approved",
    "channel": "webhook",
    "recipient": "https://api.cliente.com/webhooks/notifications",
    "variables": {
      "borrower_name": "Maria Garcia",
      "amount": "5,000.00",
      "currency": "USD",
      "loan_id": "LOAN-2026-001"
    },
    "rendered_body": "Hola Maria Garcia, tu credito por 5,000.00 USD ha sido aprobado. Ref: LOAN-2026-001",
    "metadata": {
      "origin_service": "imaglend",
      "origin_reference": "loan-uuid-001",
      "tenant_id": "tenant-uuid-001"
    }
  }
}

Validacion de Firma (HMAC)

El receptor debe validar la firma HMAC para asegurar la autenticidad del webhook:

signature = HMAC-SHA256(secret, timestamp + "." + body)

El header X-Webhook-Signature contiene el prefijo sha256= seguido del hash calculado. El receptor debe:

  1. Extraer el timestamp de X-Webhook-Timestamp
  2. Verificar que el timestamp no sea mayor a 5 minutos (replay protection)
  3. Calcular HMAC-SHA256(secret, timestamp + "." + raw_body)
  4. Comparar con el valor en X-Webhook-Signature

Politica de Reintentos (Webhook)

IntentoDelayDescripcion
1InmediatoPrimer envio
230 segundosPrimer reintento
32 minutosSegundo reintento
410 minutosTercer reintento (final)

Si todos los intentos fallan, el log se marca como failed y se registra la ultima respuesta del endpoint.

Respuesta Esperada del Receptor

El endpoint del tenant debe responder con status 2xx para confirmar recepcion:

json
{
  "received": true
}

Cualquier respuesta con status 4xx o 5xx se considera fallo y dispara reintento.

Eventos Consumidos (Notificaciones Automaticas)

imagy-notifications consume eventos del bus de mensajes y dispara notificaciones automaticas basadas en la configuracion de plantillas del tenant.

Mapeo Evento-Plantilla

La relacion entre eventos y plantillas se configura por tenant. Cuando llega un evento, el servicio:

  1. Busca plantillas asociadas al event_type para el tenant
  2. Extrae variables del payload del evento
  3. Determina el destinatario del payload
  4. Despacha por los canales configurados

Formato de Evento Consumido

json
{
  "event_id": "evt-uuid-001",
  "event_type": "lend.loan.approved",
  "tenant_id": "tenant-uuid-001",
  "timestamp": "2026-05-18T10:30:00Z",
  "payload": {
    "loan_id": "LOAN-2026-001",
    "borrower_name": "Maria Garcia",
    "borrower_email": "maria.garcia@email.com",
    "borrower_phone": "+593991234567",
    "amount": "5000.00",
    "currency": "USD"
  },
  "metadata": {
    "source": "imaglend",
    "correlation_id": "corr-uuid-001"
  }
}

Headers Especiales

HeaderDireccionDescripcion
Idempotency-KeyRequest (POST)UUID para idempotencia en envios
If-MatchRequest (PATCH)ETag para concurrencia optimista
ETagResponse (GET)Version del recurso
X-Idempotent-ReplayedResponsetrue si es respuesta cacheada
X-Request-IdResponseID de trazabilidad
X-Internal-ServiceRequestIdentifica servicio interno que invoca
X-Webhook-SignatureRequest/Response (webhook)Firma HMAC del payload
X-Webhook-TimestampRequest/Response (webhook)Timestamp para replay protection
X-Webhook-IdResponse (webhook)ID unico del webhook enviado

Codigos de Estado

StatusCuando
200Operacion exitosa (GET, OTP verify)
201Recurso creado (plantilla)
202Notificacion aceptada y encolada
400Validacion fallida (variables faltantes, canal no configurado)
401No autenticado
403Sin permisos para la operacion
404Recurso no encontrado (plantilla, OTP, log)
409Conflicto (plantilla duplicada)
412ETag mismatch (concurrencia)
422Error de procesamiento (proveedor rechazo, destino invalido)
429Rate limit excedido (OTP: max 5 por destino en 15 min)
500Error interno
502Error de comunicacion con proveedor externo

Rate Limiting

OperacionLimiteVentana
OTP send (por destino)5 solicitudes15 minutos
OTP verify (por otp_id)3 intentosVida del OTP
Notifications send (por tenant)100 solicitudes1 minuto
Bulk send (por tenant)10 solicitudes1 minuto
Bulk recipients (por request)1000 destinatariosPor request

Reimagine Tech LLC — Documentacion Interna