API Checkout v1

La fidélité Sourdi, intégrée à votre checkout.

Affichez ou imprimez un QR à usage unique après le paiement, ou scannez la carte du client en caisse, pour qu’il collecte ses tampons ou ses points dans l’application Sourdi.

Parcourir la documentation
Commerces : prise en main

Démarrage rapide

L’API est disponible pour les commerces actifs ayant un programme de fidélité à tampons ou à points.

1Créez votre clé

Dans Tableau marchand → Paramètres → API checkout.

2Appelez Sourdi côté serveur

Après paiement, envoyez le montant de fidélité et l’identifiant unique de la commande.

3Affichez le QR retourné

Affichez-le sur la page de confirmation ou l’écran client, ou imprimez-le sur le ticket de caisse.

Authentification

Transmettez la clé avec le schéma Bearer dans l’en-tête Authorization. Les clés dans l’URL ou le corps sont refusées.

Authorization: Bearer sourdi_live_KEY_ID.API_SECRET
Content-Type: application/json
La clé n’est visible qu’une fois.

Une seule clé peut être active. Après révocation, elle cesse de fonctionner immédiatement et une nouvelle clé peut être créée.

Intégration site web
Intégration site web

Checkout d’une carte à tampons

À la confirmation du paiement, votre serveur demande un stamp_code signé. Le QR est lié à votre commerce, expire après 10 minutes par défaut et ne peut être validé qu’une fois.

POST/api/v1/checkout/stamps

Requête serveur

curl --request POST "/api/v1/checkout/stamps" \
  --header "Authorization: Bearer $SOURDI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "stamps": 2,
    "transaction_id": "order-2026-000184"
  }'
ChampTypeDescription
stamps requisintegerNombre de tampons à collecter : 1, 2 ou 3.
transaction_id requisstringIdentifiant stable et unique de la commande, de 8 à 128 caractères.

Réponse — HTTP 201

{
  "status": true,
  "data": {
    "reward_type": "stamps",
    "stamps": 2,
    "stamp_code": "sourdi_checkout_v1.…",
    "transaction_id": "order-2026-000184",
    "expires_at": "2026-08-14T12:10:00.000Z",
    "qr": {
      "data": "sourdi_checkout_v1.…",
      "image_data_url": "data:image/png;base64,iVBOR…"
    }
  },
  "idempotent_replay": false
}
Intégration site web

Checkout d’une carte à points

Pour une carte à points, le checkout retourne un QR adapté au solde de crédits. Le client le scanne avec la même fonctionnalité de scan de l’application ; les points sont ajoutés après validation serveur.

POST/api/v1/checkout/points

Requête serveur

curl --request POST "/api/v1/checkout/points" \
  --header "Authorization: Bearer $SOURDI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "points": 25,
    "transaction_id": "order-2026-000185",
    "purchases": ["PRODUCT-001", "PRODUCT-002"]
  }'
ChampTypeDescription
points requisintegerDe 1 à 100 000 points à créditer.
transaction_id requisstringIdentifiant stable et unique de la commande.
purchasesstring[]Jusqu’à 100 références produit facultatives.

Réponse — HTTP 201

{
  "status": true,
  "data": {
    "reward_type": "points",
    "points": 25,
    "transaction_id": "order-2026-000185",
    "expires_at": "2026-08-14T12:10:00.000Z",
    "qr": {
      "data": "sourdi_checkout_v1.…",
      "image_data_url": "data:image/png;base64,iVBOR…"
    }
  },
  "idempotent_replay": false
}

Afficher le QR sur la page de confirmation

Votre backend appelle Sourdi avec la clé secrète, puis renvoie uniquement data.qr.image_data_url à votre navigateur. L’exemple suivant suppose que votre propre endpoint /api/order/reward effectue cet appel serveur-à-serveur.


        
Ne transmettez jamais la clé API au navigateur.

Le navigateur reçoit l’image du QR, jamais l’en-tête Authorization. Servez la page et votre endpoint de commande en HTTPS.

Intégration caisse (POS)
Intégration caisse (POS)

Sourdi dans votre caisse

Votre logiciel de caisse appelle l’API avec la même clé que le checkout web. Le parcours dépend du type de carte de votre commerce.

