Endpoint Reference — /api/v1/
Tutti gli endpoint richiedono autenticazione tramite chiave API (header X-Api-Key o Authorization: Bearer).
L’organizationId non è mai un parametro — viene sempre derivato dalla chiave.
I parametri areaId, dayId e menuItemIds devono appartenere all’evento richiesto. In caso contrario la risposta è 404 Not Found.
Envelope di risposta
Tutte le risposte usano lo stesso wrapper:
{
"data": { ... },
"meta": {
"generatedAt": "2026-06-11T20:00:00Z",
"eventId": "3fa85f64-...",
"dayId": 42
}
}meta.dayId è omesso quando non pertinente. Gli errori usano RFC 7807 (application/problem+json).
I valori monetari sono sempre numerici in EUR: "totalSales": 1234.50 — mai stringhe formattate.
Events
GET /api/v1/events
Scope richiesto: events:read
Restituisce tutti gli eventi dell’organizzazione. Se la chiave è vincolata a un evento specifico, restituisce un array con il solo evento vincolato.
Risposta 200:
{
"data": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Sagra del Borgo 2026",
"startDate": "2026-06-11T18:00:00Z",
"isOpen": true,
"currentDay": {
"id": 42,
"date": "2026-06-11",
"openedAt": "2026-06-11T17:30:00Z"
}
}
],
"meta": { "generatedAt": "2026-06-11T20:00:00Z", "eventId": "00000000-..." }
}currentDay è null se nessun giorno evento è attualmente aperto.
GET /api/v1/events/{eventId}
Scope richiesto: events:read
Restituisce il dettaglio di un singolo evento, incluso il giorno attualmente aperto.
Parametri path:
| Parametro | Tipo | Descrizione |
|---|---|---|
eventId | uuid | ID dell’evento |
Risposta 200: stessa struttura del singolo oggetto in GET /api/v1/events.
Errori:
404— evento inesistente o appartenente a un’altra organizzazione404— chiave vincolata a un evento diverso
Analytics
GET /api/v1/events/{eventId}/analytics/kpis
Scope richiesto: analytics:read
KPI aggregati per il giorno o l’area specificati.
Query parameters:
| Parametro | Tipo | Descrizione |
|---|---|---|
dayId | int | (opzionale) ID del giorno evento. Default: giorno attualmente aperto, o ultimo chiuso. |
areaId | int | (opzionale) Filtra per area. Default: tutti i dati dell’evento. |
Risposta 200:
{
"data": {
"totalOrders": 312,
"totalSales": 4875.50,
"currency": "EUR",
"averageOrderValue": 15.63,
"totalItemsSold": 894
},
"meta": { "generatedAt": "...", "eventId": "...", "dayId": 42 }
}GET /api/v1/events/{eventId}/analytics/sales-trend
Scope richiesto: analytics:read
Andamento delle vendite giornaliero negli ultimi N giorni.
Query parameters:
| Parametro | Tipo | Descrizione |
|---|---|---|
days | int | (opzionale) Numero di giorni da includere. Default: 7. |
areaId | int | (opzionale) Filtra per area. |
Risposta 200:
{
"data": [
{ "date": "2026-06-09", "totalSales": 3200.00, "orderCount": 210, "currency": "EUR" },
{ "date": "2026-06-10", "totalSales": 4100.00, "orderCount": 275, "currency": "EUR" },
{ "date": "2026-06-11", "totalSales": 4875.50, "orderCount": 312, "currency": "EUR" }
],
"meta": { "generatedAt": "...", "eventId": "..." }
}GET /api/v1/events/{eventId}/analytics/orders/by-hour
Scope richiesto: analytics:read
Distribuzione degli ordini per ora del giorno.
Query parameters:
| Parametro | Tipo | Descrizione |
|---|---|---|
dayId | int | (opzionale) ID del giorno evento. Default: giorno corrente o ultimo chiuso. |
areaId | int | (opzionale) Filtra per area. |
Risposta 200:
{
"data": [
{ "hour": 18, "orderCount": 45, "revenue": 702.00, "currency": "EUR" },
{ "hour": 19, "orderCount": 112, "revenue": 1748.50, "currency": "EUR" },
{ "hour": 20, "orderCount": 98, "revenue": 1530.00, "currency": "EUR" }
],
"meta": { "generatedAt": "...", "eventId": "...", "dayId": 42 }
}GET /api/v1/events/{eventId}/analytics/orders/top-items
Scope richiesto: analytics:read
Prodotti più venduti, ordinati per quantità decrescente.
Query parameters:
| Parametro | Tipo | Descrizione |
|---|---|---|
dayId | int | (opzionale) ID del giorno evento. Default: giorno corrente o ultimo chiuso. |
areaId | int | (opzionale) Filtra per area. |
limit | int | (opzionale) Numero massimo di prodotti da restituire. Default: 10. |
Risposta 200:
{
"data": [
{ "menuItemId": 12, "itemName": "Birra alla spina", "categoryName": "Bevande", "quantity": 134, "revenue": 536.00, "currency": "EUR" },
{ "menuItemId": 5, "itemName": "Porchetta", "categoryName": "Panini", "quantity": 89, "revenue": 712.00, "currency": "EUR" }
],
"meta": { "generatedAt": "...", "eventId": "...", "dayId": 42 }
}GET /api/v1/events/{eventId}/live/item-counts
Scope richiesto: analytics:read
Quantità vendute per prodotto in tempo reale. Conta solo gli ordini confermati — esclude PreOrder, Pending e Cancelled. Progettato per polling frequente da dispositivi IoT.
Query parameters:
| Parametro | Tipo | Descrizione |
|---|---|---|
dayId | int | (opzionale) ID del giorno evento. Default: giorno corrente o ultimo chiuso. |
menuItemIds | int,int,... | (opzionale) Filtra per ID prodotto. Se uno degli ID non appartiene all’evento: 404. |
Risposta 200:
{
"data": [
{ "menuItemId": 12, "itemName": "Birra alla spina", "quantitySold": 134 },
{ "menuItemId": 13, "itemName": "Birra in bottiglia", "quantitySold": 57 }
],
"meta": { "generatedAt": "...", "eventId": "...", "dayId": 42 }
}Se menuItemIds non è specificato, restituisce tutti i prodotti dell’evento con almeno un ordine confermato nel giorno selezionato.
Codici di errore
| Codice | Situazione |
|---|---|
401 Unauthorized | Token assente, malformato, scaduto o revocato |
403 Forbidden | Token valido ma senza lo scope richiesto dall’endpoint |
404 Not Found | Evento inesistente, appartenente a un’altra organizzazione, chiave vincolata a un altro evento, oppure areaId/dayId/menuItemIds non appartenenti all’evento richiesto |
429 Too Many Requests | Rate limit superato — attendere Retry-After secondi |
SagraFacile non restituisce mai 403 per un evento che non appartiene all’organizzazione: usa sempre 404 per non rivelare l’esistenza della risorsa.