Visión general de la API
Cada llamada a la API de CORE-M pasa por un único API gateway. El gateway sirve
gRPC en el puerto 9090 y REST en el puerto 8080, y utiliza
gRPC-Gateway para transcodificar las
peticiones REST en la llamada gRPC equivalente al servicio de backend. Hay un solo
contrato — las definiciones de servicio de protobuf — expuesto de dos maneras.
Esta página documenta el comportamiento transversal que aplica a cada llamada: cómo se autentican, paginan y limitan (rate limit) las peticiones, y cómo se devuelven los errores. Para el catálogo de recursos, consulta las tablas por servicio más abajo y el OpenAPI/Swagger integrado en la aplicación.
REST y gRPC: un contrato, dos superficies
Sección titulada «REST y gRPC: un contrato, dos superficies»| Aspecto | REST | gRPC |
|---|---|---|
| Puerto | 8080 | 9090 |
| Codificación | JSON | Protobuf |
| Enrutamiento | Path + método → método gRPC | Despacho nativo de servicio/método |
| Fuente de verdad | Anotaciones protobuf google.api.http | Definiciones de servicio de protobuf |
| Propagación de metadata | Headers HTTP → metadata gRPC | Metadata nativa |
| Contexto de traza | Headers traceparent / tracestate | Interceptor gRPC de OTel |
Una llamada REST GET /api/v1/devices/{device_id} se transcodifica a la
llamada gRPC DeviceRegistryService.GetDevice contra el device registry; la
respuesta protobuf se serializa de nuevo a JSON. Un cliente gRPC nativo que llama al
mismo método en el puerto 9090 se proxea con propagación completa de metadata y
la respuesta se devuelve sin modificar.
Servicios detrás del gateway
Sección titulada «Servicios detrás del gateway»| Servicio | Propósito | Rutas REST confirmadas |
|---|---|---|
auth | Tenants, login, tokens, API keys, grupos, SSO, MFA, sesiones | POST /api/v1/auth/login, POST /api/v1/auth/refresh, POST /api/v1/auth/validate, POST /api/v1/auth/api-keys — consulta Swagger para el conjunto completo |
device-registry | CRUD de dispositivos, grupos, tags, estado | POST /api/v1/devices, GET /api/v1/devices/{device_id}, GET /api/v1/devices, PATCH/DELETE /api/v1/devices/{device_id} |
device-link | RPC de dispositivos, atributos, provisioning, gateways, OTA | POST /api/v1/provision, POST /api/v1/devices/{device_id}/rpc, GET /api/v1/devices/rpc/{correlation_id}, POST /api/v1/gateway/{gateway_device_id}/connect |
telemetry | Ingesta, snapshot más reciente, consulta histórica, métricas | POST /api/v1/telemetry, GET /api/v1/telemetry/{device_id}/latest, GET /api/v1/telemetry/{device_id} |
rules-engine | CRUD de reglas CEL, habilitar/deshabilitar, validación | POST /api/v1/rules, GET /api/v1/rules/{rule_id}, PATCH/DELETE /api/v1/rules/{rule_id}, POST /api/v1/rules/{rule_id}/enable |
chain-anchor | Estado de lotes (batch), historial de anchors | GET /api/v1/anchors, GET /api/v1/anchors/batches/{batch_id} |
verifier | Pruebas en blockchain verificables públicamente | GET /api/v1/verify/hash/{data_hash_hex}, POST /api/v1/verify/raw, GET /api/v1/verify/batch/{batch_id} |
dashboard-bff | Fan-out en tiempo real al dashboard | WebSocket /ws (no REST) |
gateway | Punto de entrada, auth, rate limiting, callback de ARC, salud | POST /api/v1/callbacks/arc, GET /healthz, GET /readyz |
Autenticación
Sección titulada «Autenticación»Toda petición se autentica excepto los endpoints de salud y readiness
(/healthz, /readyz) y el callback de ARC (/api/v1/callbacks/arc, que se
valida con HMAC en su lugar). El middleware de auth del gateway valida la
credencial llamando al RPC ValidateToken del servicio de auth, extrae
tenant_id y user_id al contexto de la petición y luego la reenvía.
Se aceptan dos tipos de credencial:
| Tipo | Formato | Header | Usado por |
|---|---|---|---|
| JWT access token | JWT firmado con Ed25519 | Authorization: Bearer <jwt> | Usuarios / dashboard |
| API key | sk_live_... | Authorization: Bearer sk_live_... o X-API-Key: sk_live_... | Dispositivos, cuentas de servicio, integraciones |
GET /api/v1/devices HTTP/1.1Host: api.example.com:8080Authorization: Bearer eyJhbGciOiJFZERTQSIs...POST /api/v1/telemetry HTTP/1.1Host: api.example.com:8080Authorization: Bearer sk_live_a3bF7kLm9pQr2sT4uV6wX8yZContent-Type: application/jsonPOST /api/v1/telemetry HTTP/1.1Host: api.example.com:8080X-API-Key: sk_live_a3bF7kLm9pQr2sT4uV6wX8yZContent-Type: application/json| Condición | REST | gRPC |
|---|---|---|
| Credencial válida | la petición continúa | la petición continúa |
| Falta la credencial en una ruta protegida | 401 | UNAUTHENTICATED |
| JWT expirado | 401 (el cuerpo sugiere el endpoint de refresh) | UNAUTHENTICATED |
| El token carece del scope requerido | 403 | PERMISSION_DENIED |
| Endpoint de salud/readiness | permitido sin auth | n/a |
Los JWT access tokens son de corta duración; intercambia un refresh token en
POST /api/v1/auth/refresh para obtener un nuevo par. Los API tokens con scope llevan un
conjunto de scopes explícito — consulta Seguridad y cumplimiento
para la tabla de scopes y el comportamiento de la IP allowlist, y
Auth y multi-tenancy para saber cómo se aplica el
aislamiento de tenant.
Paginación
Sección titulada «Paginación»Todos los endpoints de listado usan paginación por cursor opaco, nunca offsets numéricos.
| Parámetro | Dónde | Significado |
|---|---|---|
page_size | query | Resultados por página. Los valores por defecto varían según el endpoint (comúnmente 50); los endpoints de listado limitan el máximo. |
page_token | query | Cursor opaco del next_page_token de la respuesta anterior. Omítelo para la primera página. |
next_page_token | response | Cursor para la siguiente página. Un valor vacío significa que no hay más resultados. |
# First pagecurl "https://api.example.com:8080/api/v1/devices?page_size=20" \ -H "Authorization: Bearer $TOKEN"
# Next page — pass the cursor back verbatimcurl "https://api.example.com:8080/api/v1/devices?page_size=20&page_token=eyJ..." \ -H "Authorization: Bearer $TOKEN"Rate limiting
Sección titulada «Rate limiting»El gateway aplica un contador de ventana deslizante por tenant, respaldado por un
incremento atómico sobre la clave de Aerospike ratelimit:{tenant_id}:{window_minute}.
La ventana se reinicia automáticamente mediante el TTL de la clave.
- Límite por defecto: 1000 peticiones por minuto por tenant.
- Ventana: un minuto, deslizante.
- Almacenamiento: namespace
sessionsde Aerospike, incremento atómico por petición.
Cada respuesta lleva el estado actual del rate limit:
| Header | Descripción | Presente en |
|---|---|---|
X-RateLimit-Limit | Máximo de peticiones permitidas en la ventana | cada respuesta |
X-RateLimit-Remaining | Peticiones restantes en la ventana actual | cada respuesta |
X-RateLimit-Reset | Segundo del epoch Unix en que se reinicia la ventana (vía TTL) | cada respuesta |
Retry-After | Segundos hasta que la ventana se reinicia | solo en 429 |
Cuando se excede el límite, el gateway devuelve 429 Too Many Requests (gRPC
RESOURCE_EXHAUSTED) con un header Retry-After, e incrementa
corem_gateway_rate_limited_total{tenant=...}.
HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 1000X-RateLimit-Remaining: 0X-RateLimit-Reset: 1711000080Retry-After: 12Content-Type: application/json
{"code": "RESOURCE_EXHAUSTED", "message": "rate limit exceeded"}Formato de errores
Sección titulada «Formato de errores»Los errores se devuelven como un objeto JSON consistente sin importar qué servicio los
produjo. La cadena code es el nombre del código de estado gRPC; el estado HTTP
se deriva de él mediante gRPC-Gateway.
{ "code": "NOT_FOUND", "message": "device not found: dev-770e8400-..."}| Código gRPC | HTTP | Significado |
|---|---|---|
OK | 200 | Éxito |
INVALID_ARGUMENT | 400 | Petición mal formada o inválida |
FAILED_PRECONDITION | 400 | Política de tenant o prerrequisito faltante |
UNAUTHENTICATED | 401 | Credencial faltante o inválida |
PERMISSION_DENIED | 403 | Rol, permiso de grupo o scope insuficiente, o IP no permitida (allowlist) |
NOT_FOUND | 404 | El recurso no existe, o ruta desconocida |
ALREADY_EXISTS | 409 | Recurso duplicado |
RESOURCE_EXHAUSTED | 429 | Rate limit o quota de tenant excedidos |
INTERNAL | 500 | Error del servidor |
UNAVAILABLE | 503 | Servicio temporalmente no disponible |
Rutas desconocidas
Sección titulada «Rutas desconocidas»Una petición a una ruta que el gateway no puede emparejar devuelve 404 con el cuerpo de
error estándar:
HTTP/1.1 404 Not FoundContent-Type: application/json
{"code": "NOT_FOUND", "message": "route not found"}Callback de ARC
Sección titulada «Callback de ARC»POST /api/v1/callbacks/arc es la única ruta pública de la API sin autenticación. Recibe
callbacks de confirmación de transacciones desde la API Merchant de ARC y
reenvía el payload al pipeline de chain-anchor a través de Redpanda. La autenticidad se
establece mediante HMAC, no mediante una credencial bearer.
| Paso | Comportamiento |
|---|---|
| Firma | ARC envía X-ARC-Signature: sha256=<hex> |
| Verificación | El gateway calcula HMAC-SHA256(body, ARC_CALLBACK_HMAC_SECRET) y compara en tiempo constante |
| Si coincide | El payload se publica en Redpanda anchor.tx.confirmed; se devuelve 200 |
| Si no coincide / falta | 401; el payload no se reenvía; se incrementa corem_arc_callback_rejected_total{reason="invalid_signature"} |
| JSON mal formado | 400; el payload se registra para depuración |
| Modo dev | Si ARC_CALLBACK_HMAC_SECRET no está definido y ARC_CALLBACK_SKIP_VALIDATION=true, se omite la validación y se registra una advertencia |
Consulta Visión general del anchoring para saber cómo fluye la confirmación hasta la finalización de la prueba.
Endpoints de salud
Sección titulada «Endpoints de salud»Ambos endpoints no requieren autenticación y están expuestos por el gateway y por cada servicio de backend.
| Endpoint | Sonda | Comportamiento |
|---|---|---|
GET /healthz | Liveness | Siempre 200 mientras el proceso esté vivo |
GET /readyz | Readiness | 200 si todas las dependencias están saludables; 503 si alguna no lo está |
// GET /readyz — 200{"auth": "healthy", "device-registry": "healthy", "telemetry": "healthy", "aerospike": "healthy"}// GET /readyz — 503{"auth": "healthy", "telemetry": "unhealthy: connection refused", "aerospike": "healthy"}/healthz reporta liveness y /readyz reporta readiness: una caída de una dependencia
falla el readiness sin fallar el liveness.
El gateway permite peticiones cross-origin desde el origen del dashboard configurado,
incluyendo las peticiones preflight OPTIONS con los correspondientes
Access-Control-Allow-Origin y las concesiones de método/header. A otros orígenes no se les
otorga acceso CORS.