Skip to main content

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 typeDescriptionEndpoint
Company ProfileCompany legal information/users/{userId}/company-profile
Company OfficersList of directors/users/{userId}/company-officers
Beneficial OwnersUltimate beneficial owners/users/{userId}/beneficial-owners
Background Check V2Compliance screening/users/{userId}/background-check-v2
Conformity CheckCompliance control results/users/{userId}/conformity-checks
Articles of AssociationCompany 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/latest

Wait 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-profile

Checkpoints:

  • insee_closing_date must be null (company is active)
  • rncs_closing_date must be null (not removed from the RCS)
  • Confirm that legal.form complies with your acceptance policy

6.2 Company officers

GET/api/v6/users/{userId}/company-officers?exclude_auditors=1

Checkpoints:

  • At least one officer with an active role (end_date = null)
  • Consistent identity information

6.3 Beneficial owners

GET/api/v6/users/{userId}/beneficial-owners

Checkpoints:

  • At least one declaration
  • total_owned_parts >= 25% for declared owners

6.4 Background Check - Risk analysis

GET/api/v6/users/{userId}/background-check-v2

How to read the result:

{
"acceptable": "OK",
"reasons": [],
"warnings": [],
"ratio": 0.0,
"details": {
"nodes": [...],
"links": [...]
}
}
FieldExpected valueAction if NOK
acceptable"OK"Review reasons if different
reasons[] (empty)Manual investigation required
ratio< 0.3High ratio of collective procedures

Risks reported in reasons:

  • MANAGEMENT_DIFFICULTIES: Management issues
  • FINANCIAL_DIFFICULTIES: Financial distress
  • CLOSED_COMPANY: Company closed
  • BENEFICIAL_OWNER_BLACKLISTED: Blacklisted beneficial owner
  • PEP: Politically exposed person
  • WATCHLIST: 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 }
};
}

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

ErrorCauseSolution
409 ConflictSynchronization already runningWait for the previous sync to finish
FETCH_ERRORTemporary provider errorRelaunch the synchronization
AUTH_ERRORAuthorization issueCheck the provider configuration
Empty beneficial ownersDeclaration not filedAsk the company to file its UBO statement

See also