Developing Bitrix24 integration via webhooks

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
    1175
  • 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

Bitrix24 Webhook Integration Development

The most common complaint about webhook integrations with Bitrix24 is: "it worked, then it stopped." The cause is almost always the same — the handler started responding slower than 5 seconds, Bitrix24 stopped calling it, and nobody noticed because there were no logs. Building a reliable webhook-based integration requires understanding how Bitrix24 delivers events and what happens during failures.

Webhook Integration Architecture

Webhooks in Bitrix24 work in two directions:

Outgoing webhook — Bitrix24 calls your URL when an event occurs. The subscription is configured in "Developers → Outgoing webhook" or via event.bind in the REST API. Bitrix24 makes a POST request with Content-Type: application/x-www-form-urlencoded.

Incoming webhook — your system calls Bitrix24 via a fixed URL with a token. Convenient for simple one-way integrations without OAuth.

For two-way integration, both types are typically used: incoming — to send data to Bitrix24, outgoing — to receive events.

Subscribing to Events via API

Programmatic subscription is more reliable than manual configuration in the interface — it does not depend on an administrator's actions:

// Subscribe via REST API (on behalf of an OAuth app)
$b24->call('event.bind', [
    'event' => 'ONCRMDEALUPDATE',
    'handler' => 'https://your-system.com/webhooks/bitrix24',
    'auth_type' => 0, // 0 = current user
]);

// Get list of active subscriptions
$bindings = $b24->call('event.get');

// Unsubscribe
$b24->call('event.unbind', [
    'event' => 'ONCRMDEALUPDATE',
    'handler' => 'https://your-system.com/webhooks/bitrix24',
]);

Incoming Request Structure

Body of the POST request from Bitrix24:

event=ONCRMDEALUPDATE
&event_handler_id=42
&auth[access_token]=abc123
&auth[expires]=1700000000
&auth[member_id]=a1b2c3
&auth[domain]=company.bitrix24.ru
&auth[application_token]=xyz789
&data[FIELDS][ID]=456
&data[FIELDS][STAGE_ID]=EXECUTING
&data[FIELDS][OPPORTUNITY]=150000

Important: data[FIELDS] contains only the changed fields, not the full object. To get the current state of the deal, a separate call to crm.deal.get is needed.

auth[application_token] — token to verify the request source. For on-premise Bitrix24, verify it matches the application token.

Mandatory Pattern: Immediate Response + Queue

The key requirement: the handler must return HTTP 200 within 5 seconds. Anything longer — timeout, the event is considered undelivered.

// routes/api.php (Laravel)
Route::post('/webhooks/bitrix24', function (Request $request) {
    // Token validation — fast
    if (!validateBitrixToken($request->input('auth.application_token'))) {
        return response('Forbidden', 403);
    }

    // Put in queue — fast
    ProcessBitrixEvent::dispatch($request->all());

    // Respond immediately
    return response('OK', 200);
});
// app/Jobs/ProcessBitrixEvent.php
class ProcessBitrixEvent implements ShouldQueue
{
    public $tries = 3;
    public $backoff = [60, 300, 900]; // 1 min, 5 min, 15 min

    public function handle(): void
    {
        $event = $this->payload['event'];
        $dealId = $this->payload['data']['FIELDS']['ID'];

        // Now retrieve the full object
        $deal = $this->b24->call('crm.deal.get', ['id' => $dealId]);
        // ... processing
    }
}

Handler Idempotency

One event can arrive twice: Bitrix24 retries on network issues, and bulk operations generate ONCRMDEALUPDATE for every field. The handler must be idempotent:

// Deduplication via event_handler_id + timestamp
$eventKey = md5($event . $dealId . $request->input('ts'));
if ($redis->set("processed:{$eventKey}", 1, ['NX', 'EX' => 3600])) {
    ProcessBitrixEvent::dispatch($payload);
    // If the key already exists — duplicate, ignore
}

Event Chains and Synchronisation Loops

The classic problem: an external system receives an ONCRMDEALUPDATE event, updates data in its database, then sends the update back to Bitrix24 — this generates ONCRMDEALUPDATE again, and the cycle repeats indefinitely.

Solutions:

  • Flag in the deal: set a custom field UF_CRM_SYNC_LOCK=Y before writing from the external system, check it in the handler — if Y, skip and clear the flag
  • Data hash: compare the incoming data hash with the last processed one — if they match, skip
  • Timestamp: if the event is about our own update (time ≤ 2 sec from our write) — ignore

Delivery Monitoring

Bitrix24 does not keep a delivery log for webhooks to external recipients. It must be maintained independently:

CREATE TABLE webhook_events (
    id SERIAL PRIMARY KEY,
    event_type VARCHAR(64),
    entity_id INTEGER,
    received_at TIMESTAMP DEFAULT NOW(),
    processed_at TIMESTAMP,
    status VARCHAR(16) DEFAULT 'pending', -- pending/done/error
    error_message TEXT
);

Alert: if there is no ONCRMDEALUPDATE event for 10 minutes during business hours — Bitrix24 has most likely stopped calling the handler.

Event Limitations

Event Notes
ONCRMDEALUPDATE Triggered on every change to any field
ONCRMDEALADD Does not fire when importing via API with DISABLE_PORTAL_ACTIVITY=Y
ONVOXIMPLANTCALLEND Call recording data is available with a 5–30 sec delay
ONTASKUPDATE Does not include checklist changes
ONIMBOTMESSAGEADD Only for bots registered via imbot.register

On-Premise Bitrix24: Extended Events

On-premise installations provide PHP-level events that are not available in the REST API:

// Event before a deal is saved — data can be modified
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'crm', 'OnBeforeCrmDealAdd',
    [MyHandler::class, 'onBeforeDealAdd']
);

// Custom event from a module
$event = new \Bitrix\Main\Event('my_module', 'OnSomethingHappened', ['data' => $data]);
$event->send();

Development Stages

Stage Content Timeline
Design List of events, data schema, handler architecture 2–3 days
Endpoint and queue HTTP handler, Job, deduplication 3–5 days
Business logic Processing each event type, calling external systems 1–3 weeks
Loop protection Sync flags, idempotency 2–3 days
Monitoring Event log, alerts, dashboard 2–3 days
Testing Event simulation, load tests 3–5 days

Total: 3–7 weeks depending on the number of events and complexity of business logic.