Documentation APICI Connect expose une API B2B pour lancer des vérifications KYC en mode machine-to-machine (M2M). Cette documentation est destinée aux équipes backend qui intègrent CI Connect côté serveur.

1. Introduction

CI Connect expose une API B2B pour lancer des vérifications KYC en mode machine-to-machine (M2M).
Objectif :
  • Créer une requête de vérification pour un citoyen
  • Suivre son statut
  • Récupérer le résultat si le citoyen accepte le partage de données
Cette documentation est faite pour les équipes backend qui intègrent CI Connect côté serveur.
Pré-requis d'accès :
  • Disposer d'un compte client vérifié sur la plateforme CI Connect
  • Disposer d'une application active (sandbox ou production)
  • Récupérer les clés de l'application : apiKey et secretKey
api/v1
Préfixe API — ex : https://api.example.com/api/v1/data-requests

2. Démarrage rapide

Étape 1 — Variables d'environnement

bash
export CI_CONNECT_BASE_URL="https://api.example.com"
export CI_CONNECT_API_KEY="pk_live_xxx_ou_pk_test_xxx"
export CI_CONNECT_SECRET_KEY="sk_xxx"

Étape 2 — Premier appel (création d'une requête)

bash
curl -X POST "$CI_CONNECT_BASE_URL/api/v1/data-requests" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $CI_CONNECT_API_KEY" \
  -H "X-Secret-Key: $CI_CONNECT_SECRET_KEY" \
  -d '{
    "nni": "12345678901", // ou "phoneNumber": "+225000000000"
    "requestedFields": ["FIRST_NAME", "LAST_NAME", "BIRTH_DATE"]
  }'
Exemple de réponse succès :
json
{
  "statusCode": 201,
  "message": "Success",
  "data": {
    "requestId": "6f7b3cf6-0a34-4d2d-9af9-0fd2b6d31d74",
    "status": "PENDING",
    "cost": {
      "base": 100,
      "fieldSurcharges": 50,
      "fieldSurchargeDetails": {
        "BIRTH_DATE": 50
      },
      "total": 150,
      "currency": "XOF"
    },
    "expiresAt": "2026-03-31T10:15:00.000Z",
    "consentTimerSeconds": 10,
    "createdAt": "2026-03-31T09:15:00.000Z"
  },
  "timestamp": "2026-03-31T09:15:00.000Z"
}

Étape 3 — Exploiter le requestId

  • Appeler GET /api/v1/data-requests/:id/status pour suivre le statut
  • Appeler GET /api/v1/data-requests/:id/result pour récupérer les données
  • Si un callback est configuré sur l'application, attendre la notification puis appeler GET /api/v1/data-requests/:id/result pour récupérer les données

3. Authentification (Sécurité)

Authentification M2M :
  • Header obligatoire X-API-Key
  • Header obligatoire X-Secret-Key
Important : les routes M2M n'utilisent pas Bearer JWT. Sans les 2 headers, la réponse est 401.
Exemple HTTP :
http
GET /api/v1/data-requests/6f7b3cf6-0a34-4d2d-9af9-0fd2b6d31d74 HTTP/1.1
Host: api.example.com
X-API-Key: pk_live_xxx
X-Secret-Key: sk_xxx
Règles d'environnement :
  • Une clé pk_test_... doit appeler les routes sandbox (/api/v1/sandbox/...)
  • Une clé production (pk_live_...) doit appeler les routes hors sandbox
  • En production, le client doit avoir l'accès production activé (hasProductionAccess), sinon 403

3.1 Champs de données possibles

Les valeurs de requestedFields doivent respecter 2 règles :
  • Utiliser des codes de champs existants dans CI Connect
  • Rester dans la liste allowedDataFields autorisée sur votre application
Pour récupérer la liste des champs actifs, utilisez :
GET /api/v1/reference-data/data-fields— optionnel : ?search=FIRST pour filtrer
bash
curl -X GET "$CI_CONNECT_BASE_URL/api/v1/reference-data/data-fields" \
  -H "Content-Type: application/json"
Exemple de réponse :
json
{
  "statusCode": 200,
  "message": "Success",
  "data": [
    {
      "id": "6c4f2a5f-d4e7-4c85-8db2-6f7f0211a31f",
      "code": "FIRST_NAME",
      "label": "Prénom",
      "category": "IDENTITY",
      "surcharge": 0,
      "isActive": true,
      "sortOrder": 1,
      "createdAt": "2026-03-01T10:00:00.000Z"
    },
    {
      "id": "6d087f8f-9f13-4d16-a0c5-d8d1a2d0c4a7",
      "code": "LAST_NAME",
      "label": "Nom",
      "category": "IDENTITY",
      "surcharge": 0,
      "isActive": true,
      "sortOrder": 2,
      "createdAt": "2026-03-01T10:00:00.000Z"
    }
  ],
  "timestamp": "2026-03-31T09:45:00.000Z"
}
Tableau de référence des champs :
CodeLabelSurchargeMontant
Note : ce tableau est chargé dynamiquement depuis GET /api/v1/reference-data/data-fields — il reflète toujours l'état réel de la plateforme.

