Custom email module for 1C-Bitrix with queues and tracking

Standard email module in 1C-Bitrix starts failing with a database of over 10,000 contacts. Clients complain about missed emails—the default `subscribe` becomes a bottleneck. Without queues and tracking, scaling email marketing is impossible. Our team with 10 years of experience builds custom subscri

Our competencies:

Frequently Asked Questions

Standard email module in 1C-Bitrix starts failing with a database of over 10,000 contacts. Clients complain about missed emails—the default subscribe becomes a bottleneck. Without queues and tracking, scaling email marketing is impossible. Our team with 10 years of experience builds custom subscription and email modules turnkey: with queues, tracking, and flexible segmentation. Estimated development time—from 2 weeks. Contact us to discuss your project.

Why the standard subscribe module doesn't work for large mailings?

The standard module stores subscribers in the b_subscribe_subscr_addr table and email templates in b_subscribe_posting. Sending logic uses CAgent agents that run synchronously during HTTP requests. With over 10,000 contacts, this leads to timeouts and missed emails. Additionally: no built-in open tracking, no retry queue for SMTP errors, no support for transactional emails with dynamic content via API. In one project, we replaced the standard module with a custom one and reduced email marketing costs by 35% by abandoning expensive third-party services. The custom module processes 3 times more emails per unit time and reduces spam complaints by 5 times, ensuring up to 95% deliverability.

How does a custom module improve deliverability?

The module registers in the standard way via /bitrix/modules/vendor.mailer/install/index.php with the class vendor_mailer. Namespace is \Vendor\Mailer.

Key components

  • SubscriberTable — ORM table b_vendor_mailer_subscriber (id, email, name, status, groups, created_at, unsubscribed_at)
  • CampaignTable — b_vendor_mailer_campaign (id, name, subject, template_id, status, scheduled_at, sent_count, open_count)
  • QueueTable — b_vendor_mailer_queue (id, campaign_id, subscriber_id, status, attempts, last_error, sent_at)
  • EventTable — b_vendor_mailer_event (id, subscriber_id, type, payload, created_at) — open and click tracking

Tables are created via \Bitrix\Main\ORM\Data\DataManager with full D7-ORM support. For reliability, we use tagged cache for information blocks—this speeds up work with large data volumes.

Send queue implementation details

Synchronous sending via CAgent is a dead end. We use a queue:

// Enqueue when campaign starts public function enqueue(int $campaignId): void { $subscribers = SubscriberTable::getList([ 'filter' => ['=STATUS' => 'active', '=GROUPS' => $this->campaign->getGroups()], 'select' => ['ID', 'EMAIL', 'NAME'], ]); while ($row = $subscribers->fetch()) { QueueTable::add([ 'CAMPAIGN_ID' => $campaignId, 'SUBSCRIBER_ID' => $row['ID'], 'STATUS' => 'pending', 'ATTEMPTS' => 0, ]); } } 

The queue handler runs as an agent every 2 minutes, fetches a batch of 100 records with status pending, sends via the selected transport, and updates the status to sent or failed.

Transports: SMTP and API providers

Transport abstraction is implemented via MailTransportInterface:

interface MailTransportInterface { public function send(Message $message): SendResult; } 

Concrete implementations: SmtpTransport (via PHPMailer or native mail()), SendGridTransport (REST API v3), MailgunTransport, AmazonSesTransport. The transport is selected in module settings (b_option, namespace vendor.mailer).

On send error, status changes to failed, attempts counter increments. When attempts >= 3, the record is marked as dead and not retried.

Open and click tracking

A transparent 1×1 pixel is inserted into the email:

<img src="https://site.ru/mailer/track/open/?uid=UNIQUE_TOKEN" width="1" height="1"> 

On GET request, the controller logs the event in b_vendor_mailer_event and returns a 1×1 GIF. Clicks are tracked via redirect: all links in the email are replaced with https://site.ru/mailer/track/click/?uid=TOKEN&url=ENCODED_URL.

