# How BlueBot works

> The whole path of a text from your app to the person's phone, and of their reply back to you, with what happens at each step and how you hear about it.

BlueBot has three parts you deal with: the API (and everything built on it, such as Zapier and MCP), a small app in your Mac's menu bar, and webhooks that tell you what happened. Your texts go out from Messages on your own Mac, so they come from your own number and appear in your existing conversations. BlueBot gives you no phone number and never sends your texts from anywhere but your Mac.

```mermaid
flowchart LR
  App[Your app] -- POST /v1/messages --> BB[BlueBot: queue, rules, holds]
  BB -- claim every 10 s --> Mac[Your Mac]
  Mac --> IM[iMessage from Messages]
  Mac --> SMS[SMS through your iPhone]
  Mac -- replies and states --> BB
  BB -- webhooks --> App
```

## A text out

Dana runs a dog-grooming business. At 2:03 PM her booking system asks BlueBot to text Maria, a client, that Biscuit is ready for pickup. Here is everything that happens.

### 1. BlueBot accepts the text

Her system calls `POST /v1/messages` with Maria's number and the words. In the same request BlueBot:

- reads the number the way a person typed it and turns it into E.164 (`+15555550123`);
- finds Maria among Dana's contacts, or adds her;
- checks Dana's stop list. Had Maria texted STOP before, the text would be answered at once as `canceled` with `cancel_reason: "opted_out"`, and nothing more would happen;
- makes the text `scheduled` if it has a `send_at` in the future, or `queued` if it's due now;
- works out whether anything holds it right now (quiet hours, the Mac away, a daily limit, its turn in the pace) and writes that on the text as its `hold`.

The answer is `201` with the message, and `message.queued` fires. The request is over in a fraction of a second, whether the text can go now or at 8:00 tomorrow morning. Nothing in the API waits for the Mac.

### 2. The Mac takes it

The BlueBot app on Dana's Mac checks in every 10 seconds. Each answer says how many texts are ready, and the Mac claims them: up to five at a time, each under a 3-minute lease. At a slow pace it claims fewer, so the last one still goes before its lease runs out. A claimed text becomes `sending`.

If the Mac hasn't checked in for 2 minutes, it's away. Texts then wait with `hold.reason: "device_offline"` and go when it comes back, unless they expire first.

### 3. Messages sends it

For each text, the Mac hands the words to the Messages app through AppleScript, the same way a person would send them, and waits your pace (20 seconds by default) before the next. Pictures and files are downloaded first through links that last 10 minutes, then sent after the words.

The service is chosen like this:

- `auto` (the default) uses whatever the newest chat with Maria used, and iMessage when there's no chat yet.
- `imessage` sends an iMessage and nothing else.
- `sms` sends a regular text through Dana's iPhone, which needs Text Message Forwarding turned on.

[Services and SMS fallback](https://usebluebot.com/docs/concepts/services) says how each one behaves.

### 4. The Mac reads it back

The Mac counts a text as sent only when Messages shows it. It reads its own text back from Messages' database for up to 30 seconds and reports one of these:

| What the Mac saw                                     | What BlueBot does                                                                                                                                                           |
| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Messages shows it sent                               | The text becomes `sent`, with `service` saying iMessage or SMS. `message.sent` fires.                                                                                       |
| An iMessage failed and never went, on an `auto` text | BlueBot queues it once more as a regular text through the iPhone (`sms_retry: true`). It keeps its place in today's counts.                                                 |
| Messages refused it, or an iMessage-only text failed | The text becomes `failed` with a `failure.reason`. `message.failed` fires.                                                                                                  |
| It can't tell                                        | The Mac says nothing. When the lease runs out the text becomes `unconfirmed` and `message.unconfirmed` fires. It may have gone, so BlueBot never sends it again on its own. |

Most texts go in a few seconds. The last case is rare and is the price of never sending anyone the same text twice. [Message states](https://usebluebot.com/docs/concepts/message-states) covers each state and how to settle an `unconfirmed` text.

### 5. Delivered and read

For seven days after a text goes, the Mac watches it in Messages. When Messages records it delivered, and later read, the next check-in says so: the text becomes `delivered`, then `read`, and `message.delivered` and `message.read` fire. Only iMessage reports delivery, and only people who share read receipts report reading. A text that went as SMS stays `sent`.

## A reply in

Maria texts back, "On my way!" Here is that path.

1. **The Mac reads it.** On its next check-in the Mac reads new messages from Messages' database (it never changes that database) and posts them to BlueBot. Which conversations it reads is your **inbound mode**: `replies` (the default) reads only conversations with people you've texted through BlueBot, and group chats where everyone else is such a person; `everything` reads every conversation with a phone number or an email address. Short codes and business chats are never read.
2. **BlueBot stores it** as a message with `direction: "in"` and `state: "received"`, on Maria's contact and in her conversation.
3. **Stop words.** If the whole reply is a stop word such as STOP, Maria goes on the stop list and every text still waiting for her is canceled. [Stop words](https://usebluebot.com/docs/concepts/stop-words)
4. **Files.** A picture or voice memo comes up from the Mac separately, usually within seconds.
5. **`message.received` fires** once the files are up, or after 2 minutes without them, so your webhook can always fetch what the message names.

The Mac copies both directions of a conversation. A text Dana types herself on her iPhone, in a conversation BlueBot reads, also arrives, as `direction: "out"` with `source: "mac"`, and fires `message.sent`. Tapbacks, edits and unsends Messages records come through too, as `reaction.added`, `reaction.removed`, `message.edited` and `message.unsent`. So your app, your CRM or your AI assistant sees the whole conversation, including texts it didn't send.

When you first connect a Mac, it also copies those conversations from the 7 days before.

## How you hear about it

Every step above fires an event. A [webhook](https://usebluebot.com/docs/webhooks) posts each one to your server, signed, within seconds, and retries for about 72 hours if your server doesn't answer. The same events are in `GET /v1/events` for 30 days, so a program that was down can catch up. Each event carries the message as the API returns it, with the `metadata` you sent, so you can match it to your own record without another request.

| You want to know                | Listen for                                                 |
| ------------------------------- | ---------------------------------------------------------- |
| A text went                     | `message.sent`                                             |
| It reached their phone          | `message.delivered` (iMessage only)                        |
| They replied                    | `message.received`                                         |
| It didn't go                    | `message.failed`, `message.expired`, `message.unconfirmed` |
| They asked you to stop          | `contact.stopped`                                          |
| Your Mac went away or came back | `device.offline`, `device.online`                          |
| Something on the Mac needs you  | `device.check_failed`                                      |

The [event catalog](https://usebluebot.com/docs/webhooks/events) has every event with an example.

## Where things are kept

- **On BlueBot's servers:** the queue, your sending rules, your contacts and stop list, and every message. The words and files of a message are deleted after 30 days; its facts (who, when, its state and service) stay so your history and counts stay right. [Data retention](https://usebluebot.com/docs/concepts/data-retention)
- **On your Mac:** the app's sign-in token, in the Keychain, and the files of texts waiting to send, which it deletes once each text is reported, and within an hour otherwise. The app never writes to Messages' database and never changes anything in Messages but sending.
- **In your logs and analytics:** never the words of a text. BlueBot keeps them out of its own logs, analytics and error reports too.

## What it needs from you

BlueBot can only send while your Mac is on, awake, online and signed in to Messages. That's the trade for using your own number with no monthly line fee. A Mac mini on a shelf is the easiest way to meet it; a MacBook works if it stays plugged in and open. [The Mac app](https://usebluebot.com/docs/concepts/mac-app) lists everything it checks and how to fix each one.
