Pipes.bot

Offline Queuing

How Pipes.bot handles messages when your WebSocket client is disconnected.

When your WebSocket client is offline, Pipes.bot queues incoming messages and delivers them when you reconnect. No messages are lost during brief disconnections.

How queuing works

  1. A WhatsApp message arrives while no matching connection is open
  2. Pipes.bot stores the message in a Redis queue
  3. When you open a user-scoped connection, all queued messages are delivered automatically
  4. Messages are delivered oldest-first with a 750ms delay between each to avoid overwhelming your client

Queuing and connection scopes

Queuing behavior depends on which connections are active when a message arrives:

  • At least one user-scoped connection open — message is delivered in real time, no queuing
  • Only pool-number-scoped connections open — message is delivered to matching connections (by pool number); if no connection matches, the message is queued
  • Only conversation-scoped connections open — message is delivered to matching connections; if no connection matches, the message is queued
  • No connections open — message is queued

Queue drain only happens on user-scoped connections. Pool-number-scoped and conversation-scoped connections do not trigger a drain to avoid partial queue complexity. If you rely on scoped connections exclusively, use the REST API or a separate user-scoped connection to handle queued messages.

Queue limits

SettingValue
Max messages100 per sender per account
Queue TTL24 hours from the sender's most recent queued message
Overflow behaviorOldest message dropped (FIFO)
Drain pacing750ms between messages

The TTL is refreshed on every push, so a sender's queue expires 24 hours after their latest message — an active sender's older messages can survive well past 24 hours, and don't expire independently.

Per-sender queues

Queues are scoped per sender. If 5 different people message you while offline, each sender gets an independent 100-message queue. This prevents a chatty sender from pushing out messages from others.

Overflow

When a sender's queue reaches 100 messages, the oldest message is dropped to make room for the new one. This ensures you always receive the most recent messages.

Drain behavior

When you open a user-scoped connection:

  1. All queued messages across all senders are retrieved
  2. Messages are sorted by server enqueue time (queuedAt), oldest first — not by the original WhatsApp message timestamp
  3. Each message is delivered with a 750ms pause between sends
  4. The connection state is checked before each delivery — if you disconnect during drain, remaining messages stay queued
  5. Queues are cleared atomically to prevent duplicate deliveries

Webhook fallback

Warning: Webhook-to-WebSocket fallback is not wired up in V1. When webhook delivery fails all 3 attempts, the payload is persisted to the WebhookDelivery table with status pending for manual retry — it is not delivered over WebSocket or re-queued. Do not rely on WS as an automatic webhook safety net.

  1. Webhook delivery is attempted up to 3 times (0s / 2s / 8s, 10s timeout)
  2. On exhaustion, the payload is saved to WebhookDelivery (status pending)
  3. Reconcile persisted failures yourself (there is no automatic re-delivery)

Offline notifications

On the first message queued during an offline period, Pipes.bot sends a WhatsApp message (from the pool number to the number that activated it — the fromNumber of the oldest conversation on that pool number) letting you know messages are waiting. This is not an email. It is deduplicated via a Redis key (offline_notified:{userId}) with a 1-hour TTL, so you get at most one notification per hour per offline period.

Queue expiry warnings

A background job runs every 15 minutes and, for any queue whose TTL is under 1 hour, sends the sender a WhatsApp warning that their queued messages will expire in ~1 hour (deduplicated for 2 hours).

App-scoped connections

BYON ak_ connections drain app-scoped queues (queue:app:{appId}:*) on connect, the same way user-scoped connections drain user queues. See WebSocket authentication.

On this page