Skip to main content

Troubleshooting: message stuck in queued

A message that stays at queued has not been sent to a provider yet — it is sitting in the pre-delivery stage. This page walks you through what each status means, the causes that park a message at queued, and how to unstick it without making the problem worse. Distinguish two failure shapes before you start: a row at queued has never left Orbit — the hold is on your side of the provider handoff, and this page is the diagnostic. A row at submitted_no_receipt has already left Orbit and is waiting on a carrier delivery receipt — that is the post-queue stage, covered by the message sent but no delivery receipt page.

How message statuses fit together

Every message moves through a defined set of statuses, and each transition is gated — a message cannot jump from queued straight to delivered, and a terminal status like read or failed cannot move backward. The happy path for an immediate send is:
For a scheduled send, the message sits at scheduled until its send_at time arrives, then joins the queue:
Statuses like accepted (the synchronous pre-queue handoff), test_sent (sandbox mode), and cancelled (a scheduled message you cancelled before it sent) exist alongside the main path. Status updates are ordered, so each row can only move forward along an allowed transition — a stale carrier callback cannot drag a settled message back to queued.

Where to check the status

  • Delivery Log: open Messages → Tools → Delivery log and search the message ID (msg_…), the provider reference, or the recipient. Filter by the Status filter to isolate stuck rows — for example /messages/delivery-log?status=queued&direction=outbound shows every queued outbound message. See the Delivery Log guide.
  • Per-channel workspace: SMS, WhatsApp, and Email channels each show outbound history with the same status values.
  • API: call GET /api/v1/messages with a status=queued filter, or fetch the row by ID. The status field tells you where the row is; the response also tells you whether the row carries a send_at (scheduled), or a provider reference once it reaches sent.

Common causes and how to unstick them

Work through these in order — they cover the overwhelming majority of queued-for-longer-than-expected rows.
A message that never gets a delivery receipt moves to submitted_no_receipt — that means it left the queue. A row still at queued has never left the queue, which is exactly the state this page diagnoses.

Diagnosis checklist

  1. Get the message ID. Open the Delivery Log or your channel workspace and copy the msg_… ID from the row.
  2. Read the message detail. Open the full message detail page — it shows the send channel, the sender pool, the scheduled time if one exists, and the campaign it belongs to. Each of those fields maps to a row in the cause table above.
  3. Check the campaign. If the message came from a campaign, open that campaign’s settings: frequency caps, quiet hours, and sender pool are configured there.
  4. Check tenant-level compliance settings. Quiet-hours and sender-registration gates are organization-wide. Confirm the hold is intentional before you clear it.
  5. Query the API if the dashboard shows conflicting data. GET /api/v1/messages/:id returns the authoritative status, send_at, channel, and any provider reference.

What not to do

Do not spam POST /api/v1/messages/:id/retry while the cause is unknown. A retry against a quiet-hours hold, a pending sender approval, or a capped growth window just moves the same blocked message through the same gate again. Fix the cause first, then retry. Also avoid re-sending a fresh copy of the message to an end user while the original row is still queued — once the hold clears, both copies go out, and your recipient receives the same content twice.

When to escalate

If you worked through the checklist, the cause is not a hold you control, and messages are still stuck for more than a few minutes, open a support ticket and include both of these so we can trace the row without a back-and-forth:
  • Your tenant ID (shown in the dashboard under Settings → Organization, and returned by the GET /api/v1/me response as organizationId).
  • The message ID of one stuck row (msg_…).
For a stuck email send, also include your warm-up position — the currentDay and todayCap values from GET /api/v1/email/warmup-status — so support can tell a deliberate ramp hold from a real fault on the first reply. That pair lets support pull the exact queue entry and the exact send-gate evaluation without you having to re-describe the symptom.

Status decoder

Every status value the Delivery Log filter and the GET /api/v1/messages response can return, and what it means for diagnosis: If a row is sitting on any of the blocked-in-queue statuses (queued, scheduled, pending, accepted, sending) for longer than you expect, the cause table and checklist above will get you to the hold faster than a blind retry.