Carte à tampons : un QR par ticket
  1. Après l’encaissement, la caisse demande un QR à usage unique pour 1 à 3 tampons.
  2. Elle l’imprime sur le ticket grâce au flux ESC/POS fourni, ou l’affiche sur l’écran client.
  3. Le client scanne le QR avec l’application Sourdi, sur place ou chez lui, dans les 48 heures.
Carte à points : scan de la carte du client
  1. Le client présente le QR de sa carte dans l’application Sourdi.
  2. Le lecteur de la caisse le scanne et transmet la valeur lue à votre logiciel.
  3. La caisse envoie cette valeur et le nombre de points calculé : Sourdi crédite la carte et renvoie le nouveau solde.
Appelez l’API depuis le serveur de la caisse ou un terminal sécurisé.

Ne stockez jamais la clé API sur un poste accessible aux clients. Chaque ticket utilise son propre transaction_id, par exemple le numéro du ticket.

Intégration caisse (POS)

QR à tampons pour la caisse

À la fin de l’encaissement, demandez un QR pour 1 à 3 tampons. Il reste valable 48 heures pour pouvoir être scanné depuis un ticket imprimé, et ne peut être utilisé qu’une fois. La réponse contient l’image du QR, les textes du ticket et le flux d’impression ESC/POS.

POST/api/v1/pos/stamps

Requête serveur

curl --request POST "/api/v1/pos/stamps" \
  --header "Authorization: Bearer $SOURDI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "stamps": 2,
    "transaction_id": "ticket-2026-000186",
    "locale": "fr",
    "paper_width": 80,
    "qr_mode": "native"
  }'
ChampTypeDescription
stamps requisintegerNombre de tampons à collecter : 1, 2 ou 3.
transaction_id requisstringIdentifiant unique du ticket, de 8 à 128 caractères. Le renvoyer réimprime le même QR.
localestringLangue du ticket : fr, en, ar, nl, de, el, it, pt, es, sv ou tr. Par défaut, la langue de votre compte Sourdi.
messagestringMessage imprimé au-dessus du QR pour ce ticket uniquement, 160 caractères maximum. Remplace le message enregistré dans le tableau de bord.
paper_widthintegerLargeur du papier en millimètres : 58 ou 80. Par défaut 80.
qr_modestringnative, par défaut : l’imprimante dessine le QR. raster : Sourdi l’envoie sous forme d’image, pour les imprimantes sans commande QR.
time_zonestringFuseau horaire IANA de la date de validité imprimée, par exemple Europe/Paris. Par défaut Africa/Tunis.

Réponse : HTTP 201

{
  "status": true,
  "data": {
    "reward_type": "stamps",
    "amount": 2,
    "stamps": 2,
    "stamp_code": "sourdi_checkout_v1.…",
    "transaction_id": "ticket-2026-000186",
    "expires_at": "2026-10-17T11:30:00.000Z",
    "qr": {
      "data": "sourdi_checkout_v1.…",
      "image_data_url": "data:image/png;base64,iVBOR…"
    },
    "receipt": {
      "locale": "fr",
      "message": "Merci pour votre visite chez Burger Spot !",
      "scan_text": "Scannez ce QR code avec l’application Sourdi pour collecter vos tampons.",
      "valid_until_text": "Valable jusqu’au 17/10/2026 12:30",
      "logo_url": "https://sourdi.net/landing/iconlogo.png"
    },
    "escpos": {
      "base64": "G0AbYQEddjAAFABgAA…",
      "paper_width": 80,
      "qr_mode": "native",
      "printed_locale": "fr"
    }
  },
  "idempotent_replay": false
}
Réimprimer un ticket

Renvoyez la même requête avec le même transaction_id : tant que le QR n’a pas été scanné, l’API renvoie le même QR avec HTTP 200 et idempotent_replay: true. Une fois le QR scanné, la réponse est 409 CHECKOUT_REWARD_ALREADY_USED.

Intégration caisse (POS)

Imprimer le ticket ou afficher le QR

