External API Integration Module Development for 1C-Bitrix
Integration built from "five lines of cURL in some random file" is a classic technical debt in Bitrix projects. Such code produces no logs, handles no errors, supports no retries, and falls apart the moment an API key changes. A proper integration module solves these problems systematically.
Integration Module Architecture Principles
A well-designed integration module consists of several layers:
- HTTP client — abstraction over the transport layer (cURL/Guzzle), authentication management, timeouts, retries
- API Gateway — methods corresponding to specific external API endpoints
- Mapper/DTO — data transformation between the API format and the Bitrix format
- Queue/Retry — queue for asynchronous requests and retry logic on errors
- Logger — log of all requests and responses for diagnostics
- Admin UI — connection settings, monitoring, manual sync trigger
HTTP Client with Retry Logic
namespace Vendor\Integration\Http;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\HttpHeaders;
class ApiClient
{
private HttpClient $http;
private string $baseUrl;
private string $apiKey;
private int $maxRetries = 3;
public function request(string $method, string $endpoint, array $data = []): array
{
$attempt = 0;
$lastException = null;
while ($attempt < $this->maxRetries) {
try {
$response = $this->doRequest($method, $endpoint, $data);
$this->logRequest($method, $endpoint, $data, $response);
return $response;
} catch (RateLimitException $e) {
sleep(pow(2, $attempt)); // Exponential backoff
$attempt++;
$lastException = $e;
} catch (ApiException $e) {
$this->logError($method, $endpoint, $e);
throw $e; // Do not retry business errors
}
}
throw $lastException;
}
}
Request Logging
A request log is a mandatory part of a production module. Without it, support becomes guesswork. ORM table for logs:
// lib/Orm/ApiLogTable.php
class ApiLogTable extends DataManager
{
public static function getTableName(): string { return 'vendor_integration_log'; }
public static function getMap(): array
{
return [
new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
new StringField('METHOD'), // GET/POST/PUT
new StringField('ENDPOINT'),
new TextField('REQUEST_BODY'),
new IntegerField('STATUS_CODE'),
new TextField('RESPONSE_BODY'),
new FloatField('DURATION_MS'), // execution time
new StringField('ERROR'),
new DatetimeField('CREATED_AT'),
];
}
}
The admin section provides a filterable list of requests by status, date, and endpoint. This is the first place a developer looks when diagnosing an issue.
Queue for Asynchronous Synchronization
Synchronous external API calls during a user request is an anti-pattern: the external service may be slow or unavailable. Heavy operations (catalog updates from ERP, order synchronization) are offloaded to a queue.
Implementation via Bitrix agents:
// Agent runs via cron every N minutes
public static function processSyncQueue(): string
{
$items = SyncQueueTable::getList([
'filter' => ['STATUS' => 'pending', '<ATTEMPTS' => 3],
'limit' => 50,
'order' => ['CREATED_AT' => 'ASC'],
]);
foreach ($items as $item) {
try {
static::processItem($item);
SyncQueueTable::update($item['ID'], ['STATUS' => 'done']);
} catch (\Exception $e) {
SyncQueueTable::update($item['ID'], [
'STATUS' => $item['ATTEMPTS'] >= 2 ? 'failed' : 'pending',
'ATTEMPTS' => $item['ATTEMPTS'] + 1,
'LAST_ERROR' => $e->getMessage(),
'NEXT_ATTEMPT' => (new \Bitrix\Main\Type\DateTime())->add('PT' . pow(2, $item['ATTEMPTS']) . 'M'),
]);
}
}
return '\Vendor\Integration\SyncAgent::processSyncQueue();'; // Return for re-scheduling
}
Webhook Handler
If the external system sends events (webhooks), the module registers a public URL to receive them:
// Public handler: /local/api/integration/webhook.php
use Bitrix\Main\Application;
$request = Application::getInstance()->getContext()->getRequest();
$payload = json_decode($request->getInput(), true);
// Signature verification
$signature = $request->getHeader('X-Signature');
if (!WebhookSecurity::verify($payload, $signature)) {
http_response_code(401);
exit;
}
// Enqueue for processing
SyncQueueTable::add([
'TYPE' => 'webhook_' . ($payload['event'] ?? 'unknown'),
'PAYLOAD' => json_encode($payload),
'STATUS' => 'pending',
]);
http_response_code(200);
echo json_encode(['ok' => true]);
Module Settings
Settings (API URL, keys, operating mode) are stored via \Bitrix\Main\Config\Option:
// Write a setting
Option::set('vendor.integration', 'api_key', $encryptedKey);
Option::set('vendor.integration', 'api_url', 'https://api.service.com/v2');
// Read
$apiKey = Option::get('vendor.integration', 'api_key');
Sensitive data (API keys, passwords) are encrypted before saving via \Bitrix\Main\Security\Sign\Signer or the standard \Bitrix\Main\Crypt function.
Typical Development Timelines
| Configuration | Timeline |
|---|---|
| Simple integration: 3–5 methods, no queue | 1–2 weeks |
| Bidirectional sync, queue, logging | 3–5 weeks |
| Complex integration: webhooks, mapping, UI | 6–10 weeks |
| Integration with ERP via CommerceML/REST | 4–8 weeks |
The module is documented at the architecture level (data flow diagram) and at the operational level (administrator guide describing settings and error interpretation).







