API Reference v1

ORI Verify API

Infrastructure de conformité KYC/KYB/AML/TM pour les institutions financières africaines. Intégrez la vérification d'identité, le screening sanctions et la surveillance des transactions en quelques heures.

KYC / KYBVérification d'identité des personnes physiques et morales
AML ScreeningCriblage temps réel sur les listes de sanctions internationales
TM + RiskDétection de transactions suspectes et scoring 5 axes
Base URLhttps://verify.ori-advisory.com/api/v1

Démarrage rapide (5 min)

Le modèle d'intégration est identique pour tous les modules : vous appelez notre API depuis votre backend, votre utilisateur complète le parcours sur notre interface hébergée, puis nous vous notifions par webhook. Aucune donnée biométrique ne transite par vos serveurs.

Votre applicationBackend / mobile
ŌORI VerifyVérification hébergée
Votre utilisateurCapture guidée
webhook kyc.completed
1Créez une session de vérification
Depuis votre backend, avec votre clé sk_test_ en sandbox.
bash
curl -X POST https://verify.ori-advisory.com/api/v1/kyc/create \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "external_user_id": "user_42" }'
json
{
  "id": "kyc_clx1a2b3c4",
  "status": "PENDING",
  "url": "https://verify.ori-advisory.com/verify/eyJ...",
  "expires_at": "2026-06-10T15:00:00Z"
}
2Redirigez l'utilisateur vers url
Par email, SMS, lien in-app ou WebView mobile. Il y capture sa pièce d'identité et passe le contrôle de présence (caméra en direct).
Infos
Recto
Verso
Selfie
Liveness
Validation
3Recevez le résultat par webhook
Dès la fin du parcours, nous POSTons l'événement à votre endpoint. Vous pouvez aussi interroger GET /kyc/{id}.
json
POST https://votre-app.com/webhooks/ori-verify

{ "event": "kyc.completed", "session_id": "kyc_clx1a2b3c4", "status": "APPROVED" }
Sandbox illimitéeLes clés sk_test_ ne consomment aucun crédit et n'émettent pas de webhooks facturés. Utilisez-les pour intégrer et tester le parcours de bout en bout avant de passer en sk_live_.

Authentification

Toutes les requêtes doivent inclure votre clé API dans le header Authorization. Vos clés sont préfixées sk_live_ (production) ou sk_test_ (sandbox).

http
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Sécurité — Ne partagez jamais vos clés API. En cas de compromission, révoquez-les immédiatement depuis le dashboard.

Environnements

ParamètreTypeRequisDescription
sk_live_...stringnonProduction — transactions réelles, webhooks actifs
sk_test_...stringnonSandbox — aucune donnée réelle, réponses simulées

Exemple cURL

bash
curl https://verify.ori-advisory.com/api/v1/kyc/create \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"external_user_id": "user_123"}'

Erreurs & Codes HTTP

L'API utilise les codes HTTP standard. Les erreurs retournent toujours un objet JSON avec un champ error.

json
{
  "error": "Validation error",
  "details": {
    "fieldErrors": { "external_user_id": ["Required"] }
  }
}
ParamètreTypeRequisDescription
200OKnonRequête traitée avec succès
201CreatednonRessource créée
204No ContentnonSuppression réussie
400Bad RequestnonParamètres invalides — voir details
401UnauthorizednonClé API manquante ou invalide
403ForbiddennonAccès refusé à cette ressource
404Not FoundnonRessource introuvable
409ConflictnonRessource déjà existante (external_id dupliqué)
429Rate LimitednonTrop de requêtes — réessayez dans 60s
502Bad GatewaynonErreur ml-service (OCR, liveness)
503UnavailablenonService temporairement indisponible

KYC — Créer une session

Crée une session de vérification KYC et retourne une URL à envoyer à votre utilisateur. Le lien est valide 1 heure.

POST/api/v1/kyc/create

Corps de la requête

ParamètreTypeRequisDescription
external_user_idstringouiVotre identifiant interne pour cet utilisateur
redirect_urlstringnonURL de retour après completion (optionnel)

Requête

json
{
  "external_user_id": "user_123",
}

Réponse 201

json
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "status": "PENDING",
  "token": "eyJhbGciOiJIUzI1NiJ9...",
  "url": "https://verify.ori-advisory.com/verify/eyJhbG...",
  "expires_at": "2026-06-10T15:00:00.000Z"
}

