KYB & Onboarding
This guide walks you through establishing a Know Your Business (KYB) verification workflow for credit applications, account openings, or general customer onboarding.
Goal
Automatically verify:
- The company’s legal existence
- The identity of directors and legal representatives
- Beneficial owners
- The absence of collective procedures or sanctions
Data collected
| Data type | Description | Endpoint |
|---|---|---|
| Company Profile | Company legal information | /users/{userId}/company-profile |
| Company Officers | List of directors | /users/{userId}/company-officers |
| Beneficial Owners | Ultimate beneficial owners | /users/{userId}/beneficial-owners |
| Background Check V2 | Compliance screening | /users/{userId}/background-check-v2 |
| Conformity Check | Compliance control results | /users/{userId}/conformity-checks |
| Articles of Association | Company bylaws | /users/{userId}/articles-of-association |
Implementation steps
Step 1: Enable the required providers
PUT /api/v6/providers/company_legal_fr
{
"enable": true
}
PUT /api/v6/providers/company_legal_fr/settings
{
"auto_connect": true
}
Advanced compliance screening
To perform PEP (Politically Exposed Person) and sanctions checks, also enable the ComplyCube provider:
PUT /api/v6/providers/comply_cube
{
"enable": true
}
PUT /api/v6/providers/comply_cube/settings
{
"officers_check": true,
"beneficial_owners_check": true,
"background_check_nodes_check": true
}
Step 2: Create the user (company to verify)
POST /api/v6/users/legal
{
"name": "My Company SAS",
"siren": "123456789",
"group": "onboarding-2024",
"return_url": "https://mon-app.com/onboarding/complete"
}
Response:
{
"id": "d0e497f1-6052-4f0d-b876-d4e6d7cf25bc",
"type": "LEGAL",
"name": "My Company SAS",
"siren": "123456789",
"group": "onboarding-2024",
"redirect_url": "https://app.qardfinance.com/auth/..."
}
note
Store the returned id; it is required for every subsequent request.
Step 3: Create the data connection
POST /api/v6/users/{userId}/data-connections
{
"requested_data_types": [
"COMPANY_PROFILE",
"COMPANY_OFFICER",
"BENEFICIAL_OWNER",
"BACKGROUND_CHECK_V2",
"CONFORMITY_CHECK",
"ARTICLES_OF_ASSOCIATION",
"COLLECTIVE_PROCEDURE"
],
"provider_name": "company_legal_fr"
}
Step 4: Start the synchronization
POST /api/v6/users/{userId}/sync
{
"data_types": [
"COMPANY_PROFILE",
"COMPANY_OFFICER",
"BENEFICIAL_OWNER",
"BACKGROUND_CHECK_V2",
"CONFORMITY_CHECK",
"ARTICLES_OF_ASSOCIATION",
"COLLECTIVE_PROCEDURE"
]
}
Step 5: Check the synchronization status
GET
/api/v6/users/{userId}/sync/latestWait for every data_sync entry to reach the SUCCESS status before fetching data.
Step 6: Retrieve and analyze the data
6.1 Company profile
GET
/api/v6/users/{userId}/company-profileCheckpoints:
insee_closing_datemust benull(company is active)rncs_closing_datemust benull(not removed from the RCS)- Confirm that
legal.formcomplies with your acceptance policy
6.2 Company officers
GET
/api/v6/users/{userId}/company-officers?exclude_auditors=1Checkpoints:
- At least one officer with an active role (
end_date=null) - Consistent identity information
6.3 Beneficial owners
GET
/api/v6/users/{userId}/beneficial-ownersCheckpoints:
- At least one declaration
total_owned_parts>= 25% for declared owners
6.4 Background Check - Risk analysis
GET
/api/v6/users/{userId}/background-check-v2How to read the result:
{
"acceptable": "OK",
"reasons": [],
"warnings": [],
"ratio": 0.0,
"details": {
"nodes": [...],
"links": [...]
}
}
| Field | Expected value | Action if NOK |
|---|---|---|
acceptable | "OK" | Review reasons if different |
reasons | [] (empty) | Manual investigation required |
ratio | < 0.3 | High ratio of collective procedures |
Risks reported in reasons:
MANAGEMENT_DIFFICULTIES: Management issuesFINANCIAL_DIFFICULTIES: Financial distressCLOSED_COMPANY: Company closedBENEFICIAL_OWNER_BLACKLISTED: Blacklisted beneficial ownerPEP: Politically exposed personWATCHLIST: Appears on a sanctions list
Example end-to-end workflow
async function kybOnboarding(siren, companyName) {
// 1. Create the user
const user = await createUser({
name: companyName,
siren: siren,
group: "kyb-onboarding"
});
// 2. Create the data connection
await createDataConnection(user.id, {
requested_data_types: [
"COMPANY_PROFILE",
"COMPANY_OFFICER",
"BENEFICIAL_OWNER",
"BACKGROUND_CHECK_V2"
],
provider_name: "company_legal_fr"
});
// 3. Trigger the synchronization
await syncUser(user.id, {
data_types: [
"COMPANY_PROFILE",
"COMPANY_OFFICER",
"BENEFICIAL_OWNER",
"BACKGROUND_CHECK_V2"
]
});
// 4. Wait for the synchronization to finish
await waitForSyncCompletion(user.id);
// 5. Retrieve and assess the data
const [profile, officers, beneficialOwners, backgroundCheck] = await Promise.all([
getCompanyProfile(user.id),
getCompanyOfficers(user.id),
getBeneficialOwners(user.id),
getBackgroundCheckV2(user.id)
]);
// 6. Decision
const decision = evaluateKYB({
isActive: !profile.insee_closing_date,
hasOfficers: officers.length > 0,
hasBeneficialOwners: beneficialOwners.length > 0,
backgroundCheckOK: backgroundCheck.acceptable === "OK"
});
return {
userId: user.id,
decision: decision,
data: { profile, officers, beneficialOwners, backgroundCheck }
};
}
Recommended webhooks
Configure these webhooks to automate the workflow:
PUT /api/v6/webhooks/DATA_SYNC_SUCCESS
{
"url": "https://mon-app.com/webhooks/qard/sync-success"
}
PUT /api/v6/webhooks/DATA_SYNC_FETCH_ERROR
{
"url": "https://mon-app.com/webhooks/qard/sync-error"
}
Common errors
| Error | Cause | Solution |
|---|---|---|
409 Conflict | Synchronization already running | Wait for the previous sync to finish |
FETCH_ERROR | Temporary provider error | Relaunch the synchronization |
AUTH_ERROR | Authorization issue | Check the provider configuration |
| Empty beneficial owners | Declaration not filed | Ask the company to file its UBO statement |
See also
- Due Diligence - For deeper investigations
- Background Check V2 - Data format details
- Company Legal FR - Provider documentation