Telegram Mini App Development with Bitrix24 Integration

Telegram Mini App Development with Bitrix24 Integration A manager in the field opens Telegram, sees a lead, and moves it to the next status in 30 seconds. Instead of 5 minutes searching in a browser. This is a typical scenario we cover with a Telegram Mini App + Bitrix24 REST API combination. The

Our competencies:

Frequently Asked Questions

Telegram Mini App Development with Bitrix24 Integration

A manager in the field opens Telegram, sees a lead, and moves it to the next status in 30 seconds. Instead of 5 minutes searching in a browser. This is a typical scenario we cover with a Telegram Mini App + Bitrix24 REST API combination. The Mini App works as a CRM dashboard right in the chat: deal list, stages, contact info, ability to comment. We develop turnkey with full OAuth routing, token storage, and webhook configuration.

Problems the Integration Solves

Authorization without a browser. The Bitrix24 REST API requires an OAuth access_token, but a Mini App is a web page in Telegram WebView where redirect is impossible. The solution is a mediator server that validates the Telegram initData and issues an OAuth link or a ready-made token. We've applied this approach in 15+ projects.

Delays in lead processing. Managers spend up to 5 minutes opening a browser, logging into the CRM, and searching for a card. The Mini App reduces this to 30 seconds: the employee sees the lead immediately and changes the status with one tap. According to our measurements, the speed of reaction to an incoming lead increases by 60–70% — the Telegram Mini App processes leads 3 times faster than through the web interface.

Real-time notifications. Without integration, a manager learns about a new deal via email digests with minutes of delay. Bitrix24 event webhooks send a POST request to the server, which then sends a message to Telegram. The notification arrives in 1–2 seconds.

Use Cases

Scenario Description Target Audience
Mobile CRM Dashboard View leads and deals, change status, add comments Field managers, sales departments
Corporate Ordering Place requests by dealers/agents via Mini App Network companies, distributors
Self-Service Portal Client sees request status and communication history B2C services, tech support
Task Manager Task list, status change, file attachment Teams using Bitrix24

Architecture: OAuth and Server Proxy

Authorization process:

  1. User opens the Mini App. The frontend sends initData to the server.
  2. Server validates initData hash, extracts user_id, and looks up a record in the tg_bx24_tokens table.
  3. If a token is found and not expired — returns a JWT for the Mini App. If not — returns an OAuth authorization link for Bitrix24.
  4. After confirmation, the server exchanges the code for an access_token and saves it linked to the Telegram user_id.
Step Action Participant
1 Open Mini App, send initData User -> Mini App
2 Validate initData, search for token Server
3 Return JWT or OAuth link Server -> Mini App
4 Confirm OAuth (if needed) User -> Bitrix24
5 Exchange code for token, save Server

The OAuth callback handling code is standard for all our integrations:

class Bx24OAuthController { public function callback(Request $request): Response { $code = $request->get('code'); $tgUserId = $request->session()->get('pending_tg_user_id'); $tokenData = $this->exchangeCode($code); \Local\TgBx24\TokenStorage::save($tgUserId, [ 'access_token' => $tokenData['access_token'], 'refresh_token' => $tokenData['refresh_token'], 'expires_at' => time() + $tokenData['expires_in'], 'domain' => $tokenData['domain'], 'user_id' => $tokenData['user_id'], ]); $this->bot->sendMessage($tgUserId, 'Bitrix24 authorization successful.'); return redirect('/auth/success'); } private function exchangeCode(string $code): array { $response = Http::post('https://oauth.bitrix.info/oauth/token/', [ 'grant_type' => 'authorization_code', 'client_id' => config('bx24.client_id'), 'client_secret' => config('bx24.client_secret'), 'code' => $code, ]); return $response->json(); } } 

Tokens are stored in a separate table or Redis. We use automatic refresh 5 minutes before expiration to ensure users don't lose access. All secrets are encrypted.

class TokenStorage { private const TABLE = 'tg_bx24_tokens'; public static function save(int $tgUserId, array $tokenData): void { $encrypted = \Local\Crypto::encrypt(json_encode($tokenData)); \Bitrix\Main\Application::getConnection()->queryExecute( "INSERT INTO " . self::TABLE . " (tg_user_id, token_data, updated_at) VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE token_data = ?, updated_at = NOW()", [$tgUserId, $encrypted, $encrypted] ); } public static function getValidToken(int $tgUserId): ?array { $result = \Bitrix\Main\Application::getConnection()->query( "SELECT token_data FROM " . self::TABLE . " WHERE tg_user_id = ?", [$tgUserId] ); $row = $result->fetch(); if (!$row) return null; $data = json_decode(\Local\Crypto::decrypt($row['token_data']), true); if ($data['expires_at'] < time() + 300) { $data = self::refreshToken($data); } return $data; } private static function refreshToken(array $data): array { $response = \Bitrix\Main\Web\HttpClient::post( 'https://oauth.bitrix.info/oauth/token/', [ 'grant_type' => 'refresh_token', 'client_id' => \Bitrix\Main\Config\Option::get('local.tg_bx24', 'client_id'), 'client_secret' => \Bitrix\Main\Config\Option::get('local.tg_bx24', 'client_secret'), 'refresh_token' => $data['refresh_token'], ] ); return $newData; } } 