KYC — Consulter une session

GET/api/v1/kyc/{id}

Requête

bash
curl /api/v1/kyc/clx1a2b3c4d5e6f7g8h9 \
  -H "Authorization: Bearer sk_live_xxx"

Réponse 200

json
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "status": "APPROVED",
    "external_user_id": "user_123",
  "first_name": "Mamadou",
  "last_name": "Keita",
  "date_of_birth": "1990-03-15",
  "nationality": "MLI",
  "document_type": "NATIONAL_ID",
  "document_number": "MLI0001234567",
  "scores": {
    "liveness": 0.82,
    "ocr": 0.91,
    "face_match": 0.87
  },
  "aml_status": "CLEAR",
  "risk_level": "LOW",
  "completed_at": "2026-06-10T14:23:11.000Z",
  "created_at": "2026-06-10T14:10:00.000Z"
}

Statuts possibles

ParamètreTypeRequisDescription
PENDINGstatusnonSession créée, l'utilisateur n'a pas encore commencé
PROCESSINGstatusnonL'utilisateur a soumis ses données, traitement en cours
APPROVEDstatusnonVérification réussie — tous les seuils atteints
REVIEWstatusnonVérification soumise mais nécessite une revue manuelle
REJECTEDstatusnonVérification rejetée — fraude suspectée ou seuils non atteints

KYC — Uploader des documents (API directe)

Alternative au flow end-user : uploadez les documents directement depuis votre backend. Utilisé quand vous avez déjà capturé les documents côté client.

POST/api/v1/kyc/{id}/documents
bash
curl -X POST /api/v1/kyc/clx1a2b3c4.../documents \
  -H "Authorization: Bearer sk_live_xxx" \
  -F "file=@id_front.jpg" \
  -F "side=front" \
  -F "document_type=NATIONAL_ID"
ParamètreTypeRequisDescription
filefileouiImage JPEG, PNG, WEBP — max 10 Mo
sideenumouifront | back | selfie | liveness
document_typeenumnonNATIONAL_ID | PASSPORT | DRIVERS_LICENSE | RESIDENCE_PERMIT

KYC — Flow end-user (lien de vérification)

C'est le mode principal : vous branchez ORI Verify dans le parcours d'onboarding de votre application (comme les néobanques avec leur prestataire KYC). Votre backend crée la session, votre app redirige l'utilisateur (WebView mobile, redirect web) vers l'URL retournée — capture caméra en direct, cadrage guidé, contrôle de présence — puis vous recevez le résultat par webhook. Pour les clients non digitaux, vos agents disposent du mode guichet dans la console (Dashboard → KYC → « Vérification au guichet ») : documents seuls, contrôle visuel par l'agent.

Infos
Recto
Verso
Selfie
Liveness
Validation
1
Créez la sessionPOST /api/v1/kyc/create → retourne url
2
Envoyez l'URL à l'utilisateurPar email, SMS ou in-app — lien valide 1h
3
L'utilisateur complète le parcoursInfos → Recto → Verso → Selfie → Liveness (STANDARD) → Validation. Le verso est sauté pour un passeport ; le n° de document est extrait automatiquement par OCR.
4
Vous recevez le webhook kyc.completedStatut APPROVED ou REVIEW selon les scores

Seuils de décision automatique

ParamètreTypeRequisDescription
liveness_scorefloatnon≥ 0.55 requis pour auto-approve (MediaPipe 3D, anti-spoofing)
ocr_scorefloatnon≥ 0.40 requis — confiance de lecture du document
face_matchfloatnon≥ 0.50 requis — correspondance selfie ↔ document
Si un score est en dessous du seuil, la session passe en REVIEW (revue manuelle) et non en REJECTED. Vous pouvez ajuster les seuils depuis le dashboard dans les paramètres de conformité.

KYB — Créer un dossier

Ouvre un dossier de vérification pour une personne morale (entreprise, ONG, etc.).

POST/api/v1/kyb/create

Requête

json
{
  "company_name": "Société Générale CI",
  "registration_no": "CI-ABJ-2018-B-00123",
  "legal_form": "SA",
  "country": "CI",
  "industry": "banking",
  "website": "https://societegenerale.ci",
  "ubos": [
    {
      "first_name": "Kofi",
      "last_name": "Asante",
      "date_of_birth": "1975-04-22",
      "nationality": "GHA",
      "ownership": 35.0,
      "role": "CEO"
    }
  ]
}