escpos.base64 contient le ticket complet, prêt pour une imprimante thermique compatible ESC/POS : logo Sourdi, message, QR code, instructions pour le client et date de validité, puis coupe du papier. Décodez-le en octets et envoyez-le sans le modifier.

Envoyer le flux à l’imprimante

Imprimante réseau : port TCP 9100 (impression RAW). Imprimante USB ou série : écrivez les octets sur le port, ou passez par une file d’impression en mode RAW.

// Node.js
const net = require('net');
const receipt = Buffer.from(data.escpos.base64, 'base64');
const printer = net.connect(9100, '192.168.1.50', () => printer.end(receipt));
# Python
import base64, socket
receipt = base64.b64decode(data["escpos"]["base64"])
with socket.create_connection(("192.168.1.50", 9100)) as printer:
    printer.sendall(receipt)
# Shell
echo "$ESCPOS_BASE64" | base64 --decode | nc 192.168.1.50 9100

Mode natif ou raster

  • native L’imprimante génère le QR avec la commande GS ( k (modèle 2, correction M). Flux léger et rendu net : c’est le mode recommandé.
  • raster Sourdi envoie le QR sous forme d’image (GS v 0). Utilisez ce mode si votre imprimante n’imprime pas de QR en mode natif.

Largeur du papier

PapierPoints imprimablesCaractères par ligne
paper_width: 5838432
paper_width: 8057648

Message du ticket

Le message imprimé au-dessus du QR est choisi dans cet ordre :

  1. le champ message de la requête, s’il est fourni ;
  2. sinon, le message enregistré dans Tableau marchand → Paramètres → Message du ticket de caisse ;
  3. sinon, un remerciement traduit dans la langue du ticket, par exemple « Merci pour votre visite chez Burger Spot ! ».

Langues et jeux de caractères

Le texte est encodé avec la page de code adaptée à la langue : WPC1252 pour les langues latines, WPC1253 pour le grec, WPC1254 pour le turc (numérotation Epson). Un caractère qu’aucune page de code ne contient, comme un emoji, fait imprimer le message par défaut.

Les imprimantes thermiques ne savent pas lier les lettres arabes. Pour un ticket en arabe, le flux ESC/POS imprime le texte en français et receipt contient le texte arabe, pour les caisses qui composent leur propre ticket. escpos.printed_locale indique la langue imprimée.

Écran client ou ticket composé par votre caisse

Sur un écran client, affichez qr.image_data_url. Si votre caisse compose elle-même ses tickets, générez le QR à partir de qr.data et utilisez les textes de receipt : message, scan_text, valid_until_text et logo_url.

Intégration caisse (POS)

Suivre un QR de caisse

Vérifiez si le client a collecté ses tampons, par exemple pour l’indiquer sur l’écran de la caisse.

GET/api/v1/pos/stamps/{transaction_id}
curl "/api/v1/pos/stamps/ticket-2026-000186" \
  --header "Authorization: Bearer $SOURDI_API_KEY"
{
  "status": true,
  "data": {
    "transaction_id": "ticket-2026-000186",
    "stamps": 2,
    "status": "redeemed",
    "expires_at": "2026-10-17T11:30:00.000Z",
    "redeemed_at": "2026-10-15T18:02:41.000Z"
  }
}
StatutDescription
issuedQR pas encore scanné.
processingLe client est en train de collecter ses tampons.
redeemedTampons ajoutés à la carte du client.
expiredQR non scanné dans les 48 heures.
Intégration caisse (POS)

Points après le scan de la carte

Le client présente le QR de sa carte dans l’application Sourdi. Votre caisse calcule les points selon vos propres règles et envoie la valeur scannée telle quelle : Sourdi retrouve la carte du client dans votre commerce et applique les points une seule fois par transaction_id.

Configurer le lecteur

  • Mode clavier (HID ou keyboard wedge), lecture des QR codes activée.
  • Suffixe Entrée pour valider la lecture, sans préfixe.
  • Même disposition de clavier que le poste, pour que les accolades et les guillemets du QR arrivent intacts.
  • Envoyez la valeur lue sans la modifier : les espaces et retours à la ligne en début et en fin sont ignorés.

Valeurs acceptées dans scan

Valeur scannéeDescription
{"type":"sourdi_user","userId":"66b8a42f9759d5ee8c90c123"}QR de la carte affiché dans l’application Sourdi. Sourdi retrouve la carte du client dans votre commerce.
66b8a42f9759d5ee8c90c456Identifiant de carte Sourdi : 24 caractères hexadécimaux.
POST/api/v1/pos/points

Requête serveur

curl --request POST "/api/v1/pos/points" \
  --header "Authorization: Bearer $SOURDI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "scan": "{\"type\":\"sourdi_user\",\"userId\":\"66b8a42f9759d5ee8c90c123\"}",
    "points": 25,
    "transaction_id": "ticket-2026-000187"
  }'
ChampTypeDescription
scanstringValeur lue par le lecteur. Requis si card_id est absent.
card_idstringIdentifiant de carte, à la place de scan.
points requisintegerPoints à créditer (positif) ou à débiter (négatif), de -100 000 à 100 000, jamais 0.
transaction_id requisstringIdentifiant stable et unique de la commande.
purchasesstring[]Jusqu’à 100 références produit facultatives.

Réponse : HTTP 201

{
  "status": true,
  "data": {
    "cardId": "66b8a42f9759d5ee8c90c456",
    "pointsApplied": 25,
    "balance": 140,
    "transactionId": "ticket-2026-000187",
    "completedAt": "2026-10-15T18:05:12.000Z"
  },
  "idempotent_replay": false
}
Débits et cartes manquantes

Un débit supérieur au solde est refusé avec 409 INSUFFICIENT_BALANCE. Si le client n’a pas encore de carte dans votre commerce, la réponse est 404 MERCHANT_CARD_NOT_FOUND : il l’ajoute dans l’application, puis vous scannez de nouveau.

Intégration caisse (POS)

Alternative : identifiant de carte ou d’utilisateur connu

Une caisse qui lit déjà l’identifiant de carte peut créditer ou débiter le solde sans afficher de QR. Cette route est réservée aux programmes à points.

POST/api/v1/points/transactions
curl --request POST "/api/v1/points/transactions" \
  --header "Authorization: Bearer $SOURDI_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "card_id": "66b8a42f9759d5ee8c90c123",
    "points": -20,
    "transaction_id": "ticket-2026-000186"
  }'
