Documentation

WhatsApp ↔ Bitrix24 Contact Center Bridge — install, connect, and operate.

What it is

A self-hosted Laravel bridge that lands inbound WhatsApp messages in the Bitrix24 Contact Center (one Open Channel, single operator, auto-creating a Lead per new customer) and relays the operator's replies back to the customer on WhatsApp — using the official Meta WhatsApp Cloud API.

Stack: Laravel · PHP 8.3+ · SQLite/MySQL · Laravel Queues · Pest. Scope: text and media (image · document · audio · video · sticker) both directions, one connector → one Open Channel, single operator.

Architecture

Customer WhatsApp
   ⇅
Meta WhatsApp Cloud API        (Graph API out / webhook in)
   ⇅
Laravel Bridge (this app, public HTTPS)
   ⇅
Bitrix24 Contact Center        (imconnector.send.messages in / OnImConnectorMessageAdd out)

Features

FeatureDetail
Two-way text messagingInbound to the Open Channel and outbound operator replies, both queue-backed.
Two-way mediaImage, document, audio, video & sticker both directions. Files are re-hosted on the public disk so each side can fetch them (Meta media needs a token; Bitrix links need auth). Operator-reply BBCode is converted to WhatsApp formatting.
Auto Lead creationNew customer numbers create a Bitrix Lead automatically.
Idempotent deliveryInbound deduped on wamid; outbound deduped on the Bitrix message id.
Self-healing retriesJobs retry with [10,30,60]s backoff, then dead-letter to failed_jobs.
OAuth token managementBitrix grant refreshed proactively (scheduler) and on demand inside the REST client.
Encrypted credential storeSecrets encrypted in the DB, edited via a token-gated admin form.
Per-request audit logEvery webhook recorded with a correlation request_id + raw payload.
Health probe & DR replay/health JSON for monitors; stored payloads are replayable after a fix.
24-hour window guardReplies outside Meta's 24h customer-service window are blocked and noted in-chat.

Requirements

APP_KEY is critical. Stored credentials are encrypted with it. Set it once and never rotate it, or every stored secret (and OAuth token) becomes unreadable. Back it up with the database.

Step-by-step setup

  1. Deploy & configure. Install dependencies, set APP_URL (your public HTTPS URL) and a strong ADMIN_UI_TOKEN in .env, then migrate.
    composer install
    php artisan key:generate
    php artisan migrate
  2. Enter credentials at /admin/credentials (open once with ?token=<ADMIN_UI_TOKEN>). Fill all Meta values and the Bitrix portal / client id / client secret.
  3. Create the Bitrix Local Application (Developer resources → Local application): type Server, handler path {APP_URL}/bitrix/events, install path {APP_URL}/bitrix/install, scopes crm, imconnector, imopenlines, im. Copy its client id / secret into the form.
  4. Install the app from inside Bitrix. Bitrix POSTs the OAuth grant to /bitrix/install automatically (you don't visit that URL yourself). You should see the "WhatsApp bridge installed" page.
  5. Register the connector. Makes WhatsApp (Zuse) appear in Contact Center and binds the reply event.
    php artisan bitrix:register-connector
  6. Bind to an Open Channel. Contact Center → WhatsApp (Zuse) → Connect → bind to a new Open Channel. Save its numeric LINE id into the credentials form (Bitrix → Open Channel (LINE) ID).
  7. Activate the connector on that line. important Registration alone does not connect the connector to a line — without this, sends fail with NOT_ACTIVE_LINE.
    php artisan bitrix:activate-connector
    # expects STATUS:true, CONFIGURED:true
  8. Subscribe the Meta webhook. Meta app → WhatsApp → Configuration → Webhook: URL {APP_URL}/webhook/whatsapp, your verify token, subscribe the messages field.
  9. Verify the WhatsApp token, then run the workers.
    php artisan whatsapp:check-token   # expects "OK — token is valid."
    php artisan queue:work             # relays
    php artisan schedule:work          # token refresh + pruning
Done when: a WhatsApp to your business number appears in the Open Channel (Lead created), and a reply from Bitrix arrives back on WhatsApp.

Credentials reference

GroupFields
Meta WhatsAppApp ID · App Secret · Access Token (permanent System User token) · Phone Number ID · WABA ID · Webhook Verify Token
Bitrix24Portal URL · Client ID · Client Secret · Connector ID · Open Channel (LINE) ID · Application Token
Application Token verifies inbound /bitrix/events calls. Paste the token from your Bitrix Outbound webhook (or local-app) here so events authenticate. If left blank, the bridge bootstraps it from the first event whose member_id matches the installed grant.

Resolution order for any credential: settings table (form)config/services.php (.env) → default. Secret fields show •••••••• once set — leave blank to keep the current value.

CLI commands

CommandWhat it does
bitrix:register-connectorRegisters WhatsApp (Zuse) in Contact Center and binds OnImConnectorMessageAdd/bitrix/events. --fresh unregisters first.
bitrix:activate-connectorConnects the registered connector to an Open Channel line (--line=ID, defaults to bitrix.line_id) and verifies STATUS/CONFIGURED. Required before sending.
whatsapp:check-tokenProbes the Meta phone-number node to confirm the access token works (catches the 190 auth error early).
bridge:replay-eventsReprocesses stored inbound payloads (idempotent) — DR after a fix or data loss.
bridge:prune-eventsDeletes old processed/skipped audit rows (PII retention). Runs daily 03:00.

Routes

MethodURIPurpose
GET/Landing page
GET/docsThis documentation
GET/healthMonitoring probe (no auth, no secrets)
GET/webhook/whatsappMeta verification handshake
POST/webhook/whatsappInbound messages (audit-logged + HMAC-gated)
POST/bitrix/eventsOperator replies (audit-logged + application_token-gated)
GET/POST/bitrix/installBitrix OAuth install handler
GET/POST/admin/credentialsCredentials form (ADMIN_UI_TOKEN-gated)
GET/setupLive readiness checklist (ADMIN_UI_TOKEN-gated)

Reliability & observability

Troubleshooting

SymptomLikely cause / fix
invalid_token on a Bitrix callStored OAuth grant isn't valid for the app/portal (never installed, reinstalled, or placeholder). Re-install the local app, then retry.
invalid_grant / "token refresh failed"The refresh token is fake/revoked/from another app. Re-install to mint a fresh grant. Confirm the client id/secret match the current app.
NOT_ACTIVE_LINEConnector registered but not connected to the line. Run php artisan bitrix:activate-connector.
Operator replies 403 at /bitrix/eventsbitrix.application_token doesn't match the token Bitrix sends. Paste the correct Outbound-webhook / local-app token into the form (or clear it to let the bridge bootstrap it).
WhatsApp send fails 401 / code 190Meta access token invalid or expired. Generate a non-expiring System User token and re-save; verify with whatsapp:check-token.
Meta error 131030Using a test number — add the recipient to the allowed list, or move to a production number.
Inbound 200 but nothing in BitrixQueue worker not running, or wrong bitrix.line_id / connector not activated. Check queue:failed.
Reply blocked, system note in chatOutside Meta's 24-hour window (templates are v2). Message status = blocked_window.
Stored credentials suddenly unreadableAPP_KEY changed — restore the original key.