bitrix24/b24phpsdk
Bitrix24 PHP SDK for working with the Bitrix24 REST API from Laravel or plain PHP. Provides typed clients, authentication helpers, API method wrappers, pagination, and webhook/OAuth support to simplify integrating CRM, tasks, chats, and other Bitrix24 modules.
┌─────────────────────────────────────────────────────────┐
│ Services │
│ src/Services/CRM/, src/Services/Sale/, ... │
│ Business-level Bitrix24 REST API wrappers │
└──────────────────────┬──────────────────────────────────┘
│ depends on
┌────────────┴────────────┐
▼ ▼
┌─────────────────┐ ┌──────────────────────┐
│ Application │ │ Infrastructure │
│ src/Application│ │ src/Infrastructure/ │
│ OAuth flows, │ │ Console commands, │
│ local portals │ │ HTTP client adapters │
└────────┬────────┘ └──────────┬────────────┘
│ │
└──────────┬──────────────┘
│ depends on
▼
┌──────────────────┐
│ Core │
│ src/Core/ │
│ Contracts, HTTP, │
│ Credentials, │
│ Exceptions │
└──────────────────┘
▲
│ depends on
┌──────────────────┐
│ Legacy │
│ src/Legacy/ │
│ Task v1 API, │
│ deprecated wrappers│
└──────────────────┘
Key rule: arrows point toward dependencies. Outer layers can use inner layers; inner layers CANNOT use outer layers.
Coredepends on nothing inside the SDK.
Architectural boundaries are enforced by deptrac (make lint-deptrac). See deptrac.yaml for the machine-readable ruleset.
src/Core/)The foundational layer. Contains:
| Sub-directory | Purpose |
|---|---|
Contracts/ |
Interfaces: CoreInterface, ApiClientInterface, BatchOperationsInterface, BulkItemsReaderInterface |
Credentials/ |
Scope, AuthToken, Webhook, ApplicationProfile |
Exceptions/ |
Typed exceptions: BaseException, TransportException, InvalidArgumentException, etc. |
Result/ |
Base result types: AbstractResult, AbstractItem, AddedItemResult, DeletedItemResult, etc. |
Requests/ |
Request value objects |
Response/ |
Raw API response parsing |
Dependency rule: Core imports NOTHING from other SDK layers.
Catches for agents: When writing Core code, do NOT import from Services\, Application\, or Infrastructure\.
src/Application/)Handles OAuth app lifecycle and local portal management.
| Sub-directory | Purpose |
|---|---|
Contracts/ |
Application-level interfaces |
Local/ |
Local application management |
Workflows/ |
OAuth authorization flows |
Requests/ |
Application request value objects |
Dependency rule: May import from Core only.
src/Infrastructure/)Adapters for external systems (console, HTTP, filesystem).
| Sub-directory | Purpose |
|---|---|
Console/ |
Symfony Console commands (bin/console) |
HttpClient/ |
Symfony HTTP Client adapter wrapping CoreInterface |
Filesystem/ |
File utilities |
Dependency rule: May import from Core only.
src/Services/)One directory per Bitrix24 API scope. Each scope follows the same layout:
src/Services/<Scope>/
├── <Scope>ServiceBuilder.php ← factory, registered in ServiceBuilder
├── Service/
│ ├── <Entity>.php ← main service (extends AbstractService)
│ └── Batch.php ← batch wrapper (when applicable)
└── Result/
├── <Entity>ItemResult.php ← single item ([@property-read](https://github.com/property-read) annotations)
├── <Entity>Result.php ← single-entity response
└── <Entity>sResult.php ← list response
Dependency rule: May import from Core and Application. Services MUST NOT import from each other or from Infrastructure.
Real example: src/Services/CRM/Deal/
CRM/Deal/
├── Service/
│ ├── Deal.php ← crm.deal.* methods
│ ├── Batch.php ← batch-mode deal operations
│ ├── DealCategory.php ← crm.dealcategory.* methods
│ └── ...
└── Result/
├── DealItemResult.php
├── DealResult.php
└── DealsResult.php
src/Legacy/)Wraps Bitrix24 Task API v1 (deprecated endpoint format). New development goes into src/Services/, not here.
Dependency rule: May import from Core, Application, and Services.
Every service scope has a <Scope>ServiceBuilder.php that creates service instances. These are wired together in src/Services/ServiceBuilder.php which is the single entry point for SDK consumers:
// Consumer code
$serviceBuilder = (new ServiceBuilderFactory())->initFromWebhook($webhookUrl);
$deal = $serviceBuilder->getCRMScope()->deal();
$result = $deal->get(42);
To add a new scope:
src/Services/<Name>/<Name>ServiceBuilder.phpget<Name>Scope(): <Name>ServiceBuilder method to src/Services/ServiceBuilder.phpgetCRMScope() or getSaleScope()All API responses are wrapped in result objects. The pattern uses PHP magic __get() to expose raw API fields:
// AbstractItem base class provides magic __get()
// Result item only needs [@property-read](https://github.com/property-read) annotations for IDE support + static analysis
/**
* [@property-read](https://github.com/property-read) int $ID
* [@property-read](https://github.com/property-read) string $TITLE
* [@property-read](https://github.com/property-read) string $STATUS_ID
*/
class DealItemResult extends AbstractItem {}
// Usage
$deal = $dealService->get(42)->deal();
echo $deal->ID; // int, type-safe via PHPDoc
echo $deal->TITLE; // string
The assertBitrix24AllResultItemFieldsAnnotated() custom assertion (in integration tests) verifies that every field returned by the API is annotated in the result item's PHPDoc.
Two custom PHP 8 attributes drive the SDK coverage documentation system:
| Attribute | Location | Purpose |
|---|---|---|
#[ApiServiceMetadata(new Scope([...]))] |
Service class | Declares which Bitrix24 scope this service needs |
#[ApiEndpointMetadata('method.name', 'docs-url', 'description')] |
Service method | Documents each REST method call |
These feed make sdk-coverage-v1-show and make build-documentation.
src/Services/<Name>/ directory structure (see above)<Name>Service.php extending AbstractService#[ApiServiceMetadata] and #[ApiEndpointMetadata] attributes[@property-read](https://github.com/property-read) annotations<Name>ServiceBuilder.phpServiceBuilder.phptests/Unit/Services/<Name>/Service/<Name>ServiceTest.phptests/Integration/Services/<Name>/Service/<Name>ServiceTest.phpphpunit.xml.distmake test-integration-<name> target to Makefilemake lint-all and make test-unit — both must passArchitectural rules are enforced at three levels:
| Level | Tool | When |
|---|---|---|
| Local pre-commit | captainhook + phpstan | Every git commit |
| Local manual | make lint-deptrac |
On demand |
| CI | .github/workflows/deptrac.yml |
Every push / PR |
If deptrac reports a violation, do NOT add an exception — fix the import instead. The only allowed workaround is adding a # deptrac-ignore-line comment with a detailed explanation.
How can I help you explore Laravel packages today?