API Sylvinio — Documentation

Référence technique de l'API REST publique. Connectez votre boutique à Zapier, à un site PrestaShop / Shopify, à votre ERP ou à toute autre intégration tierce.

Base URL : /api/v1Auth : Bearer tokenFormat : JSON

Démarrage rapide

  1. Activez l'API publique dans votre plan (option publicApi).
  2. Générez une clé depuis Compte → Clés API (label, scopes nécessaires).
  3. Copiez la clé affichée (visible une seule fois) puis testez :
bash
curl https://app.sylvinio.com/api/v1/me \
  -H "Authorization: Bearer sylv_live_xxxxxxxx_<secret>"

Réponse :

json
{
  "shop": { "id": "...", "name": "Ma Boutique", "slug": "ma-boutique" },
  "apiKey": { "label": "Zapier prod", "scopes": ["products:read", "orders:read"] }
}

Authentification

Toutes les requêtes doivent inclure un header Authorization: Bearer <clé>. Les clés ont le format :

sylv_live_<8-hex-prefix>_<64-hex-secret>

Le préfixe sylv_live_xxxxxxxxest visible dans votre dashboard ; le suffixe (32 octets aléatoires) n'est jamais réaffiché — sauvegardez-le dans votre coffre. En cas de fuite, révoquez la clé et créez-en une nouvelle.

Une clé révoquée ou expirée renvoie 401 UNAUTHORIZED. Une clé d'une boutique dont l'abonnement n'est pas actif renvoie 403 SUBSCRIPTION_INACTIVE. Si la feature publicApi n'est pas dans le plan, c'est 403 FEATURE_NOT_IN_PLAN.

Scopes

Chaque clé porte une liste de scopes. Donnez le minimum nécessaire à votre intégration : si Zapier n'a besoin que de lire les commandes, ne lui donnez pas clients:read. Vous pouvez créer plusieurs clés avec des scopes différents pour isoler vos intégrations.

ScopeDescription
products:readLire les produits, leurs variantes et leur stock affiché.
products:writeRéservé V2.
orders:readLire les commandes (POS + Click & Collect + en ligne).
orders:writeRéservé V2.
stock:readLire l'état du stock par variante.
stock:writeAjuster le stock (POST /stock/adjustments).
clients:readLire les clients identifiés et leurs coordonnées.
clients:writeRéservé V2.
loyalty:readLire les soldes fidélité, paliers, tickets loterie.
loyalty:writeRéservé V2.

Les scopes en écriture (:write) hors stock:writesont réservés pour la V2. La V1 est volontairement majoritairement lecture pour limiter la surface d'attaque.

Pagination

Les endpoints de liste utilisent un curseur opaque. Réponse type :

json
{
  "data": [ /* items */ ],
  "pageInfo": {
    "hasMore": true,
    "nextCursor": "cmo93tkdb00971okloesfihdj",
    "limit": 20
  }
}

Pour récupérer la page suivante, passez ?cursor=<nextCursor>. Le curseur est stable tant que les ids ne sont pas réorganisés. Limite max : 100.

Format des prix

Tous les montants monétaires sont retournés en centimes (Int) avec un suffixe Cents. Exemple : "priceCents": 1990 = 19,90 €.

Avantage : pas d'arrondi flottant côté client. Pour afficher, divisez par 100 et formatez avec votre devise. Le taux de TVA est exposé en string (préservation Decimal) : "taxRate": "20.00".

Erreurs

