FedTopUp
FedKYCopenapi.json

Documentation

Six étapes, et vous avez terminé : créez l'application, prenez une clé de production, créez une vérification, redirigez la personne, recevez le webhook, vérifiez le résultat.

1. Authentification

Toutes les routes attendent votre clé dans l'en-tête :Authorization: Bearer fkyc_live_…

Cette clé est un secret de backend. Elle ne doit jamais apparaître dans le code d'une page web, dans une application mobile, ni dans un dépôt public. Une clé qui fuit se révoque depuis votre portail, et la révocation est immédiate.

Un seul environnement : la production. Les clés fkyc_live_…ne voient que les vérifications de votre application.

2. Créer une vérification

curl -X POST https://kyc.fedtopup.com/api/v1/verifications \
  -H "Authorization: Bearer $FEDKYC_API_KEY" \
  -H "Idempotency-Key: commande-4821" \
  -H "Content-Type: application/json" \
  -d '{
    "external_user_id": "customer_98734",
    "required_level": "standard",
    "country": "HT",
    "document_types": ["national_id", "driver_license", "passport"],
    "return_url": "https://votre-site.com/kyc/retour"
  }'

Idempotency-Key est obligatoire. Deux appels avec la même clé rendent la même vérification : un double clic, une reprise après coupure réseau, un rejeu de file — rien ne crée de doublon.

external_user_id est votre identifiant. Il peut être opaque : nous n'avons besoin ni de l'e-mail, ni du nom, ni de quoi que ce soit d'autre. Et « 123 » chez vous n'a aucun rapport avec « 123 » chez une autre application.

3. Rediriger la personne

La réponse porte verification_url. Envoyez-y la personne. Tout s'y passe : la caméra, le document, le visage. Votre site ne manipule jamais ces images.

Le retour du navigateur sur votre return_url n'est pas une preuve. N'importe qui peut ouvrir cette adresse. La preuve, c'est le webhook signé, ou un appel àGET /v1/verifications/{id}.

4. Statuts

createdCréée, la personne n'a pas encore ouvert le lien.
startedLe parcours est ouvert.
document_submittedLe document est déposé.
processingAnalyse en cours.
retry_requiredLes preuves ne suffisent pas. La personne peut recommencer.
verifiedIdentité vérifiée.
rejectedRefusée.
unable_to_verifyTentatives épuisées sans conclusion.
expiredLe lien a expiré.
cancelledAnnulée par votre application.
technical_errorIncident de notre côté.

Il n'existe aucun statut d'attente d'approbation humaine. Un résultat incertain devient retry_required, jamais verified.

5. Webhooks

Déclarez vos points de réception dans le portail. Chaque envoi porte :

FedKYC-Signature: t=1789962474,v1=9f2a…c1
FedKYC-Timestamp: 1789962474
FedKYC-Event-Id:  fkyc_evt_3K8…
FedKYC-Event-Type: verification.verified

La signature est HMAC-SHA256(secret, `$${t}.$${corpsBrut}`), en hexadécimal. Vérifiez-la sur le corps brut, avant tout parsing JSON — un JSON.parse suivi d'un JSON.stringify ne redonne pas les mêmes octets.

Node.js

import crypto from "node:crypto";

export function verifier(corpsBrut, entete, secret) {
  const p = Object.fromEntries(entete.split(",").map(x => x.split("=")));
  const t = Number(p.t);
  if (!Number.isFinite(t)) return false;
  if (Math.abs(Date.now() / 1000 - t) > 300) return false;   // anti-rejeu
  const attendu = crypto.createHmac("sha256", secret)
                        .update(`${t}.${corpsBrut}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(attendu), Buffer.from(p.v1));
}

Python

import hmac, hashlib, time

def verifier(corps_brut: bytes, entete: str, secret: str) -> bool:
    p = dict(x.split("=", 1) for x in entete.split(","))
    t = int(p["t"])
    if abs(time.time() - t) > 300:
        return False
    attendu = hmac.new(secret.encode(),
                       f"{t}.".encode() + corps_brut,
                       hashlib.sha256).hexdigest()
    return hmac.compare_digest(attendu, p["v1"])

PHP

function verifier(string $corpsBrut, string $entete, string $secret): bool {
    parse_str(str_replace(',', '&', $entete), $p);
    $t = (int) ($p['t'] ?? 0);
    if (abs(time() - $t) > 300) return false;
    $attendu = hash_hmac('sha256', $t . '.' . $corpsBrut, $secret);
    return hash_equals($attendu, $p['v1'] ?? '');
}

FedKYC-Event-Id est unique : gardez-le et ignorez un identifiant déjà traité. Un webhook peut arriver deux fois — coupure réseau, reprise automatique, relance manuelle depuis votre portail.

En cas d'échec, nous reprenons avec un intervalle qui double : 30 s, 1, 2, 4, 8, 16, 32 min, puis une heure. Au bout de huit tentatives, la livraison part en lettre morte et vous pouvez la relancer à la main.

Les 10 événements
  • verification.created
  • verification.started
  • verification.document_submitted
  • verification.processing
  • verification.retry_required
  • verification.verified
  • verification.rejected
  • verification.expired
  • verification.cancelled
  • verification.technical_error

6. Preuve vérifiable hors ligne

POST /v1/verifications/{id}/proof rend un JWT signé en EdDSA (Ed25519). Vous le vérifiez chez vous avec le JWKS public, sans nous appeler :https://kyc.fedtopup.com/.well-known/jwks.json

Par défaut, ce jeton ne contient aucune donnée personnelle : ni nom, ni date de naissance, ni numéro de document. Seulement le statut, le niveau, le pays, le type de document et les dates.

Cette preuve est de courte durée (dix minutes). Elle dit « cette identité était vérifiée à cet instant », pas « elle le sera toujours ». Avant un geste sensible, redemandez le statut frais.

7. Erreurs

Toutes les erreurs ont la même forme. Testez le code, jamais le message : le code est stable, le message peut être reformulé.

{ "error": { "code": "RATE_LIMITED",
             "message": "Trop d'appels. Réessayez dans un instant.",
             "request_id": "req_8f2c…" } }
INVALID_API_KEY401Clé inconnue, révoquée, ou utilisée dans le mauvais environnement.
INSUFFICIENT_SCOPE403La clé ne porte pas le scope requis.
LIVE_NOT_ENABLED403L'accès production n'est pas encore accordé à l'application.
IDEMPOTENCY_KEY_REQUIRED400L'en-tête Idempotency-Key manque sur la création.
INVALID_RETURN_URL400return_url ne correspond à aucune URL déclarée.
VERIFICATION_NOT_FOUND404Vérification inconnue pour cette application.
INVALID_STATE409L'état actuel n'autorise pas cette opération.
MAX_ATTEMPTS_REACHED409Le plafond de tentatives est atteint.
VERIFICATION_EXPIRED410Le lien de vérification a expiré.
RATE_LIMITED429Trop d'appels sur cette fenêtre.
API_DISABLED503L'API est momentanément fermée.

request_id figure aussi dans l'en-têtex-request-id et dans vos journaux d'appels : c'est avec lui qu'on retrouve un appel précis.

8. Environnement

FedKYC ne propose pas de bac à sable : toute vérification est réelle et se fait avec une clé fkyc_live_…. Aucune donnée simulée n'existe.

FedKYC est un produit Fed Digital — Gonaïves, Haïti.