bitrix24/b24phpsdk
Official PHP library for the Bitrix24 REST API, with two branches: v1 (stable, PHP 8.2–8.4) and v3 (active development, PHP 8.4+, new REST methods, breaking changes). Handles authentication, API calls, and batch requests.
Install the SDK (use v3 for current projects):
composer require bitrix24/b24phpsdk:"^3.7" # v3, PHP 8.4+
composer require bitrix24/b24phpsdk:"^1.0" # v1, legacy PHP 8.2-8.4
The 3.7.0 release is additive: new batch scopes, REST v3 incremental user services, Pull/Voximplant/timeman/SonetGroup wrappers, and a portal-domain redirect veto event. No breaking changes are called out in the release notes.
Initialize the service (pick one auth method):
use Bitrix24\SDK\Services\ServiceBuilderFactory;
$b24Service = ServiceBuilderFactory::createServiceBuilderFromWebhook(
config('services.bitrix24.webhook_url')
);
use Bitrix24\SDK\Core\Credentials\ApplicationProfile;
$appProfile = ApplicationProfile::initFromArray([
'CLIENT_ID' => config('services.bitrix24.client_id'),
'CLIENT_SECRET' => config('services.bitrix24.client_secret'),
'SCOPE' => 'crm,im,sale,landing', // request only what you need
]);
$b24Service = ServiceBuilderFactory::createServiceBuilderFromPlacementRequest(
\Symfony\Component\HttpFoundation\Request::createFromGlobals(),
$appProfile
);
First API call (e.g., fetch CRM deals):
$deals = $b24Service->getMainScope()->crm()->deals()->getDeals();
/src/Services/ – service scopes and typed service classes./examples/ – webhook, local app, and marketplace workflows./tests/ – integration patterns; use .env.local for credentials.CHANGELOG.md – check before upgrading, especially if you use legacy IMBot, Pull, timeman, or log endpoints.getPullScope(), Voximplant callback service, and PortalDomainUrlChangingEvent.For a Laravel app, start with one sync job:
Use builder methods to chain operations:
$deals = $b24Service
->getMainScope()
->crm()
->deals()
->getDeals(['SELECT' => ['ID', 'TITLE', 'AMOUNT']]);
Use generators for large CRM result sets:
foreach ($b24Service->getMainScope()->crm()->deals()->getDealsBatch() as $deal) {
yield $deal->getId();
}
New in 3.7.0: Sale batch calls. Sale services expose a batch property:
$sale = $b24Service->getSaleScope();
$payments = $sale->payment()->batch->add([
// payment payloads
]);
$shipments = $sale->shipment()->batch->list([
// filter/select params
]);
Use this for order/payment/shipment synchronization jobs where you would otherwise issue many individual REST calls. The release covers many Sale entities, including basket properties, cashboxes, deliveries, payments, shipments, properties, statuses, and trade platforms.
New in 3.7.0: Landing batch calls. Landing batch services cover sites, pages, blocks, demos, repo, repo widgets, roles, sys pages, and templates:
$landing = $b24Service->getLandingScope();
$sites = $landing->site()->batch->list([
'params' => ['limit' => 50, 'offset' => 0],
]);
Landing list batch methods support offset pagination via params.limit and params.offset.
New in 3.7.0: PortalDomainUrlChangingEvent lets you veto a portal redirect before credentials change or tokens are sent:
use Bitrix24\SDK\Core\Events\PortalDomainUrlChangingEvent;
use Bitrix24\SDK\Core\Exceptions\PortalDomainChangeRejectedException;
Event::listen(PortalDomainUrlChangingEvent::class, function (PortalDomainUrlChangingEvent $event) {
if (! in_array($event->getNewDomain(), config('services.bitrix24.allowed_domains'), true)) {
$event->deny(); // irreversible
}
});
Catch PortalDomainChangeRejectedException in the surrounding flow and fail closed.
Create:
$deal = $b24Service->getMainScope()->crm()->deals()->addDeal([
'TITLE' => 'New Deal',
'AMOUNT' => 1000,
]);
Update:
$deal->setTitle('Updated Deal')->update();
Delete:
$deal->delete();
For Sale and Landing, prefer typed service methods. For bulk updates, use the new batch services rather than looping individual CRUD calls in a job.
Handle webhook events:
$event = $b24Service->getCore()->getWebhookEvent();
switch ($event->getType()) {
case 'crm.deal.add':
$deal = $event->getData()['fields'];
break;
}
For Laravel, route webhook handlers through a controller and dispatch queued jobs:
public function __invoke(Request $request)
{
$event = $b24Service->getCore()->getWebhookEvent();
SyncBitrix24Event::dispatch($event->getType(), $event->getData());
return response()->noContent();
}
3.7.0 adds REST v3 user history, field-change, and metadata services with cursor-based incremental reads. Use these for user sync instead of full-list polling:
$cursor = Cache::get('b24.user.history.cursor');
$history = $b24Service
->getMainScope()
->user()
->history()
->list(['cursor' => $cursor]);
foreach ($history->getItems() as $change) {
// apply user change locally
}
Cache::put('b24.user.history.cursor', $history->getNextCursor());
Store the cursor per portal/account and advance it only after successful processing. This is a good fit for scheduled Laravel jobs.
Register a singleton:
// app/Providers/Bitrix24ServiceProvider.php
public function register(): void
{
$this->app->singleton('bitrix24', function () {
return ServiceBuilderFactory::createServiceBuilderFromWebhook(
config('services.bitrix24.webhook_url')
);
});
}
Use it in controllers or jobs:
public function syncDeals()
{
$deals = app('bitrix24')
->getMainScope()
->crm()
->deals()
->getDeals();
// Process deals...
}
For multi-account apps, resolve the service builder per portal/account rather than registering a single global webhook client.
Wrap API calls and batch jobs in try/catch:
try {
$result = $b24Service->getCore()->call('crm.deal.get', ['ID' => 1]);
} catch (\Bitrix24\SDK\Core\Exceptions\ApiException $e) {
if ($e->getCode() === 401) {
// Auth/token issue; SDK may auto-refresh, but surface persistent failures
}
Log::error('Bitrix24 API error', [
'code' => $e->getCode(),
'message' => $e->getMessage(),
]);
}
For portal domain changes:
try {
// OAuth/install flow that may change portal domain
} catch (PortalDomainChangeRejectedException $e) {
Log::warning('Rejected untrusted portal domain redirect', [
'exception' => $e,
]);
abort(403);
}
^3.7 for new projects unless you are pinned by PHP version or existing v1 integration.PortalDomainUrlChangingEvent::deny() is irreversible.PortalDomainChangeRejectedException and fail closed.batch property.params.limit and params.offset.lid for landing.landing.update|deleteid for landing.site.update|deletecode for *.unregister methodslog.comment.delete remains excluded, and some Disk allowed-operation endpoints are still unresolved. Use a raw API call only when you accept the contract risk.crm, sale, landing, im, disk.getDeals() calls. Use filters, SELECT, TOP, or batch/generator methods.COUNT => false where supported for list queries to reduce payload size.\Bitrix24\SDK\Services\AbstractService for undocumented endpoints:
class CustomService extends AbstractService
{
public function customMethod(array $params): array
{
return $this->getApiClient()->call('custom.endpoint', $params);
}
}
ApiClient for logging, metrics, or tracing.getPullScope() facade for legacy Pull channel/configuration work.voximplant.callback.start.ACTIVE=false serialization; preserve that behavior when building payloads manually..env.local for integration credentials and never commit it.make test-integration-scope-crm
$mockClient = new ApiClientMock();
$mockClient->setResponse('crm.deal.get', ['result' => ['ID' => 1]]);
$service = new CrmService($mockClient);
ngrok or a similar tunnel for local OAuth development.Ctrl+Click service methods to inspect typed parameters and return types./examples/ and the changelog when adopting new Sale, Landing, Pull, Voximplant, timeman, SonetGroup, or REST v3 user-history services.How can I help you explore Laravel packages today?