Autenticazione
Tutte le richieste API utilizzano una chiave API come bearer token. Creane una nell'app in Developer → API keys. Le chiavi sono di sola lettura oppure di lettura/scrittura.
curl https://savrsoft.com/api/v1/events \ -H "Authorization: Bearer savr_live_your_key_here"
URL di base: https://savrsoft.com/api/v1 · Tutte le risposte sono in formato JSON. Gli errori restituiscono { "error": "…" } con uno status 4xx/5xx.
Codici di stato
| Codice | Quando lo ricevi |
|---|---|
200 | Lettura riuscita. |
201 | Creazione riuscita. Il corpo è { "id": 42 } — recupera il record se ti serve il resto delle informazioni. |
400 | Manca un campo obbligatorio, ad es. { "error": "name is required" }, oppure non è stato possibile leggere un cursore di paginazione. |
401 | Nessuna chiave API, una non valida o una che è stata revocata. |
403 | È stata usata una chiave in sola lettura per una scrittura. Crea una chiave di lettura/scrittura in Developer → API keys. |
404 | Il record non esiste, oppure appartiene a un'altra organizzazione — le due situazioni sono deliberatamente indistinguibili. Restituito anche per un percorso o metodo non riconosciuto. |
Ambito di questa API
L'API consente solo lettura e creazione. Non ci sono endpoint DELETE, PUT o PATCH
— una richiesta che ne usa uno restituisce 404 Unknown endpoint, come qualsiasi percorso non riconosciuto. L'eliminazione e
la modifica avvengono nell'app, quindi un'integrazione non può distruggere i dati di un caterer. Se ti serve un accesso in scrittura oltre alla creazione,
dicci cosa stai realizzando.
Paginazione
Ogni endpoint di tipo elenco è paginato. Passa limit (predefinito 100, massimo 500) e
segui next_cursor finché has_more è false:
{
"data": [ ... ],
"has_more": true,
"next_cursor": "eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0"
}
Passa il cursore così com'è per ottenere la pagina successiva. Trattalo come opaco — la codifica potrebbe cambiare:
curl "https://savrsoft.com/api/v1/clients?limit=100&cursor=eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0" \ -H "Authorization: Bearer savr_live_your_key_here"
La paginazione è ancorata all'ultima riga ricevuta, non a un offset, quindi le righe create o eliminate durante la navigazione non causeranno il salto o la ripetizione di un record. Un cursore rimane valido indefinitamente.
| Parametro | Comportamento |
|---|---|
limit | 1–500. Se assente, zero, negativo o non numerico, il valore predefinito è 100; qualsiasi valore superiore a 500 viene limitato a 500. |
cursor | next_cursor dalla pagina precedente. Ometterlo per la prima pagina. Un cursore che non può essere letto — corrotto o proveniente da un endpoint diverso — restituisce 400 Invalid cursor invece di ricominciare silenziosamente dall'inizio. |
Ordinamento: /events e /invoices dal più recente; /recipes e
/clients per nome, con parità risolta per id in modo che la paginazione rimanga stabile quando i nomi si ripetono.
Endpoint
/v1/meLa tua organizzazione (id, nome, valuta).
/v1/eventsElenca gli eventi, dal più recente. Paginato — vedi Paginazione sopra.
/v1/events/{id}Un evento con i suoi piatti.
/v1/events · richiede read/writeCrea un evento. Corpo:
{ "name": "Acme holiday party", "event_date": "2026-12-18",
"guests": 120, "price_per_head": 55, "event_type": "corporate" }
/v1/recipes · GET/v1/recipes/{id}Elenca le ricette (con allergeni & indicazioni dietetiche), oppure una singola ricetta con gli ingredienti. L'elenco è paginato e ordinato per nome.
/v1/clients · POST/v1/clientsElenca i clienti (paginati, per nome) oppure ne crea uno. Corpo per la creazione: { "name": "...", "email": "...", "phone": "...", "company": "..." }
/v1/invoices · GET/v1/invoices/{id}Elenca le fatture con i totali calcolati, dalla più recente, oppure una singola fattura con le voci di dettaglio. L'elenco è paginato.
Webhook
Registra un endpoint in Developer → Webhooks. Inviamo una richiesta POST con un payload JSON quando si verificano gli eventi:
{
"id": "evt_abc123",
"type": "event.inquiry",
"created_at": "2026-07-17T18:20:00.000Z",
"data": { "id": 42, "name": "Corporate lunch — Dana Ruiz", ... }
}
Tipi di evento
| Tipo | Si attiva quando |
|---|---|
event.created | Viene creato un evento |
event.inquiry | Arriva una nuova richiesta di catering online |
invoice.sent | Una fattura viene contrassegnata come inviata |
invoice.paid | Una fattura viene contrassegnata come pagata |
proposal.approved | Un cliente approva una proposta |
client.created | Viene creato un cliente |
Verifica delle firme
Ogni consegna include un header x-savrsoft-signature : l'HMAC-SHA256 del corpo grezzo della richiesta, firmato con la tua chiave segreta del webhook. Calcola lo stesso valore e confrontalo:
// Node.js
const crypto = require('crypto');
const sig = crypto.createHmac('sha256', SIGNING_SECRET)
.update(rawBody).digest('hex');
if (sig !== req.headers['x-savrsoft-signature']) reject();
