Développeurs · version 2026-09-01
API et intégrations Fidélitas
Envoyez les ventes d'une caisse, reconnaissez les clients, créditez leurs points et recevez les événements de Fidélitas en temps réel. Une API REST en JSON, des webhooks signés et un format d'événements unique, quel que soit le logiciel d'origine.
Démarrer
- Le commerçant crée une clé dans Intégrations › API et webhooks et choisit ses droits (préréglage « Caisse » recommandé).
- Chaque requête porte l'en-tête
Authorization: Bearer fidk_…. La clé désigne le commerce : aucun identifiant de commerce à transmettre. - Adresse de base :
https://fidelitas.fr/api/v1. Réponses JSON, dates ISO 8601, montants en centimes.
Droits d'une clé
customers:readLire les clients et leurs pointscustomers:writeCréer des clientstransactions:writeEnvoyer des ventes et remboursementsloyalty:writeEnregistrer des visites et ajuster des pointsrewards:readLire les récompenses et le programmerewards:writeUtiliser des récompensesactions:runDéclencher les codes caisse (récompense, points, message)campaigns:sendLancer des campagneswebhooks:manageGérer les webhooks
Endpoints
| Méthode et chemin | Rôle | Droit |
|---|---|---|
| GET/customers | Lister / rechercher les clients (q, filter, page) | customers:read |
| POST/customers | Créer un client (consentement explicite)· Idempotency-Key | customers:write |
| GET/customers/{id} | Fiche d'un client | customers:read |
| GET/customers/{id}/points | Solde, prochaine récompense, récompenses disponibles | customers:read |
| POST/customers/{id}/visits | Enregistrer une visite· Idempotency-Key | loyalty:write |
| GET/customers/by-token/{cardToken} | Retrouver un client depuis le QR de sa carte | customers:read |
| POST/transactions | Envoyer une vente (points, visite, rattachement au scan) ; Prefer: respond-async → 202 sans attendre le traitement· Idempotency-Key | transactions:write |
| POST/transactions/{saleId}/refunds | Rembourser une vente (points retirés)· Idempotency-Key | transactions:write |
| POST/loyalty/events | Passage sans vente ou ajustement de points· Idempotency-Key | loyalty:write |
| GET/rewards | Paliers du programme et récompenses d'un client (customerId) | rewards:read |
| POST/rewards/redeem | Utiliser une récompense (code)· Idempotency-Key | rewards:write |
| GET/program | Règles du programme (tampons / points, paliers) | rewards:read |
| GET/pos/actions | Codes caisse configurés par le commerçant | actions:run |
| POST/pos/actions/{code} | Déclencher un code caisse pour un client (récompense, points, campagne, étiquette)· Idempotency-Key | actions:run |
| GET/campaigns | Campagnes prêtes à partir (brouillons) | campaigns:send |
| POST/campaigns/{id}/send | Lancer une campagne (tout le groupe) ou l'envoyer à un seul client (customer)· Idempotency-Key | campaigns:send |
| GET/webhooks | Adresses de webhooks | webhooks:manage |
| POST/webhooks | Ajouter une adresse (secret renvoyé une seule fois)· Idempotency-Key | webhooks:manage |
| DELETE/webhooks/{id} | Supprimer une adresse | webhooks:manage |
Envoyer une vente
Le client est désigné par son identifiant Fidélitas, le jeton de sa carte (QR scanné en caisse), son email ou son téléphone. Un client inconnu avec email ou téléphone est créé (sans accord marketing). La même vente envoyée deux fois ne compte qu'une fois.
curl https://fidelitas.fr/api/v1/transactions \
-H "Authorization: Bearer fidk_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ticket-2026-000123" \
-d '{
"id": "ticket-2026-000123",
"amountCents": 2450,
"currency": "EUR",
"customer": { "cardToken": "ckx…", "email": "lea@exemple.fr" },
"items": [{ "name": "Coupe", "quantity": 1, "unitCents": 2450 }]
}'{
"eventId": "cm…",
"duplicate": false,
"status": "PROCESSED",
"outcome": "visit_created",
"outcomeLabel": "Visite enregistrée",
"customerId": "cm…",
"visitId": "cm…",
"transactionId": "cm…",
"points": 1,
"error": null
}Borne ou caisse en rush : n'attendez jamais Fidélitas. Ajoutez l'en-tête Prefer: respond-async : la vente est accusée en 202 dès sa réception (statut RECEIVED) et traitée juste après. Envoyez-la sans bloquer l'encaissement, avec un délai d'attente de 2 secondes et un rejeu avec la même Idempotency-Key en cas d'échec : jamais de double. Le résultat arrive par le webhook transaction.created et dans Intégrations › Journal. Indiquez terminalId (le nom du poste, comme sur la Caisse ou le Comptoir) pour qu'une vente sans client ne soit rattachée qu'à la carte scannée sur ce poste.
Remboursement : POST /transactions/ticket-2026-000123/refunds avec { "refundId": "r-1", "amountCents": 2450 }. Les points correspondants sont retirés (jamais en dessous de zéro).
Règles de fidélité appliquées
- Si la carte du client a été scannée juste avant (30 minutes), le montant est rattaché à cette visite : pas de double comptage.
- Plusieurs achats le même jour : une seule visite (carte à tampons : un seul tampon ; carte à points : les points s'ajoutent).
- Vente sans client : rattachée à la seule carte scannée dans les minutes précédentes sur le même poste (
terminalId), sinon enregistrée sans point (chiffre d'affaires connu). Le client peut aussi rattacher lui-même son ticket depuis sa carte, avec le numéro de commande (idde la vente). - La carte Wallet est mise à jour et les automatisations du commerçant se déclenchent comme pour une visite saisie en caisse.
Webhook « Autre caisse »
Pour une caisse qui sait appeler une adresse web (ou via Zapier / Make) : le commerçant crée une connexion « Autre caisse » et vous transmet une adresse et un secret. Envoyez un événement (ou { "events": [ … ] }, 100 au plus), signé.
Types d'événements
sale.completedVentesale.refundedRemboursementcustomer.upsertedClient créé ou modifiécustomer.visitPassageloyalty.adjustedPoints ajustés
{
"id": "evt-000123",
"type": "sale.completed",
"occurredAt": "2026-09-23T10:42:00Z",
"data": {
"saleId": "ticket-000123",
"amountCents": 1250,
"currency": "EUR",
"customer": { "externalId": "C-981", "phone": "+33612345678", "firstName": "Léa" },
"locationExternalId": "boutique-1"
}
}import crypto from "node:crypto";
const body = JSON.stringify(event);
const t = Math.floor(Date.now() / 1000);
const v1 = crypto.createHmac("sha256", SECRET).update(`${t}.${body}`).digest("hex");
await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json", "Fidelitas-Signature": `t=${t},v1=${v1}` },
body,
});Réponse 202 avec le nombre d'événements acceptés et de doublons ignorés. Signature absente, fausse ou de plus de 5 minutes : 401. Format invalide : 422 avec le champ en cause. Le résultat de chaque événement est visible par le commerçant dans son journal.
Webhooks sortants
Fidélitas appelle vos adresses (https) à chaque événement. En cas d'échec (réponse autre que 2xx, ou pas de réponse en 10 secondes), nouvel essai après 1 min, 5 min, 30 min, 2 h, 6 h, 12 h puis 24 h. Une adresse en échec 50 fois de suite est mise en pause.
customer.createdNouveau clientcustomer.updatedClient modifiévisit.createdVisite enregistréetransaction.createdVente reçue d'une caissepoints.addedPoints ajoutésreward.unlockedRécompense débloquéereward.redeemedRécompense utiliséewallet.updatedCarte Wallet ajoutéeautomation.triggeredMessage automatique envoyé
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = Buffer.from(crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex"));
const received = Buffer.from(parts.v1 ?? "");
return received.length === expected.length && crypto.timingSafeEqual(expected, received);
}En-têtes : Fidelitas-Signature, Fidelitas-Event-Id (identique à chaque nouvel essai : dédoublonnez avec), Fidelitas-Event-Type.
Erreurs, limites, idempotence
- Erreurs au format
{ "error": "message lisible", "code": "VALIDATION_ERROR", "details": [ … ] }: 400 requête illisible, 401 clé invalide, 403 droit manquant (INSUFFICIENT_SCOPE), 402 abonnement inactif, 404 introuvable, 409 conflit, 422 données invalides, 429 limite atteinte. - 600 requêtes par minute et par commerce, toutes adresses confondues ; la création de client (120), les codes caisse (300), l'envoi de campagne et l'ajout de webhook (30) ont en plus leur propre limite. En-têtes
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-ResetetRetry-After. Idempotency-Keysur les écritures : la même clé avec la même requête renvoie la même réponse pendant 24 h (en-têteIdempotent-Replayed: true), sans effet en double ; avec une autre requête : 422.- Chaque réponse porte
Fidelitas-Api-VersionetX-Request-Id(à citer au support). Les changements incompatibles passeront par une nouvelle version, annoncée à l'avance.
Caisses connectées
Connexion en deux clics depuis l'espace commerçant, sans développement :
- Squaretemps réel · client transmis
- Shopify POStemps réel · client transmis
- SumUptoutes les 15 minutes · rattachement à la carte scannée
- Zettle (PayPal POS)temps réel · rattachement à la carte scannée
- HubRisetemps réel · client transmis
- Hiboutiktemps réel · client transmis
Éditeur de caisse et intéressé par un connecteur natif ? Écrivez-nous.