Docs

Reach · 08.05

The Channel Turn Queue: Durable Inbound Dispatch

How an inbound channel message survives a restart and reliably becomes an agent turn — the queue behind 'messages never get lost.'

Turn queue

From fire-and-forget to a real durable job

Before this queue existed, an inbound channel message was dispatched fire-and-forget: one in-memory process, no backpressure, and a restart mid-flight silently dropped every in-progress conversation on a busy desk. Now an inbound message is enqueued as a durable, at-least-once job — idempotent on the inbound message's own id, so a webhook retry can never double-process the same message — and the webhook still gets its fast acknowledgment immediately, before the actual turn has even started running.

Ordered per thread, capped per App, resumable after a crash

A background poller claims pending rows once their backoff window has elapsed, using a compare-and-swap claim so two pollers can never grab the same row. Turns for a single conversation thread run with concurrency 1 — strictly in order — while a per-App in-flight cap stops one unusually busy App from starving every other App's turns. A crash mid-processing simply leaves the row claimed; its lease (5 minutes by default) expires and the poller re-picks it up automatically, bounded by an attempt cap with backoff between retries before a truly stuck job is parked as failed rather than retried forever.

Continue