Conversation lifecycle

How an Apple Messages for Business conversation flows end to end — inbound-first threading, typing indicators, read receipts, bot-to-human handoff, and closing — all through the Blooio v4 chats API.

An Apple Messages for Business conversation has a shape: a customer opens it, you (an automation, a human agent, or both) work it, and eventually it closes. This page walks that lifecycle and the chat controls that go with it — typing indicators, read receipts, handoff between automation and humans, and closing.

Everything here hangs off a chat. The inbound message.received webhook that opens a conversation carries a chat_id; use it with the chats endpoints for the rest of the conversation.

A conversation handled by automation first, then escalated to a human agent and resolved. Click a step for details.

The lifecycle

  1. Open. The customer taps a Messages entry point (or accepts an invitation). Apple mints an opaque id for them and Blooio delivers message.received with the chat_id and the opaque id as the sender.
  2. Reply. You answer by addressing the opaque id, or by posting to the chat directly with POST /v4/chats/{chatId}/messages (the sender and recipient are fixed by the chat, so omit from/to).
  3. Work the thread. Show typing, mark it read, send interactive messages, take payment, or hand off to a human.
  4. Close. The conversation ends — resolved by you, or left by the customer. See Closing & opt-out.

The opaque id is stable for that customer and your brand, so a returning customer threads back into the same conversation history.

Typing indicators

Show a typing indicator before a reply so a pause reads as "thinking" rather than a stall. Start it with POST /v4/chats/{chatId}/typing:

curl -X POST https://api.blooio.com/v4/chats/chat_.../typing \
  -H "Authorization: Bearer bl_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "state": "started" }'
Try it

Clear it by sending { "state": "stopped" }, or with DELETE /v4/chats/{chatId}/typing. You don't have to clear it manually: Apple auto-expires a typing indicator 60 seconds after it starts if you never send a message or a stop. A good rhythm is about one second of typing before each message you send — see the timeouts in Capabilities & limits.

When the customer is typing to you, Blooio delivers typing.started / typing.stopped webhook events so your agent console can show the same.

Read receipts

Mark a conversation read when your agent (or bot) has seen it, with POST /v4/chats/{chatId}/read:

curl -X POST https://api.blooio.com/v4/chats/chat_.../read \
  -H "Authorization: Bearer bl_live_..."
Try it

Handoff: automation ↔ human

Most desks run a bot for the first touch and bring in a human when the question needs one. Blooio emits handoff webhook events so your systems can coordinate who owns the conversation:

Event Fired when
handoff.created A conversation is handed off (for example, a bot escalates to a human queue).
handoff.customer_reply The customer replies while a handoff is pending.
handoff.acknowledged An agent (or system) picks up the handoff.
handoff.resolved The handoff is closed out.

Subscribe to these alongside message.received to drive routing: when handoff.created fires, surface the thread in your agent console; when a human replies, send through the same chat so the customer sees one continuous conversation. Replies from automation and from humans both go out over POST /v4/chats/{chatId}/messages — the channel is the same; only your internal routing changes.

Note A customer can ask your automation to stop. Honor it: once a customer has opted out of automated messages, route the conversation to a human and don't resume bot sends until they re-engage.

Closing & opt-out

A chat is normally open. It becomes closed when the conversation ends — including when the customer leaves or reports the conversation (Apple's CloseSession). Blooio surfaces this as a chat.closed webhook event; for a conversation that began as an invitation, the close carries your original reference_id.

  • Sending to a closed chat is rejected with a conflict such as 409 chat_closed — don't retry; wait for the customer to re-engage.
  • Mark a thread closed from your side with PATCH /v4/chats/{chatId} when your product considers it resolved.
  • Respect opt-out. If a customer leaves or reports a conversation that started from an invitation, do not invite that number again until they opt back in. See Business invitations.