Bitrix24 PHP SDK is an official library for working with Bitrix24 REST API. The project represents a high-level SDK with typed methods, auto-renewal tokens support and efficient work with large data volumes through batch operations.
src/
├── Application/ # Application contracts
├── Attributes/ # PHP attributes for metadata
├── Core/ # Core SDK components
├── Deprecations/ # Deprecated methods handling
├── Events/ # Event system
├── Infrastructure/ # Infrastructure components
└── Services/ # Services for API work
- HTTP/JSON protocol - basic communication level
- Symfony HTTP Client - HTTP client for requests
- Core\ApiClient - work with REST API endpoints (input: arrays/strings, output: Response)
- Services - work with Bitrix24 entities (input: arrays/strings, output: typed DTO)
Main components for working with different API scopes:
- AI/ - AI services work
- CRM/ - CRM management (contacts, deals, companies, leads, products, etc.)
- Catalog/ - product catalog management
- Department/ - departments work
- Entity/ - universal data storage
- IM/ - instant messages
- IMOpenLines/ - open lines
- Main/ - main system methods
- Placement/ - application placements
- Telephony/ - telephony
- User/ - user management
- UserConsent/ - user consents
- Workflows/ - business processes
- AbstractService - base class for all services
- AbstractBatchService - base class for batch operations
- AbstractServiceBuilder - base class for service builders
PHP 8+ attributes are used for metadata:
- ApiServiceMetadata - service metadata (scope, version)
- ApiEndpointMetadata - endpoint metadata (method name, documentation, description)
- ApiBatchMethodMetadata - metadata for batch methods
- ApiBatchServiceMetadata - metadata for batch services
All methods return typed results:
- AbstractResult - base class for all results
- FieldsResult - fields retrieval result
- AddedItemResult - item addition result
- UpdatedItemResult - item update result
- DeletedItemResult - item deletion result
- CoreInterface - interface for API work
- Scope - permissions (scopes) management
- BatchOperationsInterface - interface for batch operations
Service structure for CRM items work:
src/Services/CRM/Item/
├── Productrow/ # Subservice for product rows
├── Result/ # Result classes
│ ├── ItemResult.php # Single item result
│ ├── ItemsResult.php # Items list result
│ └── ItemItemResult.php # Typed item DTO
└── Service/ # Main services
├── Item.php # Main service
├── Batch.php # Batch operations
└── ItemDetailsConfiguration.php # Details configuration
#[ApiServiceMetadata(new Scope(['crm']))]
class Item extends AbstractService
{
public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger)
{
parent::__construct($core, $logger);
}
#[ApiEndpointMetadata(
'crm.item.add',
'https://apidocs.bitrix24.com/api-reference/crm/universal/crm-item-add.html',
'Method creates new SPA item with entityTypeId.'
)]
public function add(int $entityTypeId, array $fields): ItemResult
{
return new ItemResult(
$this->core->call('crm.item.add', [
'entityTypeId' => $entityTypeId,
'fields' => $fields,
])
);
}
// Other CRUD methods: get, list, update, delete, fields
// Additional methods: countByFilter
}#[ApiBatchServiceMetadata(new Scope(['crm']))]
class Batch
{
public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) {}
#[ApiBatchMethodMetadata(
'crm.item.list',
'https://apidocs.bitrix24.com/api-reference/crm/universal/crm-item-list.html',
'Method returns array with SPA items with entityTypeId.'
)]
public function list(int $entityTypeId, array $order, array $filter, array $select, ?int $limit = null): Generator
{
foreach ($this->batch->getTraversableList('crm.item.list', $order, $filter, $select, $limit, ['entityTypeId' => $entityTypeId]) as $key => $value) {
yield $key => new ItemItemResult($value);
}
}
}// Single item result
class ItemResult extends AbstractResult
{
public function item(): ItemItemResult
{
return new ItemItemResult($this->getCoreResponse()->getResponseData()->getResult()['item']);
}
}
// Typed item DTO
class ItemItemResult extends AbstractCrmItem
{
// Automatic properties via magic methods and PHP Doc
}Each service usually implements standard set of methods:
- add(array $fields) - add item
- get(int $id) - get item by ID
- list(array $order, array $filter, array $select, int $start) - get list
- update(int $id, array $fields) - update item
- delete(int $id) - delete item
- fields() - get fields description
Each new service should be registered in corresponding ServiceBuilder:
// In CRMServiceBuilder.php
public function item(): Item\Service\Item
{
if (!isset($this->serviceCache[__METHOD__])) {
$this->serviceCache[__METHOD__] = new Item\Service\Item(
new Item\Service\Batch($this->batch, $this->log),
$this->core,
$this->log
);
}
return $this->serviceCache[__METHOD__];
}- crm - CRM (almost complete, many subscopes)
- catalog - Product catalog (partial)
- user - Users
- telephony - Telephony
- bizproc - Business processes
- entity - Universal storage
- ai_admin - AI services
According to src/Core/Credentials/Scope.php, following scopes are supported:
protected static array $availableScope = [
'ai_admin',
'appform',
'baas',
'biconnector',
'bizproc',
'calendar',
'calendarmobile',
'call',
'cashbox',
'catalog',
'catalogmobile',
'configuration.import',
'contact_center',
'crm',
'delivery',
'department',
'disk',
'documentgenerator',
'entity',
'faceid',
'forum',
'humanresources.hcmlink',
'iblock',
'im',
'imopenlines',
'intranet',
'landing',
'lists',
'log',
'mailservice',
'messageservice',
'mobile',
'notifications',
'pay_system',
'placement',
'pull',
'rating',
'rpa',
'sale',
'salescenter',
'socialnetwork',
'sonet_group',
'style',
'task',
'tasks',
'telephony',
'timeman',
'user',
'user.basic',
'userconsent'
];- task/tasks - Tasks and projects
- calendar - Calendar
- disk - Disk (files)
- im/imopenlines - Messages and open lines (partially implemented)
- sale - Sales/orders
- landing - Landings
- lists - Lists
- socialnetwork - Social network
- rpa - Robotic Process Automation
- documentgenerator - Document generator
- Tasks (task/tasks) - one of the most used scopes
- Calendar (calendar) - important for most applications
- Disk (disk) - file work is critical for many integrations
- Sales (sale) - CRM complement for orders work
- Lists (lists) - universal tool for data
- Study official API documentation for scope
- Check scope availability in
src/Core/Credentials/Scope.php - Create scope-level service builder extending
AbstractServiceBuilder - Register scope builder in root
ServiceBuilder.php - Create folder structure following CRM/Item pattern
- Implement main service with CRUD methods
- Add Batch service for mass operations
- Create typed result classes with proper PHPDoc properties
- Register service in corresponding ServiceBuilder
- Add metadata attributes for all methods
- Write unit and integration tests
- Run code quality checks (PHPStan, CS Fixer, Rector)
- Update documentation and run
make build-documentation - Update changelog
# Fork repository and clone
git clone https://github.com/YOUR-USERNAME/b24phpsdk.git
cd b24phpsdk
# Switch to dev branch (latest development)
git checkout dev
# Initialize development environment
make docker-initCreate webhook and application bridge for integration tests:
Webhook setup:
cp /tests/.env /tests/.env.local
# Add BITRIX24_WEBHOOK with maximum scopeApplication bridge setup:
cp /tests/ApplicationBridge/.env /tests/ApplicationBridge/.env.local
# Add application credentials for token-based auth// 1. Check/add scope in src/Core/Credentials/Scope.php
// 2. Create scope service builder
#[ApiServiceBuilderMetadata(new Scope(['new_scope']))]
class NewScopeServiceBuilder extends AbstractServiceBuilder
{
public function someService(): SomeService\Service\SomeService
{
if (!isset($this->serviceCache[__METHOD__])) {
$this->serviceCache[__METHOD__] = new SomeService\Service\SomeService(
$this->core,
$this->log
);
}
return $this->serviceCache[__METHOD__];
}
}
// 3. Register in root ServiceBuilder.php
public function getNewScope(): NewScopeServiceBuilder
{
if (!isset($this->serviceCache[__METHOD__])) {
$this->serviceCache[__METHOD__] = new NewScopeServiceBuilder(
$this->core,
$this->batch,
$this->bulkItemsReader,
$this->log
);
}
return $this->serviceCache[__METHOD__];
}Create proper result classes with typed properties:
/**
* @property-read int $id
* @property-read non-empty-string $name
* @property-read SomeEnum $status
* @property-read CarbonImmutable $dateCreate
*/
class SomeItemResult extends AbstractItem
{
public function __get($offset)
{
switch ($offset) {
case 'id':
return $this->data[$offset] ? (int)$this->data[$offset] : null;
case 'status':
return SomeEnum::from($this->data[$offset]);
case 'dateCreate':
return CarbonImmutable::createFromTimestamp($this->data[$offset]);
default:
return $this->data[$offset] ?? null;
}
}
}Auto-generate field descriptions:
make dev-show-fields-descriptionCreate comprehensive integration tests:
#[CoversClass(SomeService::class)]
class SomeServiceTest extends TestCase
{
protected ServiceBuilder $serviceBuilder;
protected function setUp(): void
{
$this->serviceBuilder = Fabric::getServiceBuilder();
}
// Test all CRUD methods with proper setup/teardown
}Add test suite to phpunit.xml.dist:
<testsuite name="integration_tests_scope_new">
<directory>./tests/Integration/Services/NewScope/</directory>
</testsuite>Add Makefile target:
test-integration-scope-new:
docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_newRun all quality checks before committing:
# License check
make lint-allowed-licenses
# Code style
make lint-cs-fixer
# Static analysis
make lint-phpstan
# Code modernization
make lint-rector
# Unit tests
make test-unit
# Integration tests
make test-integration-scope-new- PHP: 8.3 or 8.4
- Composer: Latest version
- Git: Version control
- Make: Build automation
- Docker: Containerization for development
- IDE: PhpStorm recommended or other IDE
- main - Latest stable release (e.g., v1.3.0)
- dev - Development branch with upcoming features (e.g., v1.4.0-dev)
- feature/ - Feature branches for new functionality
- bugfix/ - Bug fix branches
Important: Always work from dev branch, not main!
- PHPStan - static analysis
- PHP CS Fixer - code formatting
- PHPUnit - testing
- Rector - code refactoring
- PSR-12 coding standards compliance
- Type declarations for all parameters and return types
- Clear, descriptive docblocks for all classes and methods
- Small, focused methods with single responsibility
- Meaningful naming for variables, methods, and classes
All API endpoint metadata must include:
- Method name (e.g.,
crm.item.add) - Updated documentation URL (prefer
apidocs.bitrix24.comovertraining.bitrix24.com) - Clear description of method purpose
Example:
#[ApiEndpointMetadata(
'crm.item.add',
'https://apidocs.bitrix24.com/api-reference/crm/universal/crm-item-add.html',
'Method creates new SPA item with entityTypeId.'
)]Naming patterns:
SomeResult- single item result containerSomeItemsResult- multiple items result containerSomeItemResult- individual item DTO (lazy loading)
Property documentation:
/**
* @property-read int $id
* @property-read non-empty-string $name
* @property-read CustomEnum $status
* @property-read CarbonImmutable $dateCreate
*/- Target
devbranch for new features - Note any backward compatibility breaks in PR description
- BC breaks → next major version
- Non-BC features → next minor version
- Target oldest applicable version for bug fixes
- Clearly explain what bug you're fixing and how
This document serves as a guide for understanding SDK architecture and creating new wrapper services for Bitrix24 REST API.
-
Event class structure
- Each event is represented by two main classes:
- Event class (for example,
OnCrmCompanyAdd) — extendsAbstractEventRequest, is responsible for handling the event request and providing the payload - Payload class (for example,
OnCrmCompanyAddPayload) — extendsAbstractItem, represents the event data in a structured format
- Event class (for example,
- Each event is represented by two main classes:
-
Inheritance hierarchy
- Event classes:
[SpecificEventClass]→AbstractEventRequest→AbstractRequest→ implementsEventInterface - Payload classes:
[SpecificPayloadClass]→AbstractItem→ implementsIteratorAggregate
- Event classes:
-
Factory pattern implementation
- Each module provides its own events factory (for example,
CrmCompanyEventsFactory,TelephonyEventsFactory) - All factories implement the
EventsFabricInterfacewith two key methods:isSupport(string $eventCode): bool— checks whether the factory can handle a given event codecreate(Request $eventRequest): EventInterface— creates the corresponding event object from the request
- Each module provides its own events factory (for example,
-
Central coordinator of factories
RemoteEventsFactorymanages all event factories and routes incoming event requests to the appropriate factory- It uses the event code from the payload to determine which factory should handle the event
-
Event registration and management
- The
EventManagerclass allows registering event handlers via the Bitrix24 API - The service class
Eventprovides direct API operations related to events:bind— register a new event handlerunbind— unregister a handlerget— retrieve the list of registered handlerstest— test event handling
- The
- The application receives an HTTP request containing event data
- The request is passed to
RemoteEventsFactory->createEvent() - Based on the event code, the appropriate factory is selected to create the concrete event object
- The event object processes the request and exposes structured access to event data via its payload class
- Application code can then handle the event accordingly
The SDK currently implements events for several modules:
-
CRM module:
- Company events (add, update, delete, custom fields operations)
- Deal events (including recurring deal operations)
- Quote events (similar patterns to company events)
-
Tasks module:
- Task events (add, update, delete)
-
Application lifecycle:
- Application install/uninstall events
-
Telephony:
- Call-related events
- Factory pattern — to create event objects
- Strategy pattern — different factories handle different event types
- Observer pattern — for the event notification system
- Immutable objects — payload objects are designed as immutable to ensure data integrity
-
Registering event handlers:
$eventManager->bindEventHandlers([ new EventHandlerMetadata( OnCrmCompanyAdd::CODE, 'https://your-handler-url.com', $userId ), ]);
-
Handling incoming events:
$event = $remoteEventsFactory->createEvent($request, $applicationToken); if ($event instanceof OnCrmCompanyAdd) { $payload = $event->getPayload(); // Handle company added event }
The Bitrix24 PHP SDK implements a robust and extensible event system following clean OOP principles and proven design patterns. Key characteristics include:
- Modular design — events are organized by module (CRM, Tasks, etc.), which makes it easy to extend
- Type safety — strong typing with concrete event and payload classes improves IDE support and error detection
- Factory pattern — factories provide a clean way to create event objects from HTTP requests
- Security features — built-in support for validating application tokens
- Immutability — event payload objects are immutable, preventing accidental modification of event data
- Standardized interface — all events follow a common interface, simplifying the implementation of new event types
To add new event classes for the Bitrix24 event system you need to:
- Create an event class that extends
AbstractEventRequest - Create a payload class that extends
AbstractItem - Create or update an events factory implementing
EventsFabricInterface - Register the factory in
RemoteEventsFactory::init()
This architecture allows seamless integration with Bitrix24 webhook-based event notifications while keeping the code clean and testable.