Commerces : fiabilité et sécurité

Retries, expiration et usage unique

En cas de coupure réseau, renvoyez exactement la même requête avec le même transaction_id. Tant que le QR est valide et inutilisé, l’API retourne le même QR avec HTTP 200 et idempotent_replay: true.

Un transaction_id ne peut pas être réutilisé avec un autre type, montant ou panier. Après expiration, créez une nouvelle opération avec un nouvel identifiant. Dès qu’un QR est scanné, toute nouvelle tentative est refusée globalement, même depuis une autre carte.

Erreurs

HTTPCodeAction recommandée
400Invalid inputCorriger les champs envoyés.
400INVALID_SCANScanner de nouveau le QR de la carte dans l’application Sourdi.
401INVALID_API_KEYVérifier la clé ou en créer une nouvelle après révocation.
403LOYALTY_API_NOT_ENABLEDActiver le programme de fidélité du commerce.
403STAMPS_API_NOT_ENABLED / POINTS_API_NOT_ENABLEDUtiliser la route correspondant au type de carte du commerce.
404MERCHANT_CARD_NOT_FOUNDLe client n’a pas de carte dans votre commerce : il doit l’ajouter dans l’application.
404POS_QR_NOT_FOUNDVérifier le transaction_id du ticket.
409IDEMPOTENCY_CONFLICTUtiliser un nouvel identifiant pour une nouvelle commande.
409CHECKOUT_QR_ALREADY_USEDNe pas appliquer la fidélité une seconde fois.
409CHECKOUT_REWARD_ALREADY_USEDQR déjà scanné : la fidélité est déjà appliquée, ne pas réimprimer.
409INSUFFICIENT_BALANCENe pas débiter plus que le solde de la carte.
409TRANSACTION_IN_PROGRESSRenvoyer la même requête après quelques secondes.
410CHECKOUT_QR_EXPIREDGénérer un nouveau QR avec un nouveau transaction_id.
429RATE_LIMITEDRespecter RateLimit-Reset avant de réessayer.

