# Rosso Fuoco — API pubbliche v1 · Manuale tecnico

**Versione documento:** 1.0 · **Ultimo aggiornamento:** 19 agosto 2026
**Base URL produzione:** `https://rossofuoco.eu/api/v1`

Questo manuale descrive le API pubbliche di Rosso Fuoco destinate ad applicazioni
esterne (app partner, siti di terzi, integrazioni). Il namespace è **versionato**:
eventuali cambi incompatibili futuri verranno pubblicati come `/api/v2` senza
rompere le integrazioni esistenti su `/api/v1`.

---

## 1. Autenticazione

Ogni richiesta (eccetto `GET /api/v1`, che restituisce la documentazione
machine-readable) richiede una **chiave API** rilasciata dal titolare.

- Formato chiave: `rf_live_` seguito da 43 caratteri (256 bit casuali).
- La chiave viene mostrata **una sola volta** alla creazione: conservatela in un
  secret manager. Sul server è salvato solo un hash SHA-256, non è recuperabile.
- Inviarla in ogni richiesta nell'header HTTP:

```
Authorization: Bearer rf_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

- Non inserite mai la chiave in URL, query string, codice frontend pubblico o
  repository. Se una chiave viene compromessa, il titolare può revocarla con
  effetto immediato e rilasciarne una nuova.

### Scope (permessi)

Ogni chiave ha un elenco di permessi granulari. Una richiesta a un endpoint per
cui la chiave non ha lo scope necessario riceve `403`.

| Scope | Consente |
|---|---|
| `menu` | Lettura menu/catalogo pubblicato |
| `orders` | Lettura vendite chiuse del giorno |
| `orders:write` | Invio di ordini al tavolo |
| `reservations` | Lettura prenotazioni |
| `reservations:write` | Creazione prenotazioni |
| `giftcards` | Lettura stato gift card (mai il PIN) |
| `inventory` | Lettura magazzino/giacenze |
| `stats` | Lettura riepilogo giornaliero |
| `staff` | Lettura turni settimanali |

## 2. Limiti di utilizzo (rate limit)

- Default: **120 richieste al minuto per chiave** (finestra scorrevole).
- Superata la soglia la risposta è `429 Too Many Requests`; attendere e
  riprovare (backoff consigliato: 5–30 secondi).

## 3. Convenzioni generali

- Tutte le risposte sono JSON `UTF-8` con un campo booleano `ok`.
- Valuta: **PLN** (złoty polacco). Gli importi sono numeri decimali (es. `59.5`).
- Date: `YYYY-MM-DD`; orari: `HH:MM` (fuso orario del ristorante, Europe/Warsaw).
- Errori: `{ "ok": false, "error": "<messaggio>" }` con status HTTP appropriato.

| Status | Significato |
|---|---|
| `200` / `201` | Operazione riuscita |
| `400` | Parametri mancanti o non validi |
| `401` | Chiave assente, non valida o revocata |
| `403` | Chiave valida ma senza lo scope richiesto (o funzione disattivata) |
| `404` | Risorsa non trovata |
| `429` | Rate limit superato |
| `503` | Dati momentaneamente non disponibili — **riprovare**, mai dati finti |

---

## 4. Endpoint

### 4.0 GET /api/v1 — documentazione (senza chiave)

Restituisce l'elenco machine-readable degli endpoint, scope e limiti. Utile per
verificare la connettività.

```bash
curl https://rossofuoco.eu/api/v1
```

### 4.1 GET /api/v1/menu — menu pubblicato · scope `menu`

Menu/catalogo con categorie e prodotti (nomi multilingua it/pl/en dove
disponibili, prezzi in PLN).

```bash
curl -H "Authorization: Bearer $API_KEY" https://rossofuoco.eu/api/v1/menu
```

Risposta (estratto):

```json
{
  "ok": true,
  "source": "snapshot",
  "publishedAt": "2026-08-18T14:22:31.000Z",
  "menu": {
    "enabled": true,
    "categories": [ { "id": "12", "name": "Pizze", "nameEn": "Pizzas" } ],
    "products": [
      {
        "id": "184",
        "categoryId": "12",
        "name": "Margherita",
        "descriptionIt": "Pomodoro, mozzarella, basilico",
        "price": 32.0
      }
    ]
  }
}
```

`source` è `snapshot` (menu pubblicato, produzione) o `live` (lettura diretta).

### 4.2 GET /api/v1/orders — vendite del giorno · scope `orders`

Ticket **chiusi** del giorno richiesto (esclusi annullati, eliminati e di test),
con totale e ripartizione pagamenti.

| Parametro | Tipo | Default | Note |
|---|---|---|---|
| `date` | `YYYY-MM-DD` | oggi | Giorno di apertura del ticket |

```bash
curl -H "Authorization: Bearer $API_KEY" \
  "https://rossofuoco.eu/api/v1/orders?date=2026-08-18"
