Integration of 1C-Bitrix with Elba (SKB Kontur)

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

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.