Integration of 1C-Bitrix with Counterparty Verification Services
A B2B store or corporate portal on Bitrix receives a request from a company — and the manager spends 15 minutes manually verifying the TIN through third-party websites. Automatic counterparty verification is embedded at the moment of organization registration, order placement, or CRM deal creation, and data from the Unified State Register of Legal Entities / disqualified persons registry appears directly in the card.
Data Sources and API
Main services integrated with Bitrix:
| Service | Provides | Request Type |
|---|---|---|
| FNS Unified Register (API) | Company status, address, director | REST by TIN/OGRN |
| Kontur.Focus | Bankruptcy registries, arbitration, financial statements | REST + OAuth |
| SPARK | Reliability scoring, affiliations | REST + API key |
| Dadata.ru | Property enrichment by TIN, auto-completion | REST, simple key |
| Transparent Business (FNS) | Disqualified persons, tax debts | Public API |
Dadata is the most common choice for auto-completion when entering properties: by TIN it returns full name, KPP, OGRN, legal address, status. Kontur.Focus or SPARK are connected when deep financial analysis is needed.
Integration Architecture
The integration works in two modes:
Mode 1 — online verification when entering TIN. A client enters the TIN into the organization registration or profile form. An AJAX request goes to an intermediate PHP controller, which queries the Dadata API and returns the properties to the front. The user sees the form filled automatically.
Mode 2 — background verification of existing counterparties. A Bitrix agent (CAgent) runs at night, iterates through companies from b_highload_block_entity (HL-block "Organizations") or from the users table, sends the TIN to the API, saves the result.
Storage of Counterparty Data
For storing organization properties — HL-block CompanyRequisites:
-
UF_INN— TIN (string, unique index) -
UF_OGRN— OGRN -
UF_KPP— KPP -
UF_FULL_NAME— full name -
UF_ADDRESS_LEGAL— legal address -
UF_DIRECTOR— director -
UF_STATUS— status (active,liquidated,reorganizing) -
UF_RISK_SCORE— risk score (if SPARK/Focus is used) -
UF_LAST_CHECK— date of last verification -
UF_RAW_DATA— raw API response JSON
Binding to Bitrix user — the UF_USER_ID field on the user or connection through the COMPANY_ID property in the profile.
Dadata Request: Implementation
class DadataContragentChecker
{
private string $apiKey;
private string $secretKey;
public function __construct(string $apiKey, string $secretKey)
{
$this->apiKey = $apiKey;
$this->secretKey = $secretKey;
}
public function getByInn(string $inn): ?array
{
$cacheKey = 'dadata_inn_' . md5($inn);
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(86400, $cacheKey, '/dadata/inn/')) {
return $cache->getVars();
}
$response = (new \Bitrix\Main\Web\HttpClient())->post(
'https://suggestions.dadata.ru/suggestions/api/4_1/rs/findById/party',
json_encode(['query' => $inn, 'count' => 1]),
[
'Content-Type' => 'application/json',
'Authorization' => 'Token ' . $this->apiKey,
'X-Secret' => $this->secretKey,
]
);
$data = json_decode($response, true);
if (empty($data['suggestions'][0])) {
return null;
}
$result = $this->normalizeResponse($data['suggestions'][0]);
$cache->startDataCache(86400, $cacheKey, '/dadata/inn/');
$cache->endDataCache($result);
return $result;
}
private function normalizeResponse(array $suggestion): array
{
$data = $suggestion['data'];
return [
'full_name' => $data['name']['full_with_opf'] ?? '',
'inn' => $data['inn'] ?? '',
'kpp' => $data['kpp'] ?? '',
'ogrn' => $data['ogrn'] ?? '',
'address' => $suggestion['unrestricted_value'] ?? '',
'director' => $data['management']['name'] ?? '',
'status' => strtolower($data['state']['status'] ?? 'unknown'),
];
}
}
24-hour caching is mandatory. Without it, when checking in bulk, API limits are quickly reached (Dadata: 10,000 requests per day on the free plan).
Integration into Registration Form
The bitrix:system.auth.registration component is redefined in the template. A TIN field with JavaScript auto-completion is added to the form:
document.getElementById('inn-field').addEventListener('blur', async function() {
const inn = this.value.replace(/\D/g, '');
if (inn.length !== 10 && inn.length !== 12) return;
const resp = await fetch('/local/ajax/check-inn.php?inn=' + inn);
const data = await resp.json();
if (data.success) {
document.getElementById('company-name').value = data.full_name;
document.getElementById('kpp-field').value = data.kpp;
document.getElementById('ogrn-field').value = data.ogrn;
document.getElementById('address-field').value = data.address;
if (data.status !== 'active') {
showWarning('Company is not active');
}
}
});
The /local/ajax/check-inn.php endpoint is a thin controller that calls DadataContragentChecker and returns JSON.
Integration with Bitrix24 CRM
If CRM is used, verification is embedded when creating a contact or company through events:
-
OnAfterCrmContactAdd/OnAfterCrmCompanyAdd— triggers background verification through a handler - Result is written to a UF field of the CRM company
- For low scoring or "liquidated" status — a task for manual verification is automatically created for the manager
AddEventHandler('crm', 'OnAfterCrmCompanyAdd', function(&$fields) {
$inn = $fields['fields']['UF_INN'] ?? '';
if (!$inn) return;
// Run asynchronously through agent
\CAgent::AddAgent(
"checkContragentAgent({$fields['id']}, '{$inn}');",
'my_module',
'N',
60
);
});
Periodic Re-verification
Companies change status — they liquidate, reorganize. The agent re-checks records older than 30 days:
function reCheckContragentsAgent(): string
{
$connection = \Bitrix\Main\Application::getConnection();
$rows = $connection->query(
"SELECT ID, UF_INN FROM b_hl17_company_requisites
WHERE UF_LAST_CHECK < DATE_SUB(NOW(), INTERVAL 30 DAY)
LIMIT 50"
);
$checker = new DadataContragentChecker(DADATA_API_KEY, DADATA_SECRET);
while ($row = $rows->fetch()) {
$data = $checker->getByInn($row['UF_INN']);
if ($data) {
updateHlBlockRecord($row['ID'], $data);
}
}
return __FUNCTION__ . '();';
}
The 50 record limit per run ensures the agent doesn't exceed PHP execution time.
Implementation Timeline
| Task Scope | Composition | Timeline |
|---|---|---|
| Property auto-completion by TIN (Dadata) | AJAX + cache + registration form | 2–4 days |
| HL-block of counterparties + background agent | Storage + periodic verification | 1 week |
| Full integration (CRM + scoring + notifications) | Kontur.Focus or SPARK + CRM integration | 2–3 weeks |