4. Endpoints / Routes (M2M)

POST/api/v1/data-requests
Créer une requête KYC en production (statut initial PENDING).
Headers : X-API-Key, X-Secret-Key
Body :
  • nni ou phoneNumber (string, obligatoire, 11 chiffres)
  • requestedFields (string[], optionnel, min 1)
Notes :
  • Si requestedFields est omis, tous les champs autorisés de l'application sont utilisés
  • Si un champ demandé n'est pas autorisé pour l'application, la requête est rejetée (400)
json
{
  "nni": "12345678901", // ou "phoneNumber": "+225000000000"
  "requestedFields": ["FIRST_NAME", "LAST_NAME"]
}
GET/api/v1/data-requests/:id/status
Récupérer le statut d'une requête KYC.
Headers : X-API-Key, X-Secret-Key
Champs de réponse : requestId, status, cost, expiresAt, respondedAt, deviceConfig, createdAt
Statuts possibles : PENDING, APPROVED, REJECTED, EXPIRED, FAILED, CALLBACK_SENT, CALLBACK_FAILED
json
{
  "statusCode": 200,
  "message": "Success",
  "data": {
    "requestId": "6f7b3cf6-0a34-4d2d-9af9-0fd2b6d31d74",
    "status": "PENDING",
    "cost": {
      "base": 100,
      "fieldSurcharges": 50,
      "fieldSurchargeDetails": {
        "BIRTH_DATE": 50
      },
      "total": 150,
      "currency": "XOF"
    },
    "expiresAt": "2026-03-31T10:15:00.000Z",
    "respondedAt": null,
    "deviceConfig": null,
    "createdAt": "2026-03-31T09:15:00.000Z"
  },
  "timestamp": "2026-03-31T09:20:00.000Z"
}
GET/api/v1/data-requests/:id/result
Récupérer le résultat de la requête (mode polling).
Headers : X-API-Key, X-Secret-Key
Comportement :
  • data est renseigné quand le statut est APPROVED ou CALLBACK_SENT
  • Sinon data vaut null
json
{
  "statusCode": 200,
  "message": "Success",
  "data": {
    "requestId": "6f7b3cf6-0a34-4d2d-9af9-0fd2b6d31d74",
    "status": "APPROVED",
    "respondedAt": "2026-03-31T09:18:15.000Z",
    "deviceConfig": {
      "source": "mobile"
    },
    "data": {
      "FIRST_NAME": "Moussa",
      "LAST_NAME": "Traore",
      "BIRTH_DATE": "1990-05-15"
    }
  },
  "timestamp": "2026-03-31T09:21:00.000Z"
}
SANDBOXPOST/api/v1/sandbox/data-requests
Créer une requête sandbox synchrone (sans contact CII).
Body :
  • nni ou phoneNumber (string, obligatoire, valeur libre en sandbox)
  • requestedFields (string[], obligatoire, min 1)
  • simulatedAction (obligatoire : APPROVED ou REJECTED)
json
{
  "nni": "FAKE-NNI-001" // ou "phoneNumber": "+225000000000" ,
  "requestedFields": ["FIRST_NAME", "LAST_NAME"],
  "simulatedAction": "APPROVED"
}

5. Flux de traitement

Client B2B
POST /api/v1/data-requests
PENDING
Consentement Citoyen
Statut Final
Callback (si configuré)
GET /data-requests/:id
GET /data-requests/:id/result
En pratique :
  • À la création, une requête est en PENDING
  • En production, le citoyen doit consentir pour que les données soient partagées
  • Si l'application a une callbackUrl, CI Connect envoie une notification quand le résultat est disponible
  • Sans callback, faire du polling sur status et result
  • La charge utile KYC est disponible via GET .../result quand la requête est approuvée
Callback reçu ? Vous devez encore appeler GET /api/v1/data-requests/:id/result

Le payload du callback ne contient pas les données KYC — il signale uniquement que la requête est finalisée. Pour récupérer les champs demandés (prénom, nom, date de naissance, etc.), vous devez impérativement effectuer un appel GET /api/v1/data-requests/:id/result après réception du callback.

6. Erreurs & Status Codes

Codes HTTP fréquents

CodeDescription
200Lecture de statut/résultat
201Création de requête
400Payload invalide, champ non autorisé, endpoint non adapté
401Auth manquante ou invalide
403Accès refusé (compte inactif, mauvaise clé, accès prod non activé)
404Requête introuvable
429Limite de débit atteinte
500Erreur interne

Format d'erreur