Bonnes pratiques de sécurité

  • Appelez l’API exclusivement depuis votre serveur ou un terminal sécurisé, toujours en HTTPS.
  • N’insérez jamais la clé dans le JavaScript du navigateur, une application mobile, Git, les logs ou les outils d’analytics.
  • Masquez les en-têtes Authorization et les données QR dans les journaux.
  • Rendez le QR uniquement après confirmation effective du paiement et ne le placez pas dans une page mise en cache.
  • Révoquez immédiatement une clé suspectée d’avoir fuité, puis générez-en une nouvelle.
  • Utilisez un transaction_id unique par commerce et conservez-le avec la commande.
Applications de livraison : démarrage

API partenaires livraison

Pour les plateformes de livraison : donnez des tampons Sourdi pour les commandes livrées, acceptez les codes promo SRD- au paiement et annulez-les quand une commande est annulée ou remboursée. Toutes les routes ci-dessous sont relatives à /api/v1/partner.

Télécharger la description OpenAPI 3.1

Sourdi vous fournit une clé Bearer. Les clés live commencent par sourdi_partner_, les clés sandbox par sourdi_partner_test_. L’en-tête de réponse X-Sourdi-Mode vaut live ou test.

Authorization: Bearer sourdi_partner_test_KEY_ID.API_SECRET
Content-Type: application/json

Chaque environnement accepte deux clés actives à la fois. Pour changer de clé sans interruption, créez la nouvelle dans la console, déployez-la, puis révoquez l’ancienne.

Identifiants

external_store_id, order_id et order_ref : de 1 à 128 caractères parmi les lettres, les chiffres et . _ : - (motif ^[A-Za-z0-9._:-]+$). Toute autre valeur reçoit 400 VALIDATION_ERROR.

Limites de débit

Chaque partenaire peut envoyer 600 requêtes par minute, dont 120 par minute sur les routes /coupons. Au-delà, l’API répond 429 RATE_LIMITED : attendez RateLimit-Reset avant de réessayer. Dix authentifications échouées depuis une même IP en 15 minutes bloquent cette IP jusqu’à la fin de la fenêtre.

Console partenaire

Votre équipe gère son intégration dans la console partenaire Sourdi : clés API de sandbox et de production, bac à sable interactif, journal des requêtes et graphiques d’usage, magasins liés, activité par commande avec export CSV, et membres de l’équipe.

Postulez depuis la page du programme partenaires livraison : Sourdi crée votre compte avec un sandbox, puis active la production quand votre intégration fonctionne. Vous pouvez d’abord explorer la console en mode démo.

Se connecter à la console partenaire Découvrir le programme partenaires

Sandbox

Demandez une clé sandbox avant d’écrire la moindre ligne. Elle n’atteint que votre commerce de test « Sourdi Sandbox <votre-slug> » et ne touche jamais de vraies cartes clients.

  1. POST /sandbox/reset remet votre commerce de test à zéro et renvoie un link_code (valable 30 jours) ainsi que vos codes promo de test.
  2. Liez-le avec POST /merchants/link, puis émettez des liens de tampons et utilisez les codes exactement comme en production.
  3. POST /stamps/claims/{order_id}/simulate-redeem joue le rôle du client qui ouvre le lien de tampons.
POST/api/v1/partner/sandbox/reset

Réponse : HTTP 200

{
  "status": true,
  "data": {
    "merchant": {
      "name": "Sourdi Sandbox glovo-sandbox",
      "stamps_to_reward": 10,
      "coupon_value": "-15%",
      "max_discount_cents": 1500,
      "min_order_cents": 1000
    },
    "linked": false,
    "external_store_id": null,
    "link_code": "SRD-7K3M-9QX2-AB4C",
    "link_code_expires_at": "2026-11-13T10:00:00.000Z",
    "test_codes": [
      { "promo_code": "SRD-4F8KQVAD", "expected_status": "valid", "description": "-15%, max €15 discount, min order €10" },
      { "promo_code": "SRD-4F8KQEXP", "expected_status": "expired", "description": "Expired yesterday" }
    ]
  }
}

