Skip to the page

Concepts

Holds

Why a text that can't go yet is accepted and waits instead of failing, every reason it can wait, the order they're checked in, and when each one clears.

A text that can't go right now isn't refused. BlueBot accepts it, answers 201, and puts a hold on it that says why it's waiting and, when it knows, until when. When the hold clears, the text goes by itself. Your code never needs to retry a send because of quiet hours, a sleeping Mac or a daily limit.

What a hold looks like

Every outgoing message has a hold field. It's null when nothing holds the text, and an object when something does:

Response201A text sent at 10:40 PM
{
  "id": "msg_1Hq2LxW9cVbN3mKpRtYs0a",
  "object": "message",
  "state": "queued",
  "to": "+15555550123",
  "hold": {
    "reason": "quiet_hours",
    "until": "2026-11-14T14:00:00.000Z",
    "description": "Waiting for quiet hours to end at 8:00 AM tomorrow."
  },
  "expires_at": "2026-11-15T04:40:00.000Z"
}
FieldWhat it is
reasonWhy it waits, as a stable code. Program against this.
untilWhen it should stop waiting, when that's known: the end of quiet hours, the next midnight, or the text's estimated slot in the pace. null when nobody can say, such as while the Mac is away.
descriptionThe hold in words, in your time zone, ready to show a person.

A hold is only ever on a queued text. A scheduled text has none until it's due.

Every reason

hold.reasonWhat it meansWhen it clears
account_heldBlueBot’s operator paused this account’s sending. Nothing sends.Only BlueBot, after you write to support. Meanwhile the API still accepts sends, and they wait with this hold.
platform_stoppedBlueBot stopped all sending for every account, for an incident.BlueBot turning sending back on. Texts go in their order, subject to their expiry.
plan_lapsedThe account’s plan isn’t active.An active plan again (update the card on the Billing page).
pausedYou paused sending from the Mac’s menu or the dashboard.Resuming, or the pause’s own end (until): an hour, or 8:00 AM in your zone.
device_offlineYour Mac hasn’t checked in for 2 minutes: asleep, off, or off the internet.The Mac checking in again. If it stays away past the text’s expires_at, the text expires.
quiet_hoursIt’s inside your quiet hours (9:00 PM to 8:00 AM in your zone unless you changed them).until: the end of quiet hours. The text then takes its place in the pace.
daily_new_people_limitYou’ve texted as many new people today as your setting allows (50 by default). People you already have a conversation with don’t count.until: the next midnight in your zone, or raising the setting.
platform_daily_limitThe account sent 1,000 texts today, BlueBot’s ceiling for one account.until: the next midnight in your zone. This one isn’t a setting.
pacingWaiting its turn: the Mac leaves your pace (20 seconds by default) between texts.until: its estimated slot. It moves as texts ahead of it go or are canceled.

The order they're checked

BlueBot checks the reasons in a fixed order and shows the first that applies, so a text has one hold at a time. The first six apply to the whole account, so every queued text shares them:

  1. account_held
  2. platform_stopped
  3. plan_lapsed
  4. paused
  5. device_offline
  6. quiet_hours

The last three apply text by text, in the order the texts were queued:

  1. daily_new_people_limit, only for a text to someone you've never texted with through BlueBot
  2. platform_daily_limit
  3. pacing

When one hold clears, the next may show. A text queued at 11:00 PM waits on quiet_hours; at 8:00 AM, if twenty texts went in ahead of it, it waits on pacing for a few minutes more, then goes. A text held by a daily limit doesn't use up a place in the pace, so texts behind it that can go still go.

Holds are worked out again every time anything changes: a send, a cancel, a settings change, the Mac checking in or going away. Raise new_people_per_day and the texts waiting on it are ready on the Mac's next check-in.

What a hold doesn't change

  • Expiry still runs. A held text that's still waiting at its expires_at (24 hours after it was due, by default) becomes expired and never goes. Choose expires_at for texts that only make sense soon, and longer for ones that can wait. Limits
  • You can still cancel it. A held text is queued, and POST/v1/messages/{id}/cancel cancels it.
  • The stop list isn't a hold. A text to someone on your stop list doesn't wait; it's canceled at once with cancel_reason: "opted_out". Stop words

New sends while the account is held

Most holds let new sends in: they're accepted and wait with everything else. Two refuse new work instead, because nothing will move until a person acts:

  • A lapsed plan answers new sends, uploads and webhook creation with plan_lapsed (402). Texts already queued wait with hold.reason: "plan_lapsed" and go once the plan is active again, if they haven't expired. Reading, canceling and changing settings keep working.
  • BlueBot pausing your account shows as account_held on every queued text. Write to support@usebluebot.com; a person at BlueBot releases it.

Don't retry a held text

The answer to a held send is a success. Sending the same text again makes a second text with its own hold, and both go when the hold clears. If your code sends in a loop, send each text once with an Idempotency-Key, and let the holds do the waiting. Idempotency

Find what's held

  • GET/v1/device counts them: held_count is how many queued texts are waiting on a hold, and queued_count is every queued text.
  • GET /v1/messages?state=queued lists them, each with its hold.
  • message.queued fires once when a text is queued and carries the hold it had at that moment. A hold changing doesn't fire an event. Listen for message.sent, message.expired and message.canceled to learn how each one ends.
GET/v1/messagesList texts →
curl "https://usebluebot.com/api/v1/messages?state=queued&limit=50" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY"
You gotIt meansDo next
Every text with the same hold.reasonAn account-wide hold: the Mac is away, it's quiet hours, or sending is paused.Fix the cause, or wait for hold.until.
pacing with times spread over the next hourA long line at your pace.Nothing; they go in order. Lower pace_seconds only if you understand the risk. Sending rules
daily_new_people_limit on some textsYou've texted as many new people today as your setting allows.Wait for midnight, or raise new_people_per_day.
device_offline with until: nullThe Mac hasn't checked in for 2 minutes.Wake it, or check its power and network. The Mac app

Tell people why

If your app shows what it sent, show hold.description. It's written for a person and set in your account's time zone: "Waiting for quiet hours to end at 8:00 AM tomorrow.", "Waiting for your Mac to come back online.", "Waiting its turn. It should go around 2:41 PM." The dashboard, the Mac's menu and BlueBot's MCP tools all use the same words.