Réponse 201

json
{
  "id": "kyb_clx9z8y7x6w5v4u3t",
  "status": "PENDING",
  "company_name": "Société Générale CI",
  "country": "CI",
  "ubos": [
    {
      "id": "ubo_clx...",
      "first_name": "Kofi",
      "last_name": "Asante",
      "ownership": 35.0
    }
  ],
  "created_at": "2026-06-10T14:00:00.000Z"
}

KYB — Consulter un dossier

GET/api/v1/kyb/{id}
json
{
  "id": "kyb_clx9z8y7x6w5v4u3t",
  "status": "APPROVED",
  "company_name": "Société Générale CI",
  "registration_no": "CI-ABJ-2018-B-00123",
  "country": "CI",
  "aml_status": "CLEAR",
  "risk_score": 38,
  "risk_level": "MEDIUM",
  "ubos": [ ... ],
  "documents": [
    { "type": "KBIS", "file_name": "kbis_2026.pdf" },
    { "type": "STATUTS", "file_name": "statuts.pdf" }
  ],
  "completed_at": "2026-06-10T16:00:00.000Z"
}

KYB — Mettre à jour un dossier

PATCH/api/v1/kyb/{id}
ParamètreTypeRequisDescription
statusenumnonAPPROVED | REJECTED | REVIEW
review_notesstringnonNotes de revue manuelle
risk_scoreintnonScore de risque manuel (0–100)

KYB — Flow end-user entreprise (lien de vérification)

Comme pour le KYC, vous pouvez déléguer la constitution du dossier à l'entreprise elle-même : créez le dossier avec flow: "hosted" et transmettez l'URL retournée. L'entreprise renseigne ses informations (formes juridiques OHADA et common law), dépose ses pièces et déclare ses bénéficiaires effectifs.

Entreprise
Pièces
UBO
Soumission
Liens KYC UBO

Requête

bash
curl -X POST .../api/v1/kyb/create \
  -H "Authorization: Bearer sk_test_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "company_name": "Sahel Distribution SARL",
    "country": "CI",
    "flow": "hosted"
  }'

Réponse 201

json
{
  "id": "kyb_clx...",
  "status": "PENDING",
  "url": "https://verify.ori-advisory.com/verify-kyb/eyJ...",
  "ubos": []
}

Pièces demandées (adaptées OHADA / Afrique)

ParamètreTypeRequisDescription
STATUTSrequisnonStatuts signés et enregistrés
RCCMrequisnonExtrait RCCM < 3 mois (ou équivalent : CAC Nigeria, Registrar Ghana/Kenya…)
DFEfacultatifnonDéclaration fiscale d'existence / NIF
PROOF_ADDRESSfacultatifnonJustificatif d’adresse du siège < 3 mois
FINANCIALSfacultatifnonDerniers états financiers approuvés

KYC automatique des bénéficiaires effectifs

À la soumission du dossier, une session KYC est créée pour chaque UBO détenant ≥ 25 % (seuil FATF/BCEAO). La réponse de finalisation — et le webhook kyb.completed — contiennent les liens de vérification à transmettre à chaque bénéficiaire :

json
{
  "id": "kyb_clx...",
  "status": "REVIEW",
  "ubo_kyc_links": [
    {
      "ubo_id": "ubo_clx...",
      "name": "Awa Diallo",
      "ownership": 60,
      "kyc_url": "https://verify.ori-advisory.com/verify/eyJ..."
    }
  ]
}
Le dossier passe en REVIEW à la soumission. Les statuts des KYC des UBO sont visibles dans le détail du dossier (/dashboard/kyb) et via GET /kyb/{id}.

AML — Screening d'une entité

Criblage d'une personne physique ou morale contre les listes de sanctions internationales (ONU, UE, OFAC…). Résultat disponible en moins de 2 secondes.

POST/api/v1/aml/screen

Requête

json
{
  "name": "Amadou Traoré",
  "dob": "1968-11-15",
  "nationality": "MLI",
  "entity_type": "PERSON",
  "ref_id": "kyc_session_id_ici",
  "ref_type": "kyc_session"
}

Réponse 201