Codes de test

CodeRésultat attendu
SRD-xxxxxVADValide : -15 %, réduction maximale 15 €, commande minimale 10 €.
SRD-xxxxxCAPValide : -100 %, plafonné à 15 € pour tester max_discount.
SRD-xxxxxEXPExpiré.
SRD-xxxxxTXTPas un pourcentage : inutilisable en livraison.
SRD-xxxxxREDDéjà utilisé sur la commande sandbox-used-order.
curl --request POST "/api/v1/partner/stamps/claims/order-1001/simulate-redeem" \
  --header "Authorization: Bearer $SOURDI_PARTNER_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "external_store_id": "store-42" }'
Applications de livraison : commandes

Tampons pour une commande

Après la livraison, demandez un lien de 1 à 3 tampons et transmettez claim_url au client. Il est à usage unique et valable 48 heures ; renvoyer le même order_id renvoie le même lien.

POST/api/v1/partner/stamps/claims
curl --request POST "/api/v1/partner/stamps/claims" \
  --header "Authorization: Bearer $SOURDI_PARTNER_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "external_store_id": "store-42", "order_id": "order-1001", "stamps": 1 }'

Réponse : HTTP 201

{
  "status": true,
  "data": {
    "claim_url": "https://app.backend.sourdi.net/claim/…",
    "order_id": "order-1001",
    "external_store_id": "store-42",
    "stamps": 1,
    "status": "issued",
    "expires_at": "2026-10-16T09:00:00.000Z"
  },
  "idempotent_replay": false
}

Statut d’un lien de tampons

GET /stamps/claims/{order_id} renvoie le statut des tampons d’une commande : issued (lien pas encore ouvert), processing (le client est en train de les collecter), redeemed, expired ou voided.

GET/api/v1/partner/stamps/claims/{order_id}
{
  "status": true,
  "data": {
    "order_id": "order-1001",
    "stamps": 1,
    "status": "redeemed",
    "expires_at": "2026-10-16T09:00:00.000Z",
    "redeemed_at": "2026-10-14T19:42:10.000Z",
    "voided_at": null
  }
}

Codes promo

Le client copie son code SRD-XXXXXXXX depuis l’application Sourdi. verify ne consomme rien ; redeem utilise le code pour une commande. Les coupons utilisables en livraison sont toujours des pourcentages de 10 à 100 %.

POST/api/v1/partner/coupons/verify
POST/api/v1/partner/coupons/redeem
curl --request POST "/api/v1/partner/coupons/redeem" \
  --header "Authorization: Bearer $SOURDI_PARTNER_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "external_store_id": "store-42", "promo_code": "SRD-7K3M9QX2", "order_id": "order-1001", "order_subtotal_cents": 2450 }'

Réduction maximale et commande minimale

Appliquez la réduction exactement comme renvoyée : réduction = arrondi(sous-total × discount_percent / 100), plafonnée à max_discount, et aucune réduction si le sous-total est inférieur à min_order. Les deux valent null quand le commerçant n’a fixé aucune limite. Envoyez order_subtotal_cents à redeem : Sourdi vérifie le minimum et renvoie discount_cents.

{
  "valid": true,
  "status": "valid",
  "promo_code": "SRD-7K3M9QX2",
  "discount_percent": 15,
  "coupon_value": "-15%",
  "expires_at": "2026-11-02T10:00:00.000Z",
  "max_discount": { "amount_cents": 1500, "currency": "EUR" },
  "min_order": { "amount_cents": 1000, "currency": "EUR" },
  "discount_rule": "percent_of_subtotal_capped"
}

Code utilisé en magasin pendant le paiement

Un code peut passer verify puis être utilisé en magasin avant que votre client paie : redeem répond alors 409 COUPON_ALREADY_REDEEMED. Retirez la réduction et demandez au client de payer la différence ou d’utiliser un autre code. Pour l’éviter, appelez POST /coupons/hold quand le client applique le code : il vous est réservé 10 minutes, magasin compris.

POST/api/v1/partner/coupons/hold
POST/api/v1/partner/coupons/hold/release

Réserver un code

