Branchez votre caisse, votre POS ou vos outils internes sur le programme de fidélité de votre établissement. L'API publique vous permet de créditer des points à l'achat, de valider les codes gagnants et de consulter le solde et l'historique d'un client par numéro de téléphone.
4. Endpoints
Programme de fidélité
GET/api/v1/programScope : loyalty:read
Configuration publique du programme de fidélité de l'établissement lié à la clé. Si le programme est désactivé, la réponse reste 200 avec "active": false(c'est une lecture de configuration, pas une erreur).
Réponse 200
{
"active": true,
"businessName": "Café Central",
"pointsPerTnd": 1,
"rewardsCount": 4
}
Exemple
curl https://scaniha.com/api/v1/program \
-H "Authorization: Bearer sk_live_xxx"
Erreurs possibles : unauthorized, invalid_key, key_revoked, business_inactive, insufficient_scope, rate_limited, server_error.
Liste des récompenses
GET/api/v1/rewardsScope : loyalty:read
Liste les récompenses actives du programme. Si le programme est désactivé : program_inactive (409).
Réponse 200
{
"rewards": [
{ "id": "uuid", "label": "Café offert", "points_cost": 50 }
]
}
Exemple
curl https://scaniha.com/api/v1/rewards \
-H "Authorization: Bearer sk_live_xxx"
Erreurs possibles : erreurs d'authentification ci-dessus, program_inactive.
Solde & historique d'un client
GET/api/v1/customers/{phone}Scope : loyalty:read
Solde, historique récent et codes actifs pour un numéro de téléphone. Le {phone} doit être encodé pour l'URL (le + devient %2B). Un numéro inconnu renvoie balance: 0 avec des tableaux vides — ce n'est pas une erreur 404.
Réponse 200
{
"phone": "+21655555555",
"balance": 85,
"recent": [
{ "delta": 75, "reason": "purchase", "note": "Achat de 75 TND", "created_at": "2026-06-17T10:00:00Z" },
{ "delta": 10, "reason": "welcome", "note": "Bienvenue", "created_at": "2026-06-17T09:59:00Z" }
],
"activeWins": [
{ "code": "K7F-3QZ", "label": "Café offert", "expires_at": "2026-06-18T...", "created_at": "..." }
],
"activeRedemptions": [
{ "code": "A2B-CDE", "label": "Dessert", "expires_at": "2026-06-19T...", "created_at": "..." }
]
}
Exemple
curl https://scaniha.com/api/v1/customers/%2B21655555555 \
-H "Authorization: Bearer sk_live_xxx"
Erreurs possibles : erreurs d'authentification, validation_error (téléphone invalide).
Créditer des points (achat)
POST/api/v1/points/awardScope : loyalty:write
Crédite un achat. amountest le montant dépensé en TND ; les points ajoutés valent round(amount × pointsPerTnd). Le bonus de bienvenue est ajouté automatiquement lors de la première écriture au grand-livre du client.
Corps de la requête
{
"phone": "+21655555555",
"amount": 75,
"note": "Optionnel, ≤ 120 caractères"
}
amount doit être un nombre fini strictement supérieur à 0. Si noteest omis, une note lisible est générée automatiquement (ex. : "Achat de 75 TND").
Réponse 200
{
"phone": "+21655555555",
"pointsAdded": 75,
"welcomeAdded": 10,
"balance": 85
}
Exemple
curl -X POST https://scaniha.com/api/v1/points/award \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"phone":"+21655555555","amount":75,"note":"Table 4"}'
Erreurs possibles : erreurs d'authentification, insufficient_scope, validation_error (téléphone ou montant invalide), program_inactive.
Échanger une récompense
POST/api/v1/rewards/redeemScope : loyalty:write
Débite les points et émet un code de récompense à présenter en caisse. Si le solde est insuffisant : insufficient_points (409) avec error.missing = points manquants.
Corps de la requête
{
"phone": "+21655555555",
"reward_id": "uuid"
}
Réponse 200
{
"code": "A2B-CDE",
"rewardLabel": "Café offert",
"expiresAt": "2026-06-19T...",
"balance": 35
}
Exemple
curl -X POST https://scaniha.com/api/v1/rewards/redeem \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"phone":"+21655555555","reward_id":"<uuid>"}'
Erreurs possibles : erreurs d'authentification, insufficient_scope, validation_error, not_found (récompense introuvable), insufficient_points, program_inactive.
Valider un code
POST/api/v1/codes/validateScope : loyalty:write
Valide un code gagnant (roue) ou un code de récompense. redeem vaut false par défautsur l'API : par défaut, le code est seulement consulté (« peek ») sans être consommé. Passez "redeem": truepour le consommer (un code ne peut être consommé qu'une seule fois).
Corps de la requête
{
"code": "A2B-CDE",
"redeem": false
}
Réponse 200 — code introuvable
{ "found": false }
Un code appartenant à un autre établissement renvoie également { "found": false } — chaque clé ne voit que ses propres codes.
Réponse 200 — code trouvé
{
"found": true,
"kind": "win",
"status": "valid",
"label": "Café offert",
"customerPhone": "+21655555555",
"expiresAt": "2026-06-18T...",
"redeemedAt": null,
"pointsCost": null
}
Sémantique des champs
- •
kind ∈ win | reward. - •
status ∈ valid | redeemed | expired | already | cancelled. valid n'apparaît qu'en mode peek (redeem = false). cancelled ne concerne que les codes de récompense. - •
customerPhone peut être null(ex. : un gain de la roue obtenu sans téléphone). Votre intégration doit tolérer la valeur null. - •
redeemedAt n'est présent que pour status: "already" (code déjà collecté). - •
pointsCost n'est présent que pour kind: "reward" (null/absent pour les gains). - •
expiresAt n'est présent que pour status: "valid" (mode peek).
Exemple
curl -X POST https://scaniha.com/api/v1/codes/validate \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"code":"A2B-CDE","redeem":true}'
Erreurs possibles : erreurs d'authentification, insufficient_scope, validation_error (code trop court ou manquant).
Une question sur l'intégration ?
Contactez-nous →