1C-Bitrix Integration with Elba (SKB Kontur)
Elba is a cloud-based tax accounting service for sole proprietors on the simplified tax regime (STS) and patent system. Unlike MoeDelo and Kontur.Accounting, Elba is aimed at very small businesses with simplified operations. Elba's API is limited: it provides access to documents (invoices, acts) and counterparties, but not to full transaction management. This defines both the capabilities and the constraints of the integration with Bitrix.
What Is Available Through the Elba API
Elba API (https://api.e-kontur.ru/api/v1/): authorization via OAuth 2.0 (Authorization Code or Client Credentials). Available resources: counterparties (counterparties), invoices (payment invoices), acts (completion acts). Creating revenue and expense entries directly through the API is not available — only through the Elba UI.
OAuth 2.0 Authorization
class ElbaOAuthService
{
private string $clientId;
private string $clientSecret;
private string $redirectUri;
private string $tokenUrl = 'https://auth.kontur.ru/connect/token';
public function getClientCredentialsToken(): string
{
// For server-side integration — Client Credentials flow
$ch = curl_init($this->tokenUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'scope' => 'elba.api',
]),
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
// Cache the token for (expires_in - 60) seconds
$this->cacheToken($response['access_token'], $response['expires_in'] - 60);
return $response['access_token'];
}
}
API Client
class ElbaApiClient
{
private ElbaOAuthService $auth;
private string $baseUrl = 'https://api.e-kontur.ru/api/v1';
public function request(string $method, string $path, array $data = []): array
{
$token = $this->auth->getCachedToken();
$ch = curl_init($this->baseUrl . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"Authorization: Bearer {$token}",
],
CURLOPT_POSTFIELDS => in_array($method, ['POST', 'PUT'])
? json_encode($data) : null,
]);
$json = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new \RuntimeException("Elba API error {$httpCode}: {$json}");
}
return json_decode($json, true) ?? [];
}
}
Creating and Searching Counterparties
public function findOrCreateCounterparty(\Bitrix\Sale\Order $order): string
{
$props = $order->getPropertyCollection();
$inn = $props->getItemByOrderPropertyCode('INN')?->getValue();
$email = $props->getUserEmail();
if ($inn) {
// Search by tax ID
$list = $this->client->request('GET', '/counterparties?inn=' . urlencode($inn));
if (!empty($list)) {
return $list[0]['id'];
}
}
// Create counterparty
$isLegal = !empty($inn);
$payload = $isLegal ? [
'fullName' => $props->getItemByOrderPropertyCode('COMPANY')?->getValue() ?? '',
'inn' => $inn,
'kpp' => $props->getItemByOrderPropertyCode('KPP')?->getValue() ?? '',
'email' => $email,
] : [
'fullName' => $props->getBuyerName(),
'email' => $email,
'type' => 'individual',
];
$created = $this->client->request('POST', '/counterparties', $payload);
return $created['id'];
}
Creating an Invoice
The primary integration scenario for sole proprietors on STS: when an order is created, automatically generate an invoice in Elba and send it to the customer by email.
public function createInvoiceForOrder(\Bitrix\Sale\Order $order): array
{
$counterpartyId = $this->findOrCreateCounterparty($order);
$items = [];
foreach ($order->getBasket() as $item) {
$items[] = [
'name' => $item->getField('NAME'),
'count' => $item->getQuantity(),
'price' => $item->getPrice(),
'unit' => 'pcs.',
'ndsRate' => 'NoNds', // STS — VAT-exempt. Options: Nds0, Nds10, Nds20
];
}
// Shipping as a separate line item
$deliveryPrice = $order->getField('PRICE_DELIVERY');
if ($deliveryPrice > 0) {
$items[] = [
'name' => 'Shipping',
'count' => 1,
'price' => $deliveryPrice,
'unit' => 'service',
'ndsRate' => 'NoNds',
];
}
$invoice = $this->client->request('POST', '/invoices', [
'number' => $order->getField('ACCOUNT_NUMBER'),
'date' => date('Y-m-d'),
'counterpartyId' => $counterpartyId,
'items' => $items,
'comment' => 'Order from website #' . $order->getField('ACCOUNT_NUMBER'),
'paymentDueDate' => date('Y-m-d', strtotime('+3 days')),
]);
return $invoice;
}
Elba Limitations and Workarounds
No API for recording payments. When an order is paid in Bitrix, it is not possible to automatically mark the invoice as paid in Elba via the API. Workaround: webhook from payment systems → email notification to the accountant with the invoice number. Alternatively, use Elba's bank integration — when funds arrive in the business account, Elba automatically matches the payment to the outstanding invoice.
API rate limits. Elba's API has request quotas. For stores with a high order volume — use a dispatch queue via \Bitrix\Main\Agent or a dedicated worker.
Only invoices and acts, not delivery notes. For goods shipments, an act document is not fully appropriate from a legal standpoint. For sole proprietors on STS this is typically inconsequential, but for LLCs — consider Kontur.Accounting instead.
Completion Act
For services (not goods), generate an act instead of an invoice:
public function createActForOrder(\Bitrix\Sale\Order $order, string $counterpartyId): array
{
// Same items structure
return $this->client->request('POST', '/acts', [
'date' => date('Y-m-d'),
'counterpartyId' => $counterpartyId,
'items' => $this->buildItems($order),
'comment' => 'Services for order #' . $order->getField('ACCOUNT_NUMBER'),
]);
}
Scope of Work
- OAuth 2.0 Client Credentials, token caching
- PHP client for Elba API
- Searching and creating counterparties
- Automatic invoice generation on order creation event
- Payment notifications (workaround for missing payments API)
- Error handling, logging, retries
Timeline: 2–4 weeks accounting for OAuth, API limitations, and full-cycle testing.







