API · v1
ez-recommendations API
Base URL: https://api.ez-recommendations.huggian.com
Quick start
Tres pasos: provisioná una key, emití una venta, consultá recomendaciones.
# 1. emitir venta
curl -X POST https://api.ez-recommendations.huggian.com/v1/ingest/sale \
-H "X-API-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "00000000-0000-0000-0000-000000000001",
"sale_id": "f0a1...-...",
"items": ["aaaa-...","bbbb-...","cccc-..."]
}'
# 2. consultar recomendaciones
curl -X POST https://api.ez-recommendations.huggian.com/v1/recommendations \
-H "X-API-Key: $KEY" \
-d '{"basket":["aaaa-..."],"available":["bbbb-...","dddd-..."],"limit":5}' Autenticación
Header X-API-Key: <plaintext>.
Hash de servidor con argon2id; el prefijo de 8 chars indexa el lookup.
Header X-Admin-Reason requerido
para cualquier PUT /v1/admin/* — queda en el audit log.
Scopes
| Scope | Endpoints |
|---|---|
| ingest | POST /v1/ingest/sale, POST /v1/ingest/reversal |
| read | POST /v1/recommendations, /by-category |
| stocking | POST /v1/stocking-suggestions |
| admin | GET/PUT /v1/admin/* |
Tiers y quotas
| Tier | Ingest/mes | Read/mes | Stocking/mes | Rate/min |
|---|---|---|---|---|
| free | 500 | 100 | 10 | 30 |
| basic | 50.000 | 150.000 | 5.000 | 600 |
| pro | 500.000 | 1.500.000 | 50.000 | 2.500 |
| enterprise | ∞ | ∞ | ∞ | 15.000 |
Quota mensual reset el 1ro de cada mes UTC. Rate limit token-bucket por X-API-Key.
Pre-requisito: ez-recommendations corre por encima de ez-catalog para resolver UUIDs canónicos de productos. Plan Basic de ez-catalog (o superior) es requerido para todos los planes pagos de ez-recommendations.
Errores
RFC 7807 application/problem+json.
{
"type": "about:blank",
"title": "Validation Failed",
"status": 400,
"detail": "basket size 200 exceeds max_basket_size 50"
} POST /v1/ingest/sale [scope: ingest]
Idempotente: duplicado (api_key_id, sale_id, kind='sale') retorna 409.
{
"tenant_id": "uuid",
"sale_id": "uuid",
"items": ["ez-catalog-uuid", ...] // 2..=100
}
→ 202 Accepted { "outbox_id": 1234 } POST /v1/ingest/reversal [scope: ingest]
Devuelve 422 Unprocessable si no existe un sale previamente procesado
para el mismo (api_key_id, sale_id). Forgery-safe: usa los items ORIGINALES, ignora los que mandás.
POST /v1/recommendations [scope: read]
{
"basket": ["uuid", ...],
"available": ["uuid", ...],
"limit": 10
}
→ 200 OK
{
"items": [{ "product_id": "uuid", "score": 0.42 }],
"k_anon_satisfied": true,
"cohort_size": 47
} k_anon_satisfied: false ⟹ cohort gate todavía no clear. UI debe ocultar el rail
o caer a un fallback (brand-siblings, trending) hasta que la red crezca.
POST /v1/recommendations/by-category [scope: read]
Returns groups[] en lugar de items[]. Cada grupo es una categoría
con hasta limit_per_cat productos. Useful para rails diversificados.
{
"basket": ["uuid", ...],
"available": ["uuid", ...],
"limit_per_cat": 3, // default
"max_cats": 5 // default
} POST /v1/stocking-suggestions [scope: stocking]
Productos NO en current_inventory que se co-compran con reference.
"Considerá stockear esto" — dashboard de owner.
{
"reference": ["uuid", ...], // optional
"current_inventory": ["uuid", ...],
"limit": 20
} Endpoints admin [scope: admin]
- GET /v1/admin/config
- PUT /v1/admin/config # header X-Admin-Reason
- GET /v1/admin/metrics
- GET /v1/admin/usage?api_key_id=<uuid>
GET /v1/health [no auth]
Liveness probe simple para orchestrators. Devuelve { "db": "ok" }.