Why a Mediator Server?

A direct request from the Mini App to the Bitrix24 API is impossible due to CORS and the lack of secure token storage on the client. The mediator server acts as a cryptographic switch: all requests to the REST API go through it, tokens never leave the server. This is the security standard for OAuth in mobile applications.

Real-Time Data Exchange with Webhooks

Bitrix24 allows setting up event webhooks — for example, ONCRMDEALUPDATE. When an event triggers, Bitrix24 sends a POST to our server. The server identifies the responsible person (ASSIGNED_BY_ID), finds their Telegram user_id via the token table, and sends a message via sendMessage. Delay is no more than 2 seconds.

How automatic token refresh works We store tokens in the `tg_bx24_tokens` table with an `expires_at` field. 5 minutes before expiration, the server automatically requests a new token via refresh_token. If the refresh_token is also expired, the user is prompted to go through OAuth again. All secrets are encrypted.

React Mini App: Employee CRM Dashboard

The frontend is built with React using the Telegram WebApp SDK. This allows embedding the interface into a bot button and using native controls (e.g., haptic feedback).

import { useEffect, useState } from 'react'; const tg = window.Telegram.WebApp; interface Deal { ID: string; TITLE: string; OPPORTUNITY: string; STAGE_ID: string; CONTACT_NAME: string; } function CrmDashboard() { const [deals, setDeals] = useState<Deal[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { tg.ready(); tg.expand(); loadDeals(); }, []); async function loadDeals() { const res = await fetch('/tg-api/crm/deals', { headers: { 'X-Tg-Init-Data': tg.initData }, }); const data = await res.json(); setDeals(data.deals); setLoading(false); } async function updateStage(dealId: string, stageId: string) { await fetch(`/tg-api/crm/deals/${dealId}/stage`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-Tg-Init-Data': tg.initData, }, body: JSON.stringify({ stage_id: stageId }), }); tg.HapticFeedback.notificationOccurred('success'); loadDeals(); } // ... render } 

On the server, a proxy class encapsulates calls to the REST API. It automatically fetches the token from storage and handles authorization errors:

class CrmDealsProxy { public function getDeals(int $tgUserId): array { $tokenData = TokenStorage::getValidToken($tgUserId); if (!$tokenData) { throw new \RuntimeException('Not authorized', 401); } $bx24 = new \Local\TgBx24\Bx24Client($tokenData['access_token'], $tokenData['domain']); $result = $bx24->call('crm.deal.list', [ 'filter' => ['ASSIGNED_BY_ID' => $tokenData['user_id'], 'CLOSED' => 'N'], 'select' => ['ID', 'TITLE', 'OPPORTUNITY', 'STAGE_ID', 'CONTACT_ID'], 'order' => ['DATE_MODIFY' => 'DESC'], 'start' => 0, ]); return $result['result'] ?? []; } public function updateDealStage(int $tgUserId, int $dealId, string $stageId): bool { $tokenData = TokenStorage::getValidToken($tgUserId); $bx24 = new \Local\TgBx24\Bx24Client($tokenData['access_token'], $tokenData['domain']); $result = $bx24->call('crm.deal.update', [ 'id' => $dealId, 'fields' => ['STAGE_ID' => $stageId], ]); return !empty($result['result']); } } 

What's Included in the Work

  • Bitrix24 application registration (OAuth), Mini App setup in @BotFather
  • Mediator server: initData validation, OAuth token storage and renewal (PHP 8.1, MariaDB)
  • React Mini App for the chosen scenario (deal dashboard, requests, tasks)
  • REST proxy to Bitrix24 API with automatic token renewal
  • Bitrix24 event webhooks for Telegram notifications
  • User onboarding: account linking and test launch
  • Installation and support documentation

Process and Timelines

We follow a structured approach: data collection → audit/analysis → design → estimation → development → testing → launch.

  • MVP for one scenario: from 3 to 5 weeks
  • Full-featured application with multiple sections: from 8 to 14 weeks

Cost is determined individually after analyzing the technical specifications. We provide a 30-day guarantee on the integration's functionality. Experience: 10+ years, over 50 integrations with external services. Certified Bitrix developers.

Contact us for a consultation — we'll conduct a free audit of your CRM and propose an architecture that will reduce your sales team's reaction time.