Toutes les erreurs renvoient ce format :

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid query parameters",
    "details": [/* zod issues éventuels */]
  }
}
StatusCodeQuand
400VALIDATION_ERRORBody ou query invalide
400INVALID_JSONBody JSON malformé
401UNAUTHORIZEDHeader manquant, clé invalide / révoquée / expirée
403INSUFFICIENT_SCOPEClé valide mais sans le scope requis
403FEATURE_NOT_IN_PLANL'API publique n'est pas dans le plan
403SUBSCRIPTION_INACTIVEAbonnement expiré ou annulé
404NOT_FOUNDRessource inconnue (ou d'une autre boutique)
422STOCK_NOT_TRACKEDSuivi de stock désactivé pour cette variante
429RATE_LIMITEDTrop de requêtes — voir header Retry-After
500INTERNAL_ERRORErreur serveur inattendue

Rate limiting

Limites par clé, fenêtre glissante :

  • Lecture (GET) : 120 requêtes / minute
  • Écriture (POST/PUT/DELETE) : 30 requêtes / minute

Quand vous dépassez, vous recevez 429 RATE_LIMITED avec un header Retry-After: <secondes>. Implémentez un back-off exponentiel côté client.

GET /me

Endpoint d'introspection : retourne les infos de la clé. N'exige aucun scope spécifique.

GET/api/v1/meScope : any
bash
curl /api/v1/me -H "Authorization: Bearer <key>"
json
{
  "shop":   { "id": "...", "name": "Ma Boutique", "slug": "ma-boutique" },
  "apiKey": { "label": "Zapier prod", "scopes": ["products:read", "orders:read"] }
}

Produits

GET/api/v1/productsScope : products:read

Liste paginée des produits actifs.

Query params : cursor, limit (1-100, défaut 20), q (recherche nom/EAN/SKU/réf), category, brand, productType, active (true|false|all), updatedSince (ISO 8601).

bash
curl "/api/v1/products?limit=20&q=pod" \
  -H "Authorization: Bearer <key>"
json
{
  "data": [{
    "id": "cmo93twzf...",
    "name": "Pod XROS 4",
    "productType": "Pod",
    "ean": "6970310300042",
    "sku": "VAP-001",
    "priceCents": 2990,
    "listPriceCents": 2990,
    "compareAtPriceCents": null,
    "promoActive": false,
    "taxRate": "20.00",
    "active": true,
    "trackStock": true,
    "stockQuantity": null,
    "tags": ["nouveau"],
    "photos": ["https://cdn.sylvinio.com/products/xxx.webp"],
    "category": { "id": "...", "name": "Pods" },
    "brand":    { "id": "...", "name": "Vaporesso" },
    "variants": [{
      "id": "...",
      "label": "Black",
      "sku": "VAP-001-BLK",
      "barcode": "6970310300042",
      "swatchColor": "#000000",
      "priceCents": 2990,
      "stock": 12,
      "lowStockThreshold": 5,
      "isDefault": true,
      "active": true,
      "order": 0
    }],
    "createdAt": "...",
    "updatedAt": "..."
  }],
  "pageInfo": { "hasMore": true, "nextCursor": "...", "limit": 20 }
}
GET/api/v1/products/:idScope : products:read

Détail d'un produit. Inclut les variantes même inactives (la liste les filtre par défaut).

Commandes

Champs volontairement exclus pour conformité NF525 et confidentialité : signature, previousSignature, cumulativeTotalTTCSnapshot, stripePaymentIntentId, internalNote.

GET/api/v1/ordersScope : orders:read

Query params : cursor, limit, status (pending|preparing|ready|completed|cancelled), paymentStatus (pending|paid|refunded), source (pos|click-collect|online), clientId, dateFrom, dateTo.

json
{
  "data": [{
    "id": "...",
    "orderNumber": 42,
    "source": "pos",
    "status": "completed",
    "paymentStatus": "paid",
    "subtotalCents": 4990,
    "discountAmountCents": 0,
    "taxAmountCents": 998,
    "totalCents": 4990,
    "paymentMethod": "card",
    "paidAt": "2026-04-30T16:01:19.358Z",
    "customer": null,
    "clientId": null,
    "items": [{
      "id": "...",
      "productId": "...",
      "variantId": "...",
      "productName": "Pod XROS 4",
      "unitPriceCents": 2990,
      "quantity": 1,
      "totalCents": 2990,
      "taxRate": "20.00",
      "taxAmountCents": 598,
      "refunded": null
    }],
    "createdAt": "...",
    "updatedAt": "..."
  }]
}
GET/api/v1/orders/:idScope : orders:read

Détail commande avec lignes complètes.

Stock

GET/api/v1/stockScope : stock:read

Query params : cursor, limit (défaut 50), productId, q (EAN/SKU/nom), lowStockOnly=true, trackedOnly=false (défaut true, masque les variantes sans suivi).

json
{
  "data": [{
    "variantId": "...",
    "productId": "...",
    "productName": "Pod XROS 4",
    "variantLabel": "Black",
    "sku": "VAP-001-BLK",
    "barcode": "6970310300042",
    "tracked": true,
    "stock": 12,
    "lowStockThreshold": 5,
    "isLowStock": false,
    "isOutOfStock": false,
    "active": true
  }]
}
POST/api/v1/stock/adjustmentsScope : stock:write

Ajuste le stock d'une variante par delta. Crée un StockMovement traçable horodaté.

bash
curl -X POST /api/v1/stock/adjustments \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{ "variantId": "...", "quantity": -2, "reason": "Casse en boutique" }'
json
{
  "data": {
    "variantId": "...",
    "productId": "...",
    "previousStock": 10,
    "newStock": 8,
    "delta": -2,
    "movementId": "...",
    "appliedAt": "2026-05-01T17:11:23.844Z"
  }
}

Erreurs spécifiques : 404 si la variante est inconnue ou appartient à une autre boutique. 422 STOCK_NOT_TRACKEDsi le suivi de stock n'est pas activé pour cette variante (à activer dans le dashboard d'abord).

