Documentation
Intégrer Keyflow
Keyflow émet et valide les clés de licence de ton logiciel. Deux façons de t'en servir, et la première ne demande aucune ligne de code.
Démarrage — 5 minutes
Crée ton compte
Sur la page d'inscription, ou par l'API. La réponse contient ta clé d'API : elle n'est affichée qu'une seule fois.
curl -X POST https://appkeyflow.com/v1/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"toi@exemple.fr","password":"un-mot-de-passe-solide"}'
{
"user": { "id": "bb60bdd1-…", "email": "toi@exemple.fr",
"subscription_status": "free" },
"api_key": {
"token_prefix": "kf_live_BBrZRi",
"token": "kf_live_BBrZRiTv50MnbcD09Hk0LCwn5ciXCAhhd5jjpQIGOFI"
}
}
Déclare ton produit
curl -X POST https://appkeyflow.com/v1/products \
-H "X-API-Key: kf_live_…" -H "Content-Type: application/json" \
-d '{"name":"Mon Logiciel","default_activation_limit":1}'
Le slug est déduit du nom si tu ne le fournis pas. C'est lui que tu utiliseras partout ensuite.
Émets ta première clé
curl -X POST https://appkeyflow.com/v1/licenses \
-H "X-API-Key: kf_live_…" -H "Content-Type: application/json" \
-d '{"product_slug":"mon-logiciel","customer_email":"acheteur@exemple.fr"}'
{
"id": "c06ee4ba-…",
"key": "P914-F9Q3-J01F-WHP1",
"key_masked": "P914-****-****-WHP1",
"status": "active",
"activation_limit": 1,
"activations_used": 0
}
key_masked. Keyflow en garde une copie chiffrée : pour la retrouver, il faut passer par POST /v1/licenses/{id}/reveal.Voie principale — les clés partent toutes seules
Tu branches le webhook de ton prestataire de paiement sur une adresse Keyflow. À chaque achat abouti, la clé est émise et montrée à ton acheteur. Tu n'écris rien : ni serveur, ni email, ni tâche planifiée.
Ce que tu fais, une fois
- Dans Keyflow, onglet Ventes : crée un point de vente sur ton produit.
- Dans Stripe, crée un point de terminaison vers l'adresse affichée, abonné à
checkout.session.completed. - Colle le
whsec_de ce point de terminaison dans Keyflow. - Règle l'URL de retour de ton paiement sur :
https://appkeyflow.com/r/{CHECKOUT_SESSION_ID}
Stripe remplace {CHECKOUT_SESSION_ID} par l'identifiant de la vente. Ton acheteur atterrit sur une page qui affiche sa clé, immédiatement après avoir payé.
whsec_ demandé ici est celui de ton compte, celui où tes clients paient. Il n'a rien à voir avec ton abonnement à Keyflow.Vendre plusieurs offres du même logiciel
Crée un point de vente par offre, sur le même produit, en surchargeant le nombre de machines : un pour « 1 poste », un pour « 3 postes ». Rien d'autre à configurer.
Si une vente échoue
L'onglet Ventes affiche le compteur d'émissions et la dernière erreur. Une vente qui dépasse les limites de ton plan est émise quand même — ton client a payé, il n'a pas à en faire les frais — puis signalée.
Voie alternative — depuis ton serveur
Si tu as déjà un backend, ou si tu vends ailleurs que par Stripe, appelle l'API après chaque paiement. C'est cinq lignes dans ton gestionnaire de commande :
import requests
def livrer_licence(email_acheteur):
reponse = requests.post(
"https://appkeyflow.com/v1/licenses",
headers={"X-API-Key": CLE_API},
json={"product_slug": "mon-logiciel", "customer_email": email_acheteur},
timeout=10,
)
reponse.raise_for_status()
return reponse.json()["key"] # → "P914-F9Q3-J01F-WHP1"
const reponse = await fetch("https://appkeyflow.com/v1/licenses", {
method: "POST",
headers: { "X-API-Key": CLE_API, "Content-Type": "application/json" },
body: JSON.stringify({ product_slug: "mon-logiciel",
customer_email: emailAcheteur }),
});
const { key } = await reponse.json(); // → "P914-F9Q3-J01F-WHP1"
C'est ensuite à toi d'envoyer cette clé à ton client. Avec la voie automatique, Keyflow s'en charge.
Valider la clé dans ton logiciel
C'est le seul appel public : pas de clé d'API, puisqu'il part du poste de ton client.
curl -X POST https://appkeyflow.com/v1/validate \
-H "Content-Type: application/json" \
-d '{"key":"P914-F9Q3-J01F-WHP1","device_id":"poste-bureau-01"}'
{
"valid": true,
"reason": "ok",
"message": "Licence valide.",
"license": {
"product_slug": "mon-logiciel", "product_name": "Mon Logiciel",
"status": "active", "expires_at": null,
"activation_limit": 1, "activations_used": 1
}
}
/validate répond toujours 200, même quand il refuse. Le verdict est dans valid, la cause dans reason.C'est délibéré : un code 403 se confondrait avec une coupure réseau, et ton logiciel ne saurait pas distinguer « licence refusée » de « serveur injoignable ». Une vraie panne, elle, se signale par un 5xx ou une absence de réponse.
Les huit motifs possibles
ok— validemalformed_key— format invalide, refusé sans interroger la basenot_found— clé inconnuerevoked— révoquée définitivementsuspended— suspendue, réversibleexpired— date dépasséeproduct_mismatch— clé d'un autre produitactivation_limit_reached— toutes les machines sont prises
Contraintes
device_id— 8 caractères minimum. En dessous, l'API répond 422.- Il doit être stable entre deux démarrages, sinon chaque lancement consomme une machine.
- Champs facultatifs :
device_label,platform,app_version,product_slug.
Refus : machine supplémentaire
{
"valid": false,
"reason": "activation_limit_reached",
"message": "Limite d'activations atteinte (1/1 machines). Libere une machine avant d'en activer une nouvelle.",
"license": { "status": "active", "activation_limit": 1, "activations_used": 1 }
}
Refus : licence révoquée
{
"valid": false,
"reason": "revoked",
"message": "Cette licence a ete revoquee. Contacte le support.",
"license": { "status": "revoked", "activation_limit": 1, "activations_used": 1 }
}
Les SDK — ce qu'ils t'évitent d'écrire
Appeler /validate à la main marche, mais te laisse trois problèmes sur les bras : fabriquer un identifiant machine stable, ne pas couper ton client quand sa connexion tombe, et empêcher qu'il modifie un cache local pour prolonger sa licence. Les SDK s'en chargent.
Python
from keyflow import Keyflow
licences = Keyflow(
base_url="https://appkeyflow.com",
product_slug="mon-logiciel",
app_name="MonLogiciel",
grace_days=7,
)
resultat = licences.check(cle_saisie_par_le_client)
if not resultat.allowed:
print(resultat.message) # message prêt à afficher
raise SystemExit(1)
JavaScript · Node et Electron
const { Keyflow } = require("./keyflow");
const licences = new Keyflow({
baseUrl: "https://appkeyflow.com",
productSlug: "mon-logiciel",
appName: "MonLogiciel",
graceDays: 7,
});
const resultat = await licences.check(cleSaisie);
if (!resultat.allowed) { console.error(resultat.message); process.exit(1); }
Ce qu'ils font pour toi
- Identifiant machine stable —
MachineGuidsous Windows,IOPlatformUUIDsous macOS,/etc/machine-idsous Linux. Haché avec ton produit avant l'envoi : Keyflow ne reçoit jamais l'identifiant brut. - Tolérance hors ligne — le dernier verdict est mis en cache. Réglable par
grace_days, 7 jours par défaut. À0, la connexion est exigée à chaque démarrage. - Cache signé — en HMAC. Modifier le fichier pour prolonger une licence invalide la signature.
release()— libère la machine courante, pour un client qui change d'ordinateur.
C# · .NET 8, WPF, WinForms
using Keyflow;
using var licences = new KeyflowClient(new KeyflowOptions
{
BaseUrl = "https://appkeyflow.com",
ProductSlug = "mon-logiciel",
AppName = "MonLogiciel",
GraceDays = 7,
});
var resultat = await licences.CheckAsync(cleSaisie);
if (!resultat.Allowed) { MessageBox.Show(resultat.Message); return; }
Aucun paquet NuGet à installer : HttpClient et System.Text.Json font partie du runtime. Copie le fichier dans ton projet.
Référence
Toutes les routes de gestion attendent l'en-tête X-API-Key. Seul POST /v1/validate est public.
| Endpoint | Rôle |
|---|---|
POST /v1/auth/signup | Créer un compte, reçoit la première clé d'API |
GET /v1/me | Vérifier son jeton |
POST /v1/products | Déclarer un produit |
GET /v1/products | Lister ses produits |
POST /v1/licenses | Émettre une clé |
GET /v1/licenses | Lister, filtrer, paginer |
GET /v1/licenses/{id} | Consulter |
PATCH /v1/licenses/{id} | Modifier limite, expiration, notes |
POST /v1/licenses/{id}/revoke | Révoquer, définitif |
POST /v1/licenses/{id}/suspend | Suspendre, réversible |
POST /v1/licenses/{id}/restore | Réactiver |
POST /v1/licenses/{id}/reveal | Retrouver une clé perdue |
GET /v1/licenses/{id}/activations | Suivi des machines |
POST /v1/activations/release | Libérer une machine |
POST /v1/validate | Valider — public |
Suivi des machines
{
"items": [{
"id": "8c652356-…",
"device_fingerprint": "c078bdb4",
"device_label": null, "platform": null, "app_version": null,
"first_seen_at": "2026-08-29T01:25:29.787498Z",
"last_seen_at": "2026-08-29T01:25:29.787498Z",
"last_ip": "127.0.0.1",
"released_at": null, "is_active": true
}],
"total": 1, "limit": 50, "offset": 0
}
device_label, platform et app_version restent nuls tant que ton logiciel ne les envoie pas à la validation. Les SDK les renseignent.
Format des erreurs
{ "error": { "code": "validation_error",
"message": "device_id: String should have at least 8 characters" } }
Codes usuels : 401 jeton absent ou invalide, 404 introuvable, 409 conflit, 422 requête mal formée, 429 débit dépassé, 402 abonnement à régulariser, 403 limite du plan atteinte.
Les deux derniers appellent des actions différentes : payer, ou supprimer un élément.
Limites de débit
/v1/validate— 60 requêtes/minute par clé, 300/minute par adresse IP
Le schéma OpenAPI complet est disponible sur la référence interactive, une fois connecté.
Bon à savoir
- Tes clients ne sont jamais coupés. Même si ton abonnement Keyflow tombe en impayé,
/validatecontinue de répondre pour les licences déjà vendues. Seule l'émission de nouvelles clés est suspendue. - La révocation reste ouverte même compte suspendu : un contrôle de sécurité ne dépend jamais d'un paiement.
- Format des clés :
XXXX-XXXX-XXXX-XXXX, alphabet sansI,L,OniUpour éviter les confusions à la saisie. La lecture tolère minuscules, espaces, etOpour0.