curl --request POST "/api/v1/partner/coupons/hold" \
  --header "Authorization: Bearer $SOURDI_PARTNER_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "external_store_id": "store-42", "promo_code": "SRD-7K3M9QX2", "order_ref": "cart-5531" }'
{
  "held": true,
  "order_ref": "cart-5531",
  "hold_expires_at": "2026-10-14T19:52:00.000Z",
  "valid": true,
  "status": "valid",
  "promo_code": "SRD-7K3M9QX2",
  "discount_percent": 15,
  "coupon_value": "-15%",
  "expires_at": "2026-11-02T10:00:00.000Z",
  "max_discount": { "amount_cents": 1500, "currency": "EUR" },
  "min_order": { "amount_cents": 1000, "currency": "EUR" },
  "discount_rule": "percent_of_subtotal_capped"
}

Commandes annulées ou remboursées

Annulez ce que la commande a donné. Les deux appels sont idempotents : les répéter renvoie le même résultat.

Les annulations (void, coupons/release et coupons/hold/release) fonctionnent même après que le commerçant a mis son programme en pause, tant que le magasin est lié.

POST/api/v1/partner/stamps/claims/{order_id}/void

Tampons : un lien non utilisé cesse de fonctionner ; des tampons déjà collectés sont retirés de la carte. S’ils avaient complété la carte et que le coupon de récompense n’est pas encore utilisé, il est retiré aussi ; sinon shortfall indique les tampons qui n’ont pas pu être retirés.

curl --request POST "/api/v1/partner/stamps/claims/order-1001/void" \
  --header "Authorization: Bearer $SOURDI_PARTNER_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "external_store_id": "store-42", "reason": "customer_cancelled" }'
POST/api/v1/partner/coupons/release

Coupon : le client récupère le même code, avec sa date d’expiration et ses limites d’origine. Un coupon expiré entre-temps n’est pas restauré (restored: false).

curl --request POST "/api/v1/partner/coupons/release" \
  --header "Authorization: Bearer $SOURDI_PARTNER_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "external_store_id": "store-42", "order_id": "order-1001" }'

Tampons et coupon sur la même commande

Une commande peut recevoir un lien de tampons et utiliser un code promo, avec le même order_id. Un second code différent sur la même commande est refusé (ORDER_ALREADY_HAS_COUPON).

Erreurs partenaires

HTTPCodeAction recommandée
400VALIDATION_ERRORCorriger les champs envoyés.
400STAMPS_LIMIT_EXCEEDEDEnvoyez au plus 3 tampons par commande.
401MISSING_API_KEYEnvoyez l’en-tête Authorization: Bearer.
401INVALID_API_KEYVérifier la clé ou en créer une nouvelle après révocation.
403PARTNER_INACTIVEVotre compte partenaire est désactivé : contactez Sourdi.
403STAMPS_API_NOT_ENABLEDLe commerçant a mis en pause ou n’a pas de programme à tampons : ne proposez pas Sourdi sur cette commande.
404STORE_NOT_LINKEDLier le magasin ou vérifier external_store_id.
404CLAIM_NOT_FOUNDAucun lien de tampons pour cet order_id : vérifiez l’identifiant.
404REDEMPTION_NOT_FOUNDAucun coupon utilisé pour cette commande : rien à rendre.
410COUPON_EXPIREDLe code a expiré : retirez la réduction.
422COUPON_NOT_PERCENTAGECe coupon n’est pas un pourcentage et ne peut pas être utilisé en livraison.
409COUPON_ALREADY_REDEEMEDRetirer la réduction : le code a été utilisé ailleurs.
409COUPON_ON_HOLDUn autre paiement a réservé le code : réessayer après details.hold_expires_at.
409ORDER_ALREADY_HAS_COUPONUne seule réduction Sourdi par commande.
409CLAIM_IN_PROGRESSLe client collecte les tampons à cet instant : réessayer après quelques secondes.
422ORDER_BELOW_MINIMUMSous-total inférieur à min_order : ne pas appliquer la réduction.
403SANDBOX_ONLYRoute réservée aux clés sandbox.
429RATE_LIMITEDRespecter RateLimit-Reset avant de réessayer.