KEYFLOW

Documentation

Intégrer Keyflow

Bêta

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

01

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"
  }
}
02

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.

03

É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
}
La clé en clair n'apparaît qu'ici. Ensuite, l'API ne renvoie plus que 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é.

Deux comptes Stripe, deux secrets. Le 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 — valide
  • malformed_key — format invalide, refusé sans interroger la base
  • not_found — clé inconnue
  • revoked — révoquée définitivement
  • suspended — suspendue, réversible
  • expired — date dépassée
  • product_mismatch — clé d'un autre produit
  • activation_limit_reached — toutes les machines sont prises

Contraintes

  • device_id8 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 stableMachineGuid sous Windows, IOPlatformUUID sous macOS, /etc/machine-id sous 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.

EndpointRôle
POST /v1/auth/signupCréer un compte, reçoit la première clé d'API
GET /v1/meVérifier son jeton
POST /v1/productsDéclarer un produit
GET /v1/productsLister ses produits
POST /v1/licensesÉmettre une clé
GET /v1/licensesLister, filtrer, paginer
GET /v1/licenses/{id}Consulter
PATCH /v1/licenses/{id}Modifier limite, expiration, notes
POST /v1/licenses/{id}/revokeRévoquer, définitif
POST /v1/licenses/{id}/suspendSuspendre, réversible
POST /v1/licenses/{id}/restoreRéactiver
POST /v1/licenses/{id}/revealRetrouver une clé perdue
GET /v1/licenses/{id}/activationsSuivi des machines
POST /v1/activations/releaseLibérer une machine
POST /v1/validateValider — 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é, /validate continue 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 sans I, L, O ni U pour éviter les confusions à la saisie. La lecture tolère minuscules, espaces, et O pour 0.