Skip to main content
iBubbl never sends an iMessage outside of the recipient’s working hours. This protects your sender reputation and respects contact preferences — late-night blasts are the fastest way to get a phone number flagged. The one exception is an after-hours auto-reply: a reply to someone who texted you in the last 20 minutes can go out straight away.

How it works

Every outbound message — sent from the dashboard, POST /v1/messages, a workflow, a scheduled send or an integration — waits for the recipient’s working hours before it leaves your device:
  • POST /v1/messages accepts the send straight away (202 Accepted) with status: "pending", whatever the time.
  • If the recipient’s local time is inside your working window, the message goes out within seconds.
  • If it is outside, the message stays pending and goes out automatically when the window next opens. There is no separate status or webhook for this wait; a message.sent webhook fires when it goes out.
You don’t need to do anything to opt in. The behavior is on by default for every account.

Configuring working hours

Working hours are set in the dashboard at Settings → Sending Hours. Defaults for new accounts: The end time is exclusive: with the default window, sends pause at 8:00 PM. The window is applied in each contact’s timezone (see below), so there is no timezone setting on the window itself. These are only your sending hours. When people can book meetings with you is a separate setting, Settings → Booking Hours, in your own time zone. You can configure different windows per day of the week, or close specific days entirely. The dashboard is the only way to manage these settings — there’s no API surface for them today.

Per-contact timezone

The local time check uses the contact’s timezone, not your account’s. iBubbl determines a contact’s timezone in this order:
  1. Explicit override — if you set the timezone field on the contact (via POST /v1/contacts or the dashboard).
  2. Area-code inference — for North American numbers, iBubbl infers a default timezone from the area code (e.g., +1415... → America/Los_Angeles).
  3. Account timezone — the timezone in your dashboard’s Settings → Notifications, which starts as your browser’s timezone.
  4. America/New_York — if none of the above is set.
To set a contact’s timezone yourself, pass it on contact upsert before you send to them:
Once a contact has a timezone, an upsert never changes it. That includes the inferred one: the area-code guess is saved on the contact when their first message is processed, so a timezone sent on a later upsert is ignored. Upsert first, then send. The timezone must be one of the supported zones (US, Canada, and a few international ones); anything else returns 422 invalid_timezone.

Detecting a held send

A message waiting for working hours is pending, like one that is about to go out. GET /v1/messages/{id} tells them apart with two fields:
  • held_reason — why it is waiting right now: working_hours (outside the contact’s working hours) or hourly_limit (your line has messaged 30 new contacts in the last 60 minutes; the message stays pending and goes out by itself when a slot frees — see Limits and queues).
  • held_until — when it is expected to go out. For working_hours, the contact’s next opening: it is recomputed within about 10 seconds when your hours change, and it is null if no sending day is enabled. For hourly_limit, an estimate of when a slot frees, or null if there is none.
Both are null when the message is not being held. A held message is first marked within about 10 seconds of being accepted, so a GET immediately after POST may not show the hold yet. A message.sent webhook fires when it goes out.

Capacity holds (status: "queued")

Separately from working hours, a one-to-one send can be accepted but parked. The response is still 202 Accepted, with status: "queued". Today the only reason is line_offline: your line’s Mac is offline. The reason is in the failed_reason field of the message.queued webhook, not in the API response. (hourly_cap_hit is a legacy reason that no current line uses; the hourly new-contact limit holds messages as pending instead.) iBubbl re-checks queued messages every 30 seconds and sends them once the Mac is back. A message still queued 60–65 minutes after it was accepted is dropped and moves to status: "expired", and a message.failed webhook fires with status: "expired" and failed_reason: "queue_expired". Send it again (a new idempotency_key, or the same one: an expired message’s key is released) if it still matters. A pending message works differently: if its line has been offline for over an hour, it is not sent late. It moves to status: "failed" with failed_reason: "agent_offline", and a message.failed webhook fires. The daily limits do not queue: a send over 50 new contacts or 1,000 messages a day fails immediately with a 422. See Limits and queues for every limit and Errors for the codes.