Clients & fidélité

Champs exclus de la sortie : note (note privée gérant), anonymousClientId (lien legacy).

GET/api/v1/clientsScope : clients:read

Query params : cursor, limit, q (recherche nom/téléphone/email).

json
{
  "data": [{
    "id": "...",
    "name": "Marie Dupont",
    "displayName": null,
    "phone": "0612345678",
    "email": "[email protected]",
    "billing": {
      "company": null,
      "vatNumber": null,
      "address": null,
      "city": null,
      "postalCode": null,
      "country": "FR"
    },
    "birthDate": null,
    "loyalty": {
      "points": 2082,
      "expiresAt": null,
      "pointsLifetime": 1217,
      "pointsAllTime": 2082,
      "tierAchievedAt": "2026-04-27T11:14:23.433Z"
    },
    "receiptByEmail": false,
    "tags": [],
    "createdAt": "...",
    "updatedAt": "..."
  }]
}
GET/api/v1/clients/:idScope : clients:read

Détail d'un client.

GET/api/v1/clients/:id/loyaltyScope : loyalty:read

Solde fidélité, palier courant + suivant + progression, 10 derniers tickets loterie, 10 derniers spins instantanés.

json
{
  "data": {
    "clientId": "...",
    "points": 2082,
    "pointsLifetime": 1217,
    "pointsAllTime": 2082,
    "tier": {
      "current": { "id": "silver", "name": "Silver", "minPointsLifetime": 500, "color": "#9CA3AF", "achievedAt": "..." },
      "next":    { "id": "gold",   "name": "Gold",   "minPointsLifetime": 1500, "pointsToNext": 283 },
      "progress": 0.717
    },
    "recentLotteryTickets": [{ "id": "...", "orderId": "...", "createdAt": "..." }],
    "recentSpins":          [{ "id": "...", "orderId": "...", "won": true, "prizeLabel": "Pod offert", "prizeType": "free_product", "createdAt": "..." }]
  }
}

Besoin d'aide ou d'un endpoint manquant ?

L'API est en V1 — la V2 ouvrira l'écriture sur produits / clients / fidélité et ajoutera les webhooks. Si vous avez un cas d'usage qui n'est pas couvert, signalez-le via le programme Éclaireur ou le chat support.