```

```json
{
  "ok": true,
  "date": "2026-08-18",
  "currency": "PLN",
  "count": 2,
  "orders": [
    {
      "ticketId": "8123",
      "shortId": "42",
      "tableNum": "7",
      "total": 156.5,
      "closedAt": "2026-08-18 21:43:10",
      "payments": { "cash": 0, "card": 156.5, "giftCard": 0 }
    }
  ]
}
```

### 4.3 POST /api/v1/orders — invio ordine al tavolo · scope `orders:write`

Invia un ordine per un tavolo. L'ordine segue lo stesso flusso del QR cliente:
entra in coda e **viene sempre confermato dal personale al POS** prima di
diventare effettivo. La funzione deve essere attiva lato ristorante (altrimenti
`403 "Ordinazione al tavolo non attiva"`).

Body JSON:

| Campo | Tipo | Obbligatorio | Note |
|---|---|---|---|
| `tableNum` | string | sì | Numero tavolo (deve esistere) |
| `items` | array | sì | `[{ "productId": "184", "qty": 2, "note": "senza basilico" }]` |
| `note` | string | no | Nota generale ordine (max 500 caratteri) |
| `lang` | string | no | Lingua del cliente (`it`, `pl`, `en`) |

```bash
curl -X POST -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"tableNum":"7","items":[{"productId":"184","qty":2}],"note":"allergia noci"}' \
  https://rossofuoco.eu/api/v1/orders
```

```json
{ "ok": true, "orderId": "c41b6c0e…", "status": "QUEUED", "mode": "confirm", "total": 64.0 }
```

`rejected` (se presente) elenca articoli scartati perché non più a menu.
Esiste inoltre un limite anti-abuso per tavolo: ordini troppo ravvicinati sullo
stesso tavolo ricevono `429` con `code: "rate_limited"`.

### 4.4 GET /api/v1/reservations — prenotazioni · scope `reservations`

| Parametro | Tipo | Note |
|---|---|---|
| `date` | `YYYY-MM-DD` | Un singolo giorno |
| `from`, `to` | `YYYY-MM-DD` | In alternativa: intervallo |
| `status` | string | Filtro stato (es. `CONFIRMED`) |

```bash
curl -H "Authorization: Bearer $API_KEY" \
  "https://rossofuoco.eu/api/v1/reservations?date=2026-08-20"
```

```json
{
  "ok": true,
  "count": 1,
  "reservations": [
    {
      "id": 311,
      "customerId": null,
      "guestName": "Kowalski",
      "guestPhone": "+48 600 000 000",
      "resDate": "2026-08-20",
      "startTime": "19:30",
      "endTime": null,
      "partySize": 4,
      "tableNum": "12",
      "status": "CONFIRMED",
      "colorTag": null,
      "source": "OTHER",
      "notes": "tavolo vicino alla finestra",
      "createdAt": "2026-08-19 10:02:11",
      "createdByName": "API: Sito partner"
    }
  ]
}
```

### 4.5 POST /api/v1/reservations — crea prenotazione · scope `reservations:write`

Body JSON:

| Campo | Tipo | Obbligatorio | Note |
|---|---|---|---|
| `guestName` | string | sì | Nome del cliente |
| `resDate` | `YYYY-MM-DD` | sì | Data |
| `startTime` | `HH:MM` | sì | Ora arrivo |
| `partySize` | number | sì | Numero persone |
| `guestPhone` | string | no | Telefono |
| `endTime` | `HH:MM` | no | Ora fine stimata |
| `tableNum` | string | no | Tavolo desiderato |
| `notes` | string | no | Note |

La prenotazione risulta creata dall'app esterna (nome della chiave API), mai da
un operatore. Risposta `201` con l'oggetto prenotazione completo (stesso formato
della lettura).

```bash
curl -X POST -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"guestName":"Kowalski","resDate":"2026-08-20","startTime":"19:30","partySize":4,"guestPhone":"+48600000000"}' \
  https://rossofuoco.eu/api/v1/reservations
```

### 4.6 GET /api/v1/gift-cards/{cardNo} — stato gift card · scope `giftcards`

Stato e saldo di una gift card. Il **PIN non viene mai esposto** e non è
possibile scalare il saldo via API: il redeem avviene solo al POS.

```bash
curl -H "Authorization: Bearer $API_KEY" \
  https://rossofuoco.eu/api/v1/gift-cards/GC-2026-000123
