| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
Vérification d'une identité particulier contre la base d'enregistrements individus FR. Score 0-100 par champ + score global agrégé.
| Header | Valeur | Requis | Description |
|---|---|---|---|
| X-API-Key | vkey-... | oui | Clé d'authentification du compte (sinon HTTP 401 missing_api_key) |
| Content-Type | application/json | oui | Type du corps de requête JSON. Le corps est parsé en JSON quelle que soit la valeur. |
| Champ | Type | Requis | Description |
|---|---|---|---|
| reference | string | non | Identifiant client retourné tel quel |
| person.first_name | string | oui | Prénom |
| person.last_name | string | oui | Nom de famille |
| person.gender | M | F | non | Civilité |
| person.birth_date | string | non* | YYYY-MM-DD recommandé (formats de date courants tolérés). Date non valide : champ ignoré et signalé status:"invalid" (invalid_birth_date), réponse 200. |
| person.birth_year | string | non | Année de naissance (AAAA). Alternative décomposée à birth_date (style Trulioo) ; l'année est le critère discriminant requis. |
| person.birth_month | string | non | Mois de naissance (MM, 01-12). Optionnel, avec birth_year. |
| person.birth_day | string | non | Jour de naissance (JJ, 01-31). Optionnel, avec birth_year. |
| person.address.street | string | non* | Rue + numéro |
| person.address.city | string | non* | Ville |
| person.address.postal_code | string | non* | 5 chiffres (zéro-padding auto) |
| person.email | string | non* | Email (validation format) |
| person.mobile | string | non* | Mobile FR strict (06/07). Invalide : champ ignoré et signalé status:"invalid" (invalid_mobile), réponse 200. |
| person.landline | string | non* | Fixe FR strict. Invalide : champ ignoré et signalé status:"invalid" (invalid_landline), réponse 200. |
| person.phone | string | non* | Téléphone FR (auto-détection mobile/fixe). Renvoie matches.phone.resolved_as = mobile | landline |
| name_threshold | number | non | Seuil de matching flou des NOMS de personne (prénom et nom uniquement) : 0.5-1.0 (défaut 0.90, ou la valeur du compte). Un nom score 0 sous ce seuil de similarité Jaro-Winkler. Baissez-le pour récupérer des noms approchants, montez-le pour être plus strict. Hors plage -> HTTP 400 invalid_name_threshold. |
| match_rule | object | non | Règle booléenne optionnelle : arbre {"and":[...]} / {"or":[...]} sur les champs (+ alias address = street ET city ET postal_code). Renvoie rule.passed. Malformée -> HTTP 400 invalid_match_rule. |
| min_score | integer | non | Seuil 1-100 (défaut 50) : un champ est satisfait si son score ≥ min_score. Pris en compte avec match_rule seulement. Hors plage -> HTTP 400 invalid_min_score. |
* Au moins un critère discriminant requis en plus de first_name + last_name : birth_date, email, mobile, landline, phone, ou une adresse complète (address.street + address.city + address.postal_code). Sinon HTTP 400 insufficient_data.
Les champs email, mobile, landline et phone acceptent la valeur en clair OU son empreinte MD5 (32 hex) / SHA256 (64 hex), auto-détectée. L'empreinte porte sur la forme canonique (email en minuscules ; téléphone national FR à 10 chiffres). Un champ haché ne renvoie que match ou no_match.
| Champ | Type | Description |
|---|---|---|
| transaction_id | string | Identifiant unique de la transaction |
| reference | string | null | Référence fournie par le client (passthrough) |
| score | number | null | Score global 0-100 (moyenne des champs avec status match / partial / no_match). Les statuts missing et not_searched sont exclus du calcul. Signal brut, non plafonné. |
| confidence | number | null | Indice de confiance global 0-100 dans l'identification : force du faisceau d'identité (pouvoir discriminant des champs qui correspondent, qui sature), unicité de la cible (pénalisée si homonymes sans contact unique fourni) et couverture. null si aucune fiche trouvée. Complète le score sans le remplacer : un score de 100 sur un homonyme ressort en confiance basse. |
| confidence_level | enum | null | Palier lisible de confidence : high (≥70, identification fiable), medium (40-69) ou low (<40, à revoir : peu de discriminant ou homonyme). null si aucune fiche. |
| ambiguous | boolean | true si plusieurs profils distincts matchent les critères fournis (homonymes au même nom+adresse avec contacts divergents). Signal informatif, n'altère pas le score : le client doit fournir plus de contacts (email, mobile, DOB) pour lever l'ambiguïté. |
| matches[field].status | enum | match, partial, no_match, not_searched, missing, invalid |
| matches[field].score | number | null | Score 0-100 pour ce champ (null si missing ou not_searched) |
| timing_ms | number | Durée du traitement côté serveur |
| rule | object | Présent uniquement si match_rule est fourni : {match_rule, passed, min_score} (règle évaluée + verdict ; n'altère ni matches ni score). |
curl -X POST https://verify.zecible.fr/api/person \ -H 'X-API-Key: vkey-...' \ -H 'Content-Type: application/json' \ -d '{ "reference": "tx-001", "person": { "first_name": "Jean", "last_name": "Dupont", "birth_date": "1980-05-15" } }'
const r = await fetch('https://verify.zecible.fr/api/person', { method: 'POST', headers: { 'X-API-Key': process.env.VERIFY_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ reference: 'tx-001', person: { first_name: 'Jean', last_name: 'Dupont', birth_date: '1980-05-15' } }) }); const data = await r.json(); console.log(data.score, data.matches);
import requests, os r = requests.post( 'https://verify.zecible.fr/api/person', headers={'X-API-Key': os.environ['VERIFY_KEY']}, json={ 'reference': 'tx-001', 'person': {'first_name': 'Jean', 'last_name': 'Dupont', 'birth_date': '1980-05-15'} } ) print(r.json()['score'])
<?php $ch = curl_init('https://verify.zecible.fr/api/person'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-API-Key: ' . getenv('VERIFY_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'reference' => 'tx-001', 'person' => ['first_name' => 'Jean', 'last_name' => 'Dupont', 'birth_date' => '1980-05-15'], ]), ]); $data = json_decode(curl_exec($ch), true); echo $data['score'];
infoPaires requête / réponse réelles, figées depuis le bac à sable de démonstration (données 100% fictives, aucune identité réelle). Aucune clé API requise.
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
Vérification d'une entreprise FR (KYB) contre la base B2B : SIRET, raison sociale, contacts, dirigeants. Score 0-100 par champ.
| Header | Valeur | Requis | Description |
|---|---|---|---|
| X-API-Key | vkey-... | oui | Clé d'authentification du compte (sinon HTTP 401 missing_api_key) |
| Content-Type | application/json | oui | Type du corps de requête JSON. Le corps est parsé en JSON quelle que soit la valeur. |
| Champ | Type | Requis | Description |
|---|---|---|---|
| reference | string | non | Identifiant client retourné tel quel |
| business.siret | string | non* | 14 chiffres (établissement) |
| business.siren | string | non* | 9 chiffres (entité juridique) |
| business.vat_intracom | string | non | N° TVA intracom (dérivé du SIREN, vérifié par clé) |
| business.name | string | non* | Raison sociale |
| business.legal_form | string | non | Forme juridique (SAS, SARL, EURL...) |
| business.naf | string | non | Code NAF / APE (ex : 7311Z) |
| business.address.street | string | non | Rue + numéro |
| business.address.city | string | non | Ville |
| business.address.postal_code | string | non | 5 chiffres |
| business.email | string | non | Email de contact |
| business.mobile | string | non | Mobile FR strict (06/07). Invalide : champ ignoré et signalé status:"invalid" (invalid_mobile), réponse 200. |
| business.landline | string | non | Fixe FR strict. Invalide : champ ignoré et signalé status:"invalid" (invalid_landline), réponse 200. |
| business.phone | string | non | Téléphone FR (auto-détection mobile/fixe). Renvoie matches.business.phone.resolved_as = mobile | landline |
| business.website | string | non | URL du site web |
| executives[N].first_name | string | non | Prénom du dirigeant ou contact |
| executives[N].last_name | string | non | Nom du dirigeant ou contact |
| executives[N].role | string | non | Fonction (président, gérant, directeur, etc.) |
| name_threshold | number | non | Seuil de matching flou des NOMS de dirigeants (prénom et nom des executives[], pas la raison sociale) : 0.5-1.0 (défaut 0.90, ou la valeur du compte). Hors plage -> HTTP 400 invalid_name_threshold. |
* Au moins un parmi : business.siret, business.siren, ou business.name + (business.address.city OU business.address.postal_code). executives[] : jusqu'à 10 entrées par requête (sinon HTTP 400 too_many_executives). Scoring best-match indépendant pour chaque entrée contre tous les contacts du SIRET de la société matchée (cap 100 contacts/SIRET). En cas d'ambiguous=true, le pool reste limité au Niveau 1 du pipeline.
Les champs siren, email, mobile, landline et phone acceptent la valeur en clair OU son empreinte MD5 (32 hex) / SHA256 (64 hex), auto-détectée (siren : 9 chiffres bruts). Un champ haché ne renvoie que match ou no_match.
| Champ | Type | Description |
|---|---|---|
| transaction_id | string | Identifiant unique de la transaction |
| reference | string | null | Référence fournie par le client (passthrough) |
| score | number | null | Score global 0-100 (moyenne des champs avec status match / partial / no_match). Les statuts missing et not_searched sont exclus du calcul. Plafonné à 50 si ambiguous=true. |
| ambiguous | boolean | true si plusieurs entreprises distinctes matchent les critères fournis (établissements du même groupe ou homonymes au même nom+adresse avec contacts divergents). Le client doit fournir SIRET ou un contact unique (email, phone, website) pour lever l'ambiguïté. |
| matches.business.<field>.status | enum | match, partial, no_match, not_searched, missing, invalid |
| matches.business.<field>.score | number | null | Score 0-100 du champ (null si missing ou not_searched) |
| matches.executives | array | Toujours présent (vide si aucun dirigeant en input). Ordre préservé = ordre d'input. |
| matches.executives[N].<field> | object | Statut + score par champ pour chaque dirigeant input (best-match indépendant, même enum que business) |
| timing_ms | number | Durée du traitement côté serveur |
curl -X POST https://verify.zecible.fr/api/business \ -H 'X-API-Key: vkey-...' \ -H 'Content-Type: application/json' \ -d '{ "reference": "tx-biz-001", "business": { "siret": "00000000000000", "name": "ACME SAS", "legal_form": "SAS" }, "executives": [ { "first_name": "Jean", "last_name": "DUPONT", "role": "président" } ] }'
const r = await fetch('https://verify.zecible.fr/api/business', { method: 'POST', headers: { 'X-API-Key': process.env.VERIFY_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ reference: 'tx-biz-001', business: { siret: '00000000000000', name: 'ACME SAS' }, executives: [{ first_name: 'Jean', last_name: 'DUPONT' }] }) }); console.log((await r.json()).score);
import requests, os r = requests.post( 'https://verify.zecible.fr/api/business', headers={'X-API-Key': os.environ['VERIFY_KEY']}, json={ 'reference': 'tx-biz-001', 'business': {'siret': '00000000000000', 'name': 'ACME SAS'}, 'executives': [{'first_name': 'Jean', 'last_name': 'DUPONT'}] } ) print(r.json()['score'])
<?php $ch = curl_init('https://verify.zecible.fr/api/business'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'X-API-Key: ' . getenv('VERIFY_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'reference' => 'tx-biz-001', 'business' => ['siret' => '00000000000000', 'name' => 'ACME SAS'], 'executives' => [['first_name' => 'Jean', 'last_name' => 'DUPONT']], ]), ]); $data = json_decode(curl_exec($ch), true); echo $data['score'];
infoPaires requête / réponse réelles, figées depuis le bac à sable de démonstration (données 100% fictives, aucune identité réelle). Aucune clé API requise.
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
| Champ | Envoyé (requête) | En base (référence) | Statut |
|---|
Aucune comparaison : la requête est rejetée avant toute recherche (400).
Reporting de consommation : nombre de requêtes + breakdown par produit facturable + total HT.
| Header | Valeur | Requis | Description |
|---|---|---|---|
| X-API-Key | vkey-... | oui | Clé d'authentification du compte (sinon HTTP 401 missing_api_key) |
| Param | Type | Requis | Description |
|---|---|---|---|
| start | string | non | Date début YYYY-MM-DD (défaut : 1er jour mois courant) |
| stop | string | non | Date fin YYYY-MM-DD (défaut : dernier jour mois courant) |
| Champ | Type | Description |
|---|---|---|
| period | object | { start, stop } - période effectivement requêtée |
| requests | number | Nombre total de requêtes |
| succeeded | number | Requêtes avec au moins un produit facturable |
| no_match | number | Requêtes exécutées sans aucun produit facturable (recherche faite, rien matché) |
| errored | number | Requêtes en erreur 4xx (non facturées) |
| failed | number | Rétro-compat : no_match + errored (requêtes non facturées) |
| errors_complete | object | { code: count } des erreurs 4xx complètes (rejets globaux) |
| errors_partial | object | { code: count } des codes invalid_* par-champ (réponses 200, non exclusif des états) |
| total_pretax | number | Total HT EUR sur la période |
| currency | string | Code devise (EUR) |
| products[] | array | Ventilation par produit (key, label, count, cpm_pretax, total_pretax) |
| timing_ms | number | Temps de traitement serveur en millisecondes |
curl 'https://verify.zecible.fr/api/usage?start=2026-05-01&stop=2026-05-31' \ -H 'X-API-Key: vkey-...'
Compteur global par clé API, fenêtre fixe d'1 minute, partagé entre /api/person, /api/business et /api/usage. Limite par défaut : 60 requêtes par minute. Surchargeable per-account (nous contacter pour les besoins entreprise).
| Header | Description |
|---|---|
| X-RateLimit-Limit | Quota maximum pour la fenêtre courante (ex : 60). 0 = compte sans limite. |
| X-RateLimit-Remaining | Quota restant après cette requête. -1 si le compte est sans limite. |
| X-RateLimit-Reset | Timestamp Unix UTC du prochain reset (début de la prochaine minute). 0 si compte sans limite. |
| Retry-After | Présent uniquement sur HTTP 429. Nombre de secondes avant le reset de la fenêtre. |
| Champ | Description |
|---|---|
| error | "rate_limit_exceeded" |
| message | Message lisible avec le nombre de secondes avant le prochain reset. |
| limit | Quota du compte. |
| reset_unix | Timestamp Unix UTC du prochain reset. |
X-RateLimit-Remaining à chaque réponse pour anticiper le throttling côté client.Retry-After avant de réessayer (backoff exponentiel inutile : la fenêtre reset à la minute exacte).# HTTP 429 Too Many Requests # Headers X-RateLimit-Limit: 60 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1779994620 Retry-After: 42 # Body { "error": "rate_limit_exceeded", "message": "Limite de 60 requetes par minute atteinte. Reessayer dans 42s.", "limit": 60, "reset_unix": 1779994620 }
Liste exhaustive des codes d'erreur renvoyés par l'API. Format de réponse uniforme : {"error": "code_machine", "message": "explication humaine"}. Toujours en JSON, charset UTF-8.
| Code | Endpoint(s) | Cause |
|---|---|---|
| missing_api_key | tous | Header X-API-Key absent ou vide. |
| invalid_api_key | tous | Clé inconnue ou révoquée. Vérifier la valeur exacte (sensible à la casse) et qu'elle correspond bien à un compte actif. |
| account_disabled | tous | Compte supprimé ou désactivé côté Zecible. Contacter le support pour réactivation. |
| ip_not_allowed | tous | IP appelante absente de la whitelist du compte. Le message contient l'IP rejetée. Ajouter l'IP via le support. |
| Code | Endpoint(s) | Cause |
|---|---|---|
| method_not_allowed | tous | POST attendu sur /api/person et /api/business. GET attendu sur /api/usage. |
| Code | Endpoint(s) | Cause |
|---|---|---|
| invalid_body | person, business | Body absent, JSON malformé, ou root non-objet. |
| invalid_reference | person, business | Le champ reference doit être une chaîne (passthrough) d'au plus 256 caractères. |
| missing_required_fields | person | person.first_name et person.last_name sont obligatoires. |
| missing_required_fields | business | Au moins un parmi business.siret, business.siren, ou business.name + business.address.city OU business.address.postal_code. |
| insufficient_data | person | Identité fournie mais aucun critère discriminant (birth_date, téléphone, email ou adresse complète) pour identifier une cible. |
| insufficient_data | business | Aucune combinaison exploitable (SIRET / SIREN / nom + adresse / nom + contact). |
| too_many_executives | business | executives[] contient plus de 10 entrées. Limiter la requête à 10 dirigeants ou en chaîner plusieurs. |
| invalid_match_rule | person | Le champ match_rule est malformé (arbre {and|or:[...]} imbriqué attendu, sur des champs person connus). |
| invalid_min_score | person | Le champ min_score doit être un entier entre 1 et 100. |
| invalid_name_threshold | person/business | Le champ name_threshold doit être un nombre entre 0.5 et 1.0. |
Ces codes ne sont pas des erreurs HTTP : la requête renvoie 200. Le champ invalide est ignoré (exclu de la recherche) et signalé dans matches.<champ>.status = invalid, le code étant repris dans error. Cas particulier : si un siret/siren invalide ne laisse aucun identifiant exploitable, la requête est alors rejetée en missing_required_fields / insufficient_data (400).
| Code | Endpoint(s) | Cause |
|---|---|---|
| invalid_birth_date | person | person.birth_date n'est pas une date valide (YYYY-MM-DD recommandé). |
| invalid_mobile | person, business | Le champ mobile doit être un numéro mobile FR (10 chiffres débutant par 06 ou 07). |
| invalid_landline | person, business | Le champ landline doit être un numéro fixe FR (pas un mobile). |
| invalid_phone | person, business | Le champ phone doit être un numéro de téléphone FR valide (10 chiffres). |
| invalid_email | person, business | Le champ email doit être une adresse email valide. |
| invalid_gender | person | Le champ gender doit être M ou F. |
| invalid_siret | business | Le champ siret doit être un SIRET valide (14 chiffres, clé de Luhn). |
| invalid_siren | business | Le champ siren doit être un SIREN valide (9 chiffres, clé de Luhn). |
| Code | Endpoint(s) | Cause |
|---|---|---|
| body_too_large | person, business | Body de requête supérieur à 32 KB. Réduire la taille du JSON (ex : limiter le nombre de dirigeants, raccourcir les champs texte). |
| Code | Endpoint(s) | Cause |
|---|---|---|
| rate_limit_exceeded | tous | Quota par minute atteint. Voir la section Rate limiting pour la stratégie de retry et les headers X-RateLimit-*. |
| Code | Endpoint(s) | Cause |
|---|---|---|
| quota_exceeded | person, business | Plafond de crédits de la période atteint (budget mensuel ou portefeuille épuisable du compte). Les headers X-Credit-Limit / X-Credit-Used / X-Credit-Remaining (EUR HT) indiquent le budget le plus contraignant. Contacter le support pour relever le plafond. |
# HTTP 4xx # Content-Type: application/json; charset=UTF-8 { "error": "invalid_birth_date", "message": "person.birth_date doit etre au format ISO 8601 (YYYY-MM-DD)." }
Le champ error est stable et destiné au traitement programmatique. Le champ message est destiné à l'affichage et peut évoluer entre versions.
Stockée uniquement dans la session de votre navigateur (sessionStorage). Aucun envoi en dehors des appels API.