json
{
  "statusCode": 403,
  "message": "L'accès à la production n'est pas activé. Utilisez /sandbox/data-requests pour tester.",
  "timestamp": "2026-03-31T09:30:00.000Z"
}
Exemple d'erreur de validation :
json
{
  "statusCode": 400,
  "message": "Validation failed",
  "errors": {
    "validationErrors": [
      "Le NNI doit contenir exactement 11 chiffres"
    ]
  },
  "timestamp": "2026-03-31T09:30:00.000Z"
}
Messages possibles côté auth/API key :
  • Clé API requise
  • Secret API requis
  • Clé API invalide ou application suspendue
  • Secret API invalide
  • Clé API de production utilisée sur une route sandbox...
  • Clé API sandbox utilisée sur une route de production...
  • Accès production non activé. Utilisez /sandbox/data-requests pour tester.

7. SDK / Librairies

SDK Node officiel : @asernum/ci-connect-sdk-node
Prérequis : Node.js >= 18

Installation

bash
pnpm add @asernum/ci-connect-sdk-node

Initialisation

typescript
import { CiConnectSDK } from "@asernum/ci-connect-sdk-node";

const sdk = new CiConnectSDK({
  apiKey: process.env.CI_CONNECT_API_KEY!,
  secretKey: process.env.CI_CONNECT_SECRET_KEY!,
  baseUrl: process.env.CI_CONNECT_BASE_URL!, // ex: https://api.example.com
  mode: "auto", // auto | live | sandbox
  timeoutMs: 10_000,
  retries: 2,
});

Premier appel

typescript
const created = await sdk.createRequest({
  nni: "12345678901",
  requestedFields: ["FIRST_NAME", "LAST_NAME"],
});

console.log(created.requestId, created.status);

Statut + résultat

typescript
const status = await sdk.getRequestStatus(created.requestId);
const result = await sdk.getRequestResult(created.requestId);

Polling automatique

typescript
const finalResult = await sdk.waitForResult(created.requestId, {
  intervalMs: 2_000,
  timeoutMs: 60_000,
  stopOnStatuses: ["APPROVED", "REJECTED"],
});

Sandbox via SDK

typescript
const sandbox = new CiConnectSDK({
  apiKey: process.env.CI_CONNECT_API_KEY!,
  secretKey: process.env.CI_CONNECT_SECRET_KEY!,
  baseUrl: process.env.CI_CONNECT_BASE_URL!,
  mode: "sandbox",
});

const sandboxResult = await sandbox.createRequest(
  { nni: "FAKE-NNI-001", requestedFields: ["FIRST_NAME"] },
  { simulatedAction: "APPROVED" },
);

Erreurs typées

  • AuthenticationError (401)
  • AuthorizationError (403)
  • NotFoundError (404)
  • ValidationError (400/422)
  • RateLimitError (429)

8. Bonnes pratiques

  • Commencer en sandbox avant toute intégration production
  • Utiliser un timeout explicite et une stratégie de retry avec backoff côté client
  • Ne jamais exposer secretKey côté frontend
  • Stocker les clés dans un gestionnaire de secrets (vault, variables chiffrées, rotation régulière)
  • Privilégier callback pour les notifications, et garder polling en fallback
  • Après réception d'un callback, toujours appeler GET .../result — le callback ne contient pas les données KYC
  • Journaliser requestId à chaque étape pour faciliter le support
  • Gérer explicitement les statuts terminaux (APPROVED, REJECTED, EXPIRED, FAILED, CALLBACK_SENT, CALLBACK_FAILED)
Rate limit :
  • Sandbox : 20 requêtes / 60 secondes par clé API
  • Global API : 100 requêtes / 60 secondes (configurable)

9. FAQ / Dépannage

Pourquoi j'ai 403 en production ?

Cela arrive typiquement si :
  • Vous utilisez une clé sandbox (pk_test_...) sur /data-requests production
  • Votre compte n'a pas encore hasProductionAccess
  • Le compte client est suspendu/rejeté

Pourquoi data est null dans GET /result ?

data est null tant que le statut n'est pas en état de partage effectif (requête encore PENDING, rejetée, expirée, ou en échec).

Quelle différence entre status et result ?

  • GET /data-requests/:id : état et métadonnées de la requête
  • GET /data-requests/:id/result : même logique de suivi, avec en plus le champ data quand disponible

Comment simuler un cas en sandbox ?

Appelez POST /api/v1/sandbox/data-requests avec :
  • Un nni fictif
  • La liste requestedFields
  • simulatedAction à APPROVED ou REJECTED

Le SDK met-il en place le polling automatiquement ?

Oui, via waitForResult(requestId, options) avec intervalle et timeout configurables.

J'ai reçu le callback — où sont les données ?

Le callback ne transporte pas les données KYC. Dès réception, appelez GET /api/v1/data-requests/:id/result pour obtenir le champ data avec les informations du citoyen.