Event handler:

EventTable::add([ 'SUBSCRIBER_ID' => $subscriberId, 'CAMPAIGN_ID' => $campaignId, 'TYPE' => 'open', // or 'click' 'PAYLOAD' => json_encode(['url' => $url, 'ip' => $_SERVER['REMOTE_ADDR']]), 'CREATED_AT' => new \Bitrix\Main\Type\DateTime(), ]); 

Subscriber segmentation

Groups are stored in b_vendor_mailer_group, links to subscribers via b_vendor_mailer_subscriber_group. Segmentation supports conditions based on Bitrix user profile fields (b_user) via JOIN.

Dynamic segments are built using a filter builder in the admin interface—the same approach as in Bitrix CRM: a set of conditions with AND/OR operators that are translated into an ORM filter when enqueuing.

Transactional emails

For system emails (email confirmation, password reset, order notifications), the module provides an event API:

// Send transactional email \Vendor\Mailer\Transactional::send('order_confirmed', [ 'USER_ID' => $userId, 'ORDER_ID' => $orderId, 'ORDER_SUM' => $orderSum, 'ITEMS' => $orderItems, ]); 

Transactional email templates are stored in b_vendor_mailer_template and editable via the admin section with support for variables like {{ORDER_ID}}.

Admin interface

The module adds sections in /bitrix/admin/:

  • vendor_mailer_subscribers.php — subscriber list and management
  • vendor_mailer_campaigns.php — campaigns, status, statistics
  • vendor_mailer_templates.php — template editor
  • vendor_mailer_settings.php — transport settings, DKIM, limits

The interface uses standard CAdminList, CAdminForm classes—full compatibility with the Bitrix theme.

Unsubscribe and GDPR

An unsubscribe link in every email leads to https://site.ru/mailer/unsubscribe/?token=TOKEN. On click, subscriber status changes to unsubscribed, and unsubscribed_at is filled. Re-enqueuing such records is blocked at the filter level.

For GDPR compliance: export subscriber data on request (GET /mailer/export-personal/?token=TOKEN), full deletion via SubscriberTable::delete() with cascading deletion from all related tables.

What is included?

  • Documentation: architecture description, DB schema, installation manual.
  • Module source code with license.
  • Training: 2-hour webinar for administrators.
  • Support: 30 days after launch (bug fixes, consultations).
  • Compatibility guarantee with Bitrix 20.0+ and PHP 8.1+.

Comparison of standard and custom modules

Criteria Standard subscribe Custom module
Send queue Synchronous via CAgent Async, up to 100 emails per tick
Segmentation Only by groups Dynamic AND/OR filters, JOIN with b_user
Open/click tracking Absent Transparent pixel + redirect
Transactional emails No Event API, templates with variables
SMTP integration Only built-in Transport abstraction, any provider
GDPR / unsubscribe Basic unsubscribe Unsubscribe link, export, data deletion

Typical mistakes when developing an email module

  • Ignoring queues—sending in a loop freezes the site. Always use an async queue.
  • Lack of retries—if an email fails, must retry up to 3 times, otherwise you lose subscribers.
  • No tracking—cannot evaluate campaign effectiveness. Build in statistics collection from the start.
  • Rigid binding to one SMTP—if the provider goes down, mailing stops. Use failover transports.

Timeline and cost

Stage Time
Module structure, ORM tables, installer 2 days
Queue and send agents 2 days
SMTP transport + 1 API provider 1 day
Open and click tracking 1 day
Segmentation, groups 1 day
Transactional emails 1 day
Admin interface 2 days
Unsubscribe, GDPR, testing 1 day

Total: from 11 working days for a basic version. Connecting additional API providers (Mailchimp, UniSender) — +1 day each.

Development cost is calculated individually after analyzing your requirements. Order module development today. Get a consultation—we will help you choose the optimal solution.

For an in-depth understanding of Bitrix ORM, we recommend studying the official documentation. Also useful to read about SMTP protocol and REST API.