Skip to main content

Lanes

Three lanes. A template's lane_class is fixed. /send cannot move a transactional template onto auth.

LaneWho uses itFrom addressRetryTTL
authDirect send: magic link, invitationno-reply@shellui.com, or the company from_email2s, then 6s, then one platform SMTP failoverDefault 120s (magic link) or 300s (invitation). Max 300s.
transactionalEvent ingest and other direct product mailSame as auth30s * 2^(attempt-1), cap 1 hour, 8 attemptsNone
bulkBroadcastsThe company Bulk From, or news@news.shellui.com for Shellui companiesSame as transactionalNone

/send with lane bulk, or a bulk template, returns 400 lane_requires_campaign. Bulk mail only goes out as a broadcast. Bulk messages carry one-click List-Unsubscribe headers and skip the company's bulk unsubscribes.

Auth TTL​

The worker checks expires_at before provider handoff. An expired auth message becomes expired, the variables are deleted, and email.message.expired is emitted. That increments shellui_email_auth_ttl_expiries.

A hard bounce suppresses the address for auth mail for 30 days and returns 422 recipient_suppressed on the next auth send. Complaints and unsubscribes do not block the auth lane.

Auth rate limit: 5 messages per recipient per company per 10 minutes (429 recipient_rate_limited).

Pause​

POST /api/v1/lanes/{lane}/pause and /resume are staff-only and pause that lane for every company. A provider HTTP 401 or 403 pauses only the company whose key was rejected (reason provider_unauthorized). Other companies keep sending. New sends for a paused company receive 409 lane_paused until that pause is cleared.

Auth mail also has a company-wide cap (EMAIL_COMPANY_AUTH_LIMIT, default 30 per EMAIL_COMPANY_AUTH_WINDOW_SECONDS, default 60). Over the cap the response is 429 company_rate_limited. The per-recipient cap is unchanged.

Workers​

python manage.py run_email_worker polls every second and claims due rows. On Postgres it also issues NOTIFY email_lane_{lane} when a message is queued. The worker does not LISTEN. The notify is reserved for a later wake-up. SQLite (local) has no notify.

python manage.py sweep_email_queue runs one pass. Use it from cron if you do not keep a resident worker.

The bulk worker (--lane bulk) and the sweep also move broadcasts forward.

Production must set EMAIL_DELIVER_SYNC=false. The sync flag delivers inside the request and is for tests.