Authentification
Toutes les requêtes API utilisent une clé API comme jeton porteur. Créez-en une dans l'application sous Développeur → Clés API. Les clés sont soit lecture seule soit lecture/écriture.
curl https://savrsoft.com/api/v1/events \ -H "Authorization: Bearer savr_live_your_key_here"
URL de base : https://savrsoft.com/api/v1 · Toutes les réponses sont au format JSON. Les erreurs renvoient { "error": "…" } avec un statut 4xx/5xx.
Codes de statut
| Code | Quand vous l'obtenez |
|---|---|
200 | Lecture réussie. |
201 | Création réussie. Le corps est { "id": 42 } — récupérez l'enregistrement si vous avez besoin du reste. |
400 | Un champ requis est manquant, par ex. { "error": "name is required" }, ou un curseur de pagination n'a pas pu être lu. |
401 | Pas de clé API, une clé mal formée, ou une clé qui a été révoquée. |
403 | Une clé en lecture seule a été utilisée pour une écriture. Créez une clé lecture/écriture sous Développeur → Clés API. |
404 | L'enregistrement n'existe pas, ou appartient à une autre organisation — les deux cas sont délibérément indissociables. Ce code est également renvoyé pour un chemin ou une méthode non reconnus. |
Portée de cette API
L'API est en lecture et création uniquement. Il n'y a pas de points de terminaison DELETE, PUT ou PATCH
— une requête utilisant l'un d'eux renvoie 404 Unknown endpoint, comme pour tout chemin non reconnu. La suppression et
la modification se font dans l'application, donc une intégration ne peut pas détruire les enregistrements d'un traiteur. Si vous avez besoin d'un accès en écriture au-delà de la création,
dites-nous ce que vous construisez.
Pagination
Chaque point de terminaison de liste est paginé. Passez limit (par défaut 100, maximum 500) et
suivez next_cursor jusqu'à ce que has_more soit false:
{
"data": [ ... ],
"has_more": true,
"next_cursor": "eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0"
}
Renvoyez le curseur tel quel pour obtenir la page suivante. Traitez-le comme opaque — l'encodage peut changer :
curl "https://savrsoft.com/api/v1/clients?limit=100&cursor=eyJuYW1lIjoiQWNtZSIsImlkIjo0Mn0" \ -H "Authorization: Bearer savr_live_your_key_here"
La pagination est ancrée à la dernière ligne reçue, et non à un décalage, si bien que les lignes créées ou supprimées pendant la navigation ne vous feront pas sauter ou répéter un enregistrement. Un curseur reste valide indéfiniment.
| Paramètre | Comportement |
|---|---|
limit | 1–500. Absent, zéro, négatif ou non numérique revient par défaut à 100 ; toute valeur supérieure à 500 est plafonnée à 500. |
cursor | next_cursor de la page précédente. Omettez-le pour la première page. Un curseur illisible — corrompu, ou provenant d'un autre endpoint — renvoie 400 Invalid cursor plutôt que de repartir silencieusement du début. |
Tri : /events et /invoices du plus récent au plus ancien ; /recipes et
/clients par nom, les égalités étant départagées par id afin que la pagination reste stable en cas de noms identiques.
Endpoints
/v1/meVotre organisation (id, nom, devise).
/v1/eventsListe des événements, du plus récent au plus ancien. Paginé — voir Pagination ci-dessus.
/v1/events/{id}Un événement avec ses plats.
/v1/events · nécessite lecture/écritureCréer un événement. Corps :
{ "name": "Acme holiday party", "event_date": "2026-12-18",
"guests": 120, "price_per_head": 55, "event_type": "corporate" }
/v1/recipes · GET/v1/recipes/{id}Liste des recettes (avec allergènes & régimes alimentaires), ou une recette avec ses ingrédients. La liste est paginée et triée par nom.
/v1/clients · POST/v1/clientsListe des clients (paginée, par nom) ou création d'un client. Corps de création : { "name": "...", "email": "...", "phone": "...", "company": "..." }
/v1/invoices · GET/v1/invoices/{id}Liste des factures avec totaux calculés, les plus récentes en premier, ou une facture avec ses lignes de détail. La liste est paginée.
Webhooks
Enregistrez un endpoint sous Developer → Webhooks. Nous envoyons en POST une charge JSON lorsque des événements se produisent :
{
"id": "evt_abc123",
"type": "event.inquiry",
"created_at": "2026-07-17T18:20:00.000Z",
"data": { "id": 42, "name": "Corporate lunch — Dana Ruiz", ... }
}
Types d'événements
| Type | Se déclenche quand |
|---|---|
event.created | Un événement est créé |
event.inquiry | Une nouvelle demande de traiteur en ligne arrive |
invoice.sent | Une facture est marquée comme envoyée |
invoice.paid | Une facture est marquée comme payée |
proposal.approved | Un client approuve une proposition |
client.created | Un client est créé |
Vérification des signatures
Chaque envoi inclut un en-tête x-savrsoft-signature : le HMAC-SHA256 du corps brut de la requête, signé avec votre clé secrète de webhook. Calculez le vôtre et comparez :
// 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();