json
{
  "id": "aml_clx...",
  "status": "CLEAR",
  "match_type": "CLEAR",
  "match_score": 0,
  "top_match": null
}

En cas de match

json
{
  "status": "POTENTIAL_MATCH",
  "match_type": "POTENTIAL",
  "match_score": 0.88,
  "top_match": {
    "id": "NK-12345",
    "caption": "Amadou Traoré (né 1968)",
    "score": 0.88,
    "datasets": ["un_sc_sanctions", "eu_fsf"]
  }
}
ParamètreTypeRequisDescription
CLEARstatusnonAucun match au-dessus du seuil (0.85) — entité non listée
POTENTIAL_MATCHstatusnonScore 0.85–0.94 — révision manuelle recommandée
CONFIRMED_MATCHstatusnonScore ≥ 0.95 — forte correspondance, bloquez l'opération
PENDINGstatusnonService yente indisponible — relancez dans 30s
Données couvertes — listes de sanctions internationales : ONU (CSNU), UE (Règlement consolidé), OFAC (US), et listes de personnes politiquement exposées (PEP).

AML — Lister les screenings

GET/api/v1/aml/screen
bash
GET /api/v1/aml/screen?status=POTENTIAL_MATCH&page=1&limit=50
json
{
  "screenings": [
    {
      "id": "aml_clx...",
      "entity_name": "Amadou Traoré",
      "entity_type": "PERSON",
      "status": "POTENTIAL_MATCH",
      "match_score": 0.88,
      "ref_id": "kyc_clx...",
      "ref_type": "kyc_session",
      "created_at": "2026-06-10T12:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 50
}

Décisions — Monitoring pré-transaction (temps réel)

Appelez ce endpoint avant d'exécuter la transaction chez votre PSP : la réponse synchrone contient l'outcome à appliquer. Chaque scénario déclenché ajoute son score_modifier ; l'outcome découle de vos seuils (par défaut : REVIEW ≥ 30, BLOCK ≥ 60, DECLINE ≥ 90 ; un scénario bloquant force au minimum BLOCK).

POST/api/v1/decisions

Requête (data model canonique)

json
{
  "external_id": "tx_20260704_001",
  "amount": 75000000,
  "currency": "XOF",
  "type": "wire_transfer_out",
  "direction": "OUTBOUND",
  "payment_provider": "BANK",
  "payment_method": "wire",
  "sender_name": "Sahel Trading SARL",
  "sender_account": "CI93CI042...",
  "sender_country": "CI",
  "receiver_country": "IR",
  "country_dest": "IR",
  "channel": "web",
  "ip_address": "196.201.64.12",
  "device_id": "dev_9f2a"
}

Réponse 201

json
{
  "id": "dec_clx...",
  "transaction_id": "tx_clx...",
  "phase": "PRE_AUTH",
  "outcome": "DECLINE",
  "score": 95,
  "rules": [
    { "name": "Pays à haut risque",
      "severity": "CRITICAL",
      "score_modifier": 70,
      "blocking": true },
    { "name": "Montant élevé",
      "severity": "MEDIUM",
      "score_modifier": 25,
      "blocking": false }
  ],
  "alert_ids": ["alr_..."]
}

Outcomes

ParamètreTypeRequisDescription
APPROVEoutcomenonExécutez la transaction
REVIEWoutcomenonExécutez, une alerte est ouverte pour investigation
BLOCKoutcomenonSuspendez la transaction en attendant la revue
DECLINEoutcomenonRefusez définitivement

Historique : GET /api/v1/decisions?outcome=BLOCK&phase=PRE_AUTH.

Transactions — Soumettre

Soumet une transaction au moteur de surveillance. Le moteur évalue instantanément tous les scénarios TM actifs et retourne le résultat (APPROVED / REVIEW / BLOCKED).

POST/api/v1/transactions

Requête

json
{
  "external_id": "tx_bank_9876543",
  "amount": 15000000,
  "currency": "XOF",
  "type": "wire_transfer_out",
  "country_origin": "CI",
  "country_dest": "NG",
  "account_id": "acc_001",
  "account_age_days": 12,
  "metadata": {
    "channel": "mobile_app",
    "ip": "41.203.x.x"
  }
}

Réponse 201

json
{
  "id": "tx_clx...",
  "external_id": "tx_bank_9876543",
  "status": "BLOCKED",
  "blocked": true,
  "alert_ids": ["alert_clx_aaa", "alert_clx_bbb"],
  "created_at": "2026-06-10T14:30:00.000Z"
}

Types de transaction supportés

text
cash_deposit | cash_withdrawal | wire_transfer_in | wire_transfer_out
mobile_money_in | mobile_money_out | card_payment | other

Transactions — Lister

GET/api/v1/transactions
bash
GET /api/v1/transactions?status=BLOCKED&page=1&limit=50

Connecteurs PSP — Stripe, Orange Money, Wave

Branchez directement les webhooks de vos prestataires de paiement : ORI Verify normalise leurs événements vers le data model canonique puis exécute le monitoring. Avec phase=pre, la réponse synchrone contient l'outcome — utilisez-la pour bloquer la transaction chez le PSP avant exécution.

http
POST /api/webhooks/psp/stripe?client_key=sk_live_...&phase=post
POST /api/webhooks/psp/orange-money?client_key=sk_live_...&phase=pre
POST /api/webhooks/psp/wave?client_key=sk_live_...
ParamètreTypeRequisDescription
client_keyqueryouiVotre clé API ORI Verify (identifie votre compte)
phasequerynonpre (décision synchrone avant exécution) | post (défaut, ingestion après coup)

Mapping automatique

ParamètreTypeRequisDescription
stripePSPnonpayment_intent.* / charge.* — montants en centimes convertis, devises zéro-décimale (XOF…) gérées. Signature Stripe-Signature vérifiée si STRIPE_WEBHOOK_SECRET configuré.
orange-moneyPSPnonCallbacks Web Payment / MoMo API — txnid, subscriberMsisdn → sender_account
wavePSPnonCheckout sessions / merchant payments — id, sender_mobile, client_reference
Les événements sont idempotents : un même external_id renvoyé deux fois par le PSP retourne la décision d'origine (duplicate: true) sans recréer de transaction.

Scénarios TM — Créer

Les scénarios sont des règles métier qui déclenchent des alertes. Chaque scénario contient une ou plusieurs règles (logique AND). Exemple : montant élevé ET pays à risque.

POST/api/v1/scenarios

Requête — Exemple 1 : gros virement

json
{
  "name": "Virement > 10M XOF",
  "description": "Alerte sur tout virement sortant > 10M XOF",
  "is_active": true,
  "blocking": false,
  "severity": "HIGH",
  "lookback_window": "24h",
  "rules": [
    {
      "field": "amount",
      "operator": "gte",
      "value": 10000000
    },
    {
      "field": "type",
      "operator": "in",
      "value": ["wire_transfer_out", "cash_withdrawal"]
    }
  ]
}

Requête — Exemple 2 : agrégat

json
{
  "name": "Structuring > 5M / 24h",
  "severity": "CRITICAL",
  "blocking": true,
  "rules": [
    {
      "field": "amount",
      "operator": "gt",
      "value": 0,
      "aggregate": "sum",
      "window": "24h"
    },
    {
      "field": "amount",
      "operator": "gte",
      "value": 5000000,
      "aggregate": "sum",
      "window": "24h"
    }
  ]
}

Opérateurs disponibles

ParamètreTypeRequisDescription
gt / gte / lt / ltecomparaisonnonSupérieur / inférieur à (strict ou non) — pour les nombres
eq / neqégaliténonÉgal / différent — fonctionne sur string et nombre
in / not_inlistenonValeur dans / hors d'une liste — ex: ["CI","NG","SN"]
containsstringnonLa valeur du champ contient la chaîne

Champs disponibles pour les règles

ParamètreTypeRequisDescription
amountnumbernonMontant de la transaction (en devise brute)
currencystringnonCode ISO 4217 (XOF, EUR, USD…)
typestringnonType de transaction (voir liste ci-dessus)
country_originstringnonCode ISO 3166-1 alpha-2 du pays d'origine
country_deststringnonCode ISO 3166-1 alpha-2 du pays de destination
account_age_daysnumbernonAncienneté du compte en jours

Agrégats (fenêtres temporelles)

ParamètreTypeRequisDescription
aggregate: "count"aggregatenonNombre de transactions du client sur la fenêtre
aggregate: "sum"aggregatenonSomme des montants du client sur la fenêtre
window: "30m"duréenonFenêtre de 30 minutes (suffixes : m, h, d)

Scénarios TM — Lister

GET/api/v1/scenarios
bash
GET /api/v1/scenarios?active=true&page=1&limit=50
json
{
  "scenarios": [
    {
      "id": "scen_clx...",
      "name": "Virement > 10M XOF",
      "is_active": true,
      "blocking": false,
      "severity": "HIGH",
      "rules": [ { "field": "amount", "operator": "gte", "value": 10000000 } ],
      "created_at": "2026-06-10T12:00:00.000Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 50
}

Scénarios TM — Modifier / Supprimer

PATCH/api/v1/scenarios/{id}
json
{
  "is_active": false,
  "blocking": true,
  "severity": "CRITICAL"
}
DELETE/api/v1/scenarios/{id}
La suppression d'un scénario n'efface pas les alertes déjà générées. Réponse 204 No Content.

Risk Scoring — Configuration

Configure les poids et les tables de référence du scoring de risque client. Chaque axe est noté de 0 à 100 et les poids doivent totaliser 100.

GET/api/v1/risk/config
json
{
  "geography_weight": 25,
  "customer_weight": 30,
  "product_weight": 15,
  "channel_weight": 15,
  "transaction_weight": 15,
  "country_scores": {
    "CI": 40, "NG": 55, "ML": 50,
    "__default": 40
  },
  "customer_scores": {
    "individual": 20, "pep": 80,
    "__default": 25
  },
  "risk_levels": [
    { "max": 30, "level": "LOW" },
    { "max": 55, "level": "MEDIUM" },
    { "max": 75, "level": "HIGH" },
    { "max": 100, "level": "VERY_HIGH" }
  ]
}
PUT/api/v1/risk/config
La requête PUT remplace toute la config. Les poids doivent sommer à 100, sinon erreur 400.

Axes de risque

ParamètreTypeRequisDescription
geographyaxenonPays de résidence ou de transaction (listes FATF incluses)
customeraxenonType de client : individual, pep, sme, ngo…
productaxenonProduit utilisé : cash, virement, mobile money…
channelaxenonCanal : agence, ATM, online, agent tiers
transactionaxenonComportement transactionnel : montant, fréquence, cash

Risk Scoring — Calculer un score

Calcule instantanément un score de risque pour un client ou une opération, sans créer de session KYC.

POST/api/v1/risk/score

Requête

json
{
  "country_code": "NG",
  "customer_type": "pep",
  "product_type": "cash_withdrawal",
  "channel": "third_party_agent",
  "amount_usd": 25000,
  "monthly_tx_count": 45,
  "has_cash_intensive": true,
  "has_high_risk_counterparty": false
}

Réponse 200

json
{
  "score": 87,
  "level": "VERY_HIGH",
  "axes": {
    "geography": 55,
    "customer": 80,
    "product": 60,
    "channel": 65,
    "transaction": 95
  }
}

Webhooks

ORI Verify envoie des événements HTTP POST à vos endpoints lors des changements d'état. Configurez vos endpoints depuis le dashboard.

Événements disponibles

ParamètreTypeRequisDescription
kyc.completedeventnonSession KYC finalisée (APPROVED, REVIEW ou REJECTED)
kyb.completedeventnonDossier KYB finalisé
aml.match_foundeventnonScreening AML avec POTENTIAL_MATCH ou CONFIRMED_MATCH
transaction.blockedeventnonTransaction bloquée par un scénario TM
transaction.flaggedeventnonTransaction en REVIEW suite à une alerte TM

Format du payload

json
POST https://your-app.com/webhooks/ori-verify
Content-Type: application/json
X-ORI-Signature: sha256=<HMAC-SHA256 de la payload avec votre secret>

{
  "event": "kyc.completed",
  "session_id": "clx1a2b3c4d5e6f7g8h9",
  "status": "APPROVED",
  "timestamp": "2026-06-10T14:23:11.000Z"
}

Vérification de signature

javascript
const crypto = require('crypto')

function verifyOriSignature(payload, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  )
}

// Dans votre route Express :
app.post('/webhooks/ori', (req, res) => {
  const sig = req.headers['x-ori-signature']
  const body = req.rawBody // string
  if (!verifyOriSignature(body, sig, process.env.ORI_WEBHOOK_SECRET)) {
    return res.status(401).end()
  }
  const event = req.body
  if (event.event === 'kyc.completed') {
    // Mettez à jour votre base de données
    await db.users.update({ where: { oriSessionId: event.session_id }, ... })
  }
  res.status(200).end()
})

Livraison & re-tentatives

Envoiimmédiat
Retry 1+ 5 min
Retry 2+ 15 min
Retry 3+ 1 h
Abandonalerte
Retournez impérativement un HTTP 200 pour acquitter la réception. Sans accusé, l'événement est réémis jusqu'à 3 fois (5 min, 15 min, 1 h) ; après le dernier échec, une alerte est visible dans le dashboard et l'événement reste rejouable manuellement.

Risk Scoring — Règles custom & prestataires externes

Depuis Dashboard → Risk Scoring → Règles & prestataires, vos équipes conformité créent sans coder des règles qui ajustent le score interne (± points), et branchent des prestataires de scoring externes dont le score est pondéré dans le résultat final.

Règles custom

text
SI  <champ>  <opérateur>  <valeur>   ALORS  score += <impact>

Champs     : country_code · customer_type · product_type · channel ·
             amount_usd · monthly_tx_count · has_cash_intensive
Opérateurs : eq · neq · in · not_in · gt · gte · lt · lte · contains
Impact     : -100 à +100 points (négatif = réduit le risque)

Prestataires externes (iDenfy, bureaux de crédit…)

Chaque prestataire actif reçoit l'input de scoring en POST JSON sur son endpoint HTTPS ; son score (0–100, lu au chemin configuré ex. data.risk.score) est combiné au score interne selon son poids. Un prestataire injoignable est ignoré — le scoring n'échoue jamais.

Réponse enrichie de POST /risk/score

json
{
  "score": 84,
  "level": "VERY_HIGH",
  "axes": { "geography": 65, "customer": 85, "product": 75, "channel": 35, "transaction": 60 },
  "rules_applied": [
    { "name": "Pays sous surveillance", "score_impact": 30 }
  ],
  "providers": [
    { "name": "iDenfy Risk", "score": 72, "weight": 30 }
  ]
}

SDK JavaScript / TypeScript

Le SDK officiel @ori-verify/sdk encapsule l'authentification et tous les modules. Typé de bout en bout, il fonctionne côté serveur (Node) — ne l'exposez jamais dans un bundle navigateur avec une clé sk_live_.

Installation

bash
npm install @ori-verify/sdk

Créer une session KYC

typescript
import { OriVerify } from '@ori-verify/sdk'

const ori = new OriVerify({
  apiKey: process.env.ORI_API_KEY!,            // sk_live_... ou sk_test_...
  baseUrl: 'https://verify.ori-advisory.com/api/v1',
})

const session = await ori.kyc.create({
  externalUserId: 'user_42',
  redirectUrl: 'https://votre-app.com/kyc/done',
})

// Envoyez session.url à l'utilisateur (email, SMS, in-app)
console.log(session.url)

Autres modules

typescript
await ori.kyb.create({ companyName: 'Demo SARL', registrationNo: 'CI-ABJ-...', country: 'CI' })
await ori.aml.screen({ name: 'Awa Diallo', nationality: 'SN', entityType: 'person' })
await ori.transactions.submit({ externalId: 'tx_1', amount: 75_000_000, type: 'wire_transfer_out' })
await ori.risk.score({ countryCode: 'ML', customerType: 'pep', productType: 'crypto' })
Le SDK lève une OriVerifyError typée (avec status et body) sur toute réponse non-2xx — enveloppez vos appels dans un try / catch.

API Keys — Administration

GET/api/v1/apikeys
json
{
  "keys": [
    {
      "id": "key_clx...",
      "name": "Production",
      "prefix": "sk_live_xxxx",
      "environment": "LIVE",
      "last_used_at": "2026-06-10T12:00:00Z",
      "created_at": "2026-06-01T00:00:00Z"
    }
  ]
}
POST/api/v1/apikeys
json
// Requête
{ "name": "Mobile App", "environment": "LIVE" }

// Réponse 201 — la clé complète n'est visible qu'une seule fois
{
  "id": "key_clx...",
  "key": "sk_live_AbCdEfGhIjKl...",
  "name": "Mobile App",
  "environment": "LIVE"
}
POST/api/v1/apikeys/{id}/revoke
La révocation est irréversible. Les appels utilisant cette clé retourneront immédiatement 401.