```

```json
{
  "ok": true,
  "card": {
    "cardNo": "GC-2026-000123",
    "balance": 150.0,
    "active": true,
    "expiryDate": "2027-08-01",
    "usable": true,
    "notUsableReason": null
  }
}
```

`404` se la carta non esiste. In caso di indisponibilità temporanea del
database del ristorante la risposta può arrivare da uno snapshot recente
(campo `source: "snapshot"`): trattarla come indicativa, la verifica
definitiva avviene sempre al POS.

### 4.7 GET /api/v1/inventory/items — magazzino · scope `inventory`

| Parametro | Valori | Default | Note |
|---|---|---|---|
| `kind` | `RAW`, `PRODUCT`, `ALL` | `RAW` | Materie prime, prodotti finiti o tutto |

```bash
curl -H "Authorization: Bearer $API_KEY" \
  "https://rossofuoco.eu/api/v1/inventory/items?kind=RAW"
```

```json
{
  "ok": true,
  "kind": "RAW",
  "count": 1,
  "items": [
    {
      "id": "55",
      "name": "Farina 00",
      "category": "Secco",
      "area": "CUCINA",
      "unit": "kg",
      "kind": "RAW",
      "stockQty": 42.5,
      "parLevel": 30,
      "lastCost": 3.2
    }
  ]
}
```

Nota: le giacenze vengono aggiornate con lo scarico alla **chiusura
giornaliera**, non in tempo reale durante il servizio.

### 4.8 GET /api/v1/stats/daily — riepilogo giornaliero · scope `stats`

Report completo del giorno: totali incassi (contanti/carta/gift card), scontrini,
storni con causali, sconti, voucher, ordini web e mance registrate.

| Parametro | Tipo | Default |
|---|---|---|
| `date` | `YYYY-MM-DD` | oggi |

```bash
curl -H "Authorization: Bearer $API_KEY" \
  "https://rossofuoco.eu/api/v1/stats/daily?date=2026-08-18"
```

Risposta (struttura principale):

```json
{
  "ok": true,
  "date": "2026-08-18",
  "currency": "PLN",
  "report": {
    "totals": {
      "receiptCount": 84,
      "total": 9421.5,
      "cash": 2110.0,
      "card": 7011.5,
      "gc": 300.0,
      "stornoCount": 2,
      "stornoAmount": 64.0,
      "voucherCount": 1,
      "discountCount": 3,
      "discountTotal": 45.0
    },
    "webOrders": { "count": 5, "total": 412.0, "cash": 0, "card": 412.0, "gc": 0 },
    "sumupTips": { "count": 12, "amount": 96.0 },
    "voidsByReason": { "ERRORE": 1, "RECLAMO": 1 },
    "closedTables": [ "…" ],
    "voids": [ "…" ],
    "discounts": [ "…" ]
  }
}
```

### 4.9 GET /api/v1/staff/shifts — turni settimanali · scope `staff`

Turni della settimana (la settimana inizia la **domenica**). Se `weekStart` non
è una domenica viene normalizzato automaticamente alla domenica della settimana.

| Parametro | Tipo | Default |
|---|---|---|
| `weekStart` | `YYYY-MM-DD` | settimana corrente |

```bash
curl -H "Authorization: Bearer $API_KEY" \
  "https://rossofuoco.eu/api/v1/staff/shifts?weekStart=2026-08-16"
```

```json
{
  "ok": true,
  "weekStart": "2026-08-16",
  "shifts": [
    {
      "id": 9021,
      "employeeId": 14,
      "employeeNm": "ANNA",
      "area": "SALA",
      "shiftDate": "2026-08-17",
      "status": "LAVORO",
      "startTime": "16:00",
      "endTime": "23:00",
      "notes": null
    }
  ],
  "employees": [ { "id": 14, "name": "ANNA", "area": "SALA" } ]
}
```

---

## 5. Buone pratiche

- **Gestite sempre il 503**: significa "riprova tra poco", mai dati parziali.
- **Cache lato client**: menu e turni cambiano di rado — una cache di 1–5 minuti
  riduce il consumo del rate limit.
- **Idempotenza scritture**: in caso di timeout su un POST, verificate prima di
  ritentare (es. rileggendo le prenotazioni del giorno) per evitare doppioni.
- **Sicurezza**: la chiave va usata solo da backend a backend, mai nel browser.

## 6. Supporto e versioni

- Endpoint di verifica: `GET /api/v1` (senza chiave).
- Le modifiche compatibili (nuovi campi, nuovi endpoint) possono arrivare senza
  preavviso su `/api/v1`; le modifiche incompatibili usciranno come `/api/v2`.
- Per richieste di nuove funzionalità o nuove chiavi, contattare il ristorante.

## 7. Changelog API

| Data | Versione | Novità |
|---|---|---|
| 2026-08-19 | v1 | Prima pubblicazione: menu, vendite, ordini al tavolo, prenotazioni (lettura+creazione), gift card, magazzino, statistiche giornaliere, turni. |
