Development of Server Applications for Bitrix24
When the task is to automatically create deals in Bitrix24 when an order arrives from an external store, synchronize contacts with an email service, or run robots on a schedule — a server application is needed. It works without user involvement: no interface, no iframe, only the REST API and a properly configured OAuth.
How a server application differs from a webhook
Webhooks in Bitrix24 are incoming URLs where Bitrix24 sends events. They are convenient for simple scenarios, but have limitations: they are tied to a specific user, do not support OAuth, and cannot be commercial. A server application (type server) is a full OAuth client that:
- Authorizes on behalf of the installing user with token storage
- Can run in the background (daemon, task queue, cron)
- Supports multi-portal operation (one application — many clients)
- Has its own event handler via
event.bind
OAuth 2.0 in the Bitrix24 context
Bitrix24 uses the standard Authorization Code Flow:
1. Redirect the user to:
https://company.bitrix24.ru/oauth/authorize/
?client_id=YOUR_CLIENT_ID
&response_type=code
&state=RANDOM_STRING
2. After consent — redirect to redirect_uri with `code`
3. Exchange the code for tokens:
POST https://oauth.bitrix.info/oauth/token/
grant_type=authorization_code
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_SECRET
&code=RECEIVED_CODE
&redirect_uri=https://your-app.com/oauth/callback
4. Response:
{
"access_token": "...",
"expires_in": 3600,
"token_type": "bearer",
"scope": "crm,task,user",
"refresh_token": "...",
"member_id": "portal_hash"
}
Critical: store tokens in a secure storage (database, AWS Secrets Manager, Vault). member_id is the unique portal identifier — use it as a key in a multi-tenant architecture.
Token storage and refresh logic
In a production system, tokens are stored approximately like this:
CREATE TABLE bitrix_tokens (
id SERIAL PRIMARY KEY,
member_id VARCHAR(64) UNIQUE NOT NULL,
domain VARCHAR(255) NOT NULL,
access_token TEXT NOT NULL,
refresh_token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
scope TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
Refresh on token expiry:
def get_valid_token(member_id: str) -> str:
token = db.get_token(member_id)
if token.expires_at < datetime.now() + timedelta(minutes=5):
response = requests.post(
'https://oauth.bitrix.info/oauth/token/',
data={
'grant_type': 'refresh_token',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'refresh_token': token.refresh_token,
}
)
new_token = response.json()
db.update_token(member_id, new_token)
return new_token['access_token']
return token.access_token
Add a 5-minute buffer (timedelta(minutes=5)) — otherwise the token may expire between the check and the actual request.
Subscribing to portal events
A server application can subscribe to Bitrix24 events via REST:
POST /rest/event.bind
{
"event": "ONCRMDEALADD",
"handler": "https://your-app.com/webhooks/bitrix/deal-add",
"auth_type": 0
}
When an event fires, Bitrix24 sends a POST to the handler with event data and a token. Supported CRM events: ONCRMDEALADD, ONCRMDEALUPDATE, ONCRMDEAIDELETED, ONCRM* — similarly for leads, contacts, and companies.
Important nuance: the handler must respond with 200 OK within 3 seconds. If your processing takes longer — immediately return 200 and put the task in a queue (RabbitMQ, Redis Streams, SQS):
@app.route('/webhooks/bitrix/deal-add', methods=['POST'])
def handle_deal_add():
data = request.json
task_queue.enqueue(process_new_deal, data) # Async processing
return jsonify({'status': 'accepted'}), 200
Working with batch requests
The Bitrix24 REST API has a limit of 2 requests per second per portal (50 requests per second for paid plans). For bulk operations use batching:
POST /rest/batch
{
"halt": 0,
"cmd": {
"get_deal": "crm.deal.get?id=42",
"get_contact": "crm.contact.get?id=17",
"get_company": "crm.company.get?id=5"
}
}
One batch request can contain up to 50 commands. With halt=1, execution stops on the first error.
Multi-portal architecture
If the application serves multiple portals, the structure must support data isolation:
/api/v1/{member_id}/sync — portal data synchronization
/webhooks/{member_id}/event — receiving events from a specific portal
Using member_id in the URL is a convenient pattern, but make sure there are no IDOR vulnerabilities: validate the request signature or authorization before processing.
Development timelines
| Server application type | Timeline |
|---|---|
| Simple integration (one-way synchronization) | 3–7 days |
| Two-way synchronization with one external system | 2–4 weeks |
| Multi-portal application with Market | 1–3 months |
| Enterprise integration (multiple systems, queues, monitoring) | 2–6 months |
The main complexity of server applications is not the REST API itself, but reliability: proper handling of duplicate requests (idempotency), retry logic when a portal is temporarily unavailable, monitoring of expired tokens. These aspects take up 40–60% of development time.







