Documentation
API SOLUTIONIA Verify
Une API REST simple. Trois appels suffisent pour verifier l'identite d'un utilisateur mauritanien : creer la session, rediriger, recevoir le verdict signe.
1. Demarrage rapide
Trois etapes pour integrer SOLUTIONIA Verify a votre application :
- Depuis votre backend, appelez
POST /v1/sessionsavec votre cle API. - Redirigez votre utilisateur vers l'URL hebergee renvoyee (
url_hebergee). - Recevez le verdict via un webhook signe HMAC-SHA256 sur l'URL configuree pour votre tenant.
2. Authentification
Les appels serveur utilisent une cle API portee par l'en-tete Authorization: Bearer sk_live_.... Deux environnements :
- Test —
sk_test_...: sessions bidons pour developper sans facturation. - Live —
sk_live_...: verifications reelles biometriques AWS, facturees.
Ne jamais exposer la cle en front. Toujours l'utiliser depuis un backend.
3. Creer une session
Depuis votre backend :
curl -X POST https://verify.solutionia.work/v1/sessions \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"loa": "substantial",
"langue": "fr",
"documents_autorises": ["cni_mr", "passport_mr", "residence_permit"],
"reference_externe": "user-12345",
"redirect_url_ok": "https://mon-app.mr/kyc/succes",
"redirect_url_ko": "https://mon-app.mr/kyc/echec",
"webhook_url_override": "https://mon-app.mr/api/verify/webhook"
}'Reponse :
{
"id": "a656c963-bee5-4155-8ea3-8b336156aec2",
"url_hebergee": "https://verify.solutionia.work/s/a656c963-.../?t=eyJ...",
"token_client": "eyJhbGciOi...",
"expire_le": "2026-08-09T13:45:00Z",
"statut": "created"
}| Champ | Type | Description |
|---|---|---|
loa | basic | substantial | high | Niveau d'assurance. |
langue | fr | ar | Langue du parcours. |
documents_autorises | string[] | Sous-ensemble de cni_mr, passport_mr, residence_permit. |
reference_externe | string? | Votre propre id utilisateur ou transaction. Renvoye dans le webhook. |
redirect_url_ok | url? | Redirection apres approbation. |
redirect_url_ko | url? | Redirection apres rejet. |
webhook_url_override | url? | Ecrase l'URL webhook du tenant pour cette session. |
meta | object? | Metadonnees libres associees a la session. |
4. Rediriger l'utilisateur
L'URL renvoyee (url_hebergee) contient un JWT de 15 minutes. Redirigez l'utilisateur vers cette URL — le parcours (capture doc, liveness) s'execute chez nous, aucune installation d'app.
Exemple en Node.js :
import { createVerifySession } from '@/lib/verify'
const session = await createVerifySession({
reference_externe: user.id,
redirect_url_ok: `${SITE}/kyc/succes`,
redirect_url_ko: `${SITE}/kyc/echec`,
})
return res.redirect(session.url_hebergee)Apres decision, l'utilisateur est renvoye vers votre redirect_url_ok ou redirect_url_ko.
5. Recevoir le webhook
Des que la decision est prise, nous envoyons un POST HTTP a l'URL webhook de votre tenant. Signature HMAC-SHA256 dans l'en-tete X-Signature: t=<epoch>,v1=<hmac>. Retry automatique 3 fois (1s, 3s, 9s) si le tenant ne repond pas 2xx.
Corps typique :
{
"event": "session.decided",
"session_id": "a656c963-bee5-4155-8ea3-8b336156aec2",
"tenant_id": "33ddd3a0-e464-4f63-b18a-c28bb02ee546",
"reference_externe": "user-12345",
"verdict": "approved",
"motifs": [],
"score_global": 0.93,
"extraction": {
"document_type": "PASSPORT",
"issuing_country": "MRT",
"last_name": "AHMED",
"given_names": "MOHAMED ABDALLAHI",
"document_number": "B02263198",
"nni": "1234567890",
"date_of_birth": "1988-06-12",
"date_of_expiry": "2029-06-11",
"sex": "M",
"confidence": 0.92
},
"face_match": {
"same_person": true,
"similarity_pct": 99.9,
"reasoning": "match_99.9"
},
"liveness": { "passed": true, "confidence": 85.2 },
"decide_le": "2026-08-09T12:30:20Z"
}6. Verifier la signature HMAC
Node.js :
import { createHmac, timingSafeEqual } from 'crypto'
function verifyWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map(p => {
const i = p.indexOf('=')
return [p.slice(0, i), p.slice(i + 1)]
})
)
const t = parts.t, v1 = parts.v1
if (!t || !v1) return false
// Rejet des webhooks vieux de plus de 5 min (anti-replay)
if (Math.floor(Date.now() / 1000) - parseInt(t, 10) > 300) return false
const expected = createHmac('sha256', secret)
.update(`${t}.${rawBody}`)
.digest('hex')
return timingSafeEqual(
Buffer.from(v1, 'hex'),
Buffer.from(expected, 'hex')
)
}Puis dans votre route :
export async function POST(req) {
const rawBody = await req.text()
const sig = req.headers.get('X-Signature')
if (!verifyWebhook(rawBody, sig, process.env.VERIFY_WEBHOOK_SECRET)) {
return Response.json({ error: 'invalid_signature' }, { status: 401 })
}
const payload = JSON.parse(rawBody)
// ... traiter payload.verdict, payload.extraction, etc.
return Response.json({ ok: true })
}7. Lire une session en polling
Si vous preferez le polling au webhook :
curl https://verify.solutionia.work/v1/sessions/<session_id> \
-H "Authorization: Bearer sk_live_..."Statuts possibles :
created— session cree, en attente de l'utilisateurin_progress— parcours en coursapproved— verifierejected— refuse (voir motifs)requires_review— zone grise, revue humaine requiseexpired— token expire (15 min)
8. Codes de rejet (motifs)
| Motif | Signification |
|---|---|
no_nni | NNI absent ou non conforme (pas 10 chiffres). |
not_mauritanian | Document emis par un autre pays que la Mauritanie. |
face_mismatch:XX | Face-match Rekognition sous 90 % de similarite. |
face_borderline:XX | Similarite 90-99 %, revue humaine recommandee. |
liveness_failed:XX | AWS Face Liveness sous le seuil de confiance. |
no_document | Aucun document n'a ete uploade. |
no_biometric | Aucun selfie/liveness enregistre. |
attempts_exceeded:step | Trop de tentatives sur une etape (5 doc, 3 liveness, 1 face-match). |
extract_mock | Extract simule (mode test sans cle Anthropic reelle). |
Besoin d'aide a l'integration ?
Notre equipe technique vous accompagne en 1 a 2 jours pour brancher Verify a votre systeme.