# Stop words and the stop list

> How a reply of STOP puts a person on your stop list, what BlueBot does with their waiting texts, how START undoes it, and how to read and change the stop list through the API.

Anyone you text can tell you to stop, and when they do, BlueBot stops. A reply of only STOP puts the person on your **stop list**: every text still waiting for them is canceled at once, and every text you send them after that is canceled too, until they or you take them off.

## The words

- **Stop words:** `STOP`, `STOPALL`, `UNSUBSCRIBE`, `CANCEL`, `END`, `QUIT`
- **Start words:** `START`, `UNSTOP`, `YES`

A reply counts only when the whole text is one of these words. BlueBot ignores capitals, spaces around the word and a period or exclamation mark at the end, so "Stop", " STOP " and "stop." all count. "Please stop texting me" doesn't, and neither does "Stop by at 3?" If someone writes a sentence asking you to stop, put them on the stop list yourself (below).

Stop words are read only in one-to-one conversations. A STOP in a group chat is a message to the whole group, so BlueBot doesn't act on it.

> **CANCEL, END and YES are on the lists.** A reminder that says "Reply CANCEL to cancel your appointment" will put everyone who replies CANCEL on your stop list, and you won't be able to text them again until they text START or you take them off by hand. Ask for a word that isn't a stop word, such as "Reply C to cancel" or "Reply CHANGE to move it." YES is a START word, so it only matters for someone who has already stopped.

### Your own list

You can replace the list with your own under **Settings → Stop words**, or with `stop_words.words` on `PATCH /v1/settings`. Your list is the whole list: a word you add, such as BASTA, stops people, and a word you take off no longer does. A keyword rule or Autopilot never answers a word on your list, and can answer one you took off, so taking CANCEL off lets a rule for CANCEL handle appointment cancellations. Send `null` to go back to the defaults. [Sending rules](https://usebluebot.com/docs/concepts/sending-rules#stop-words)

## What happens when someone stops

Maria gets a text from Dana's salon and replies "STOP." When the Mac copies the reply:

1. Maria goes on the stop list with `source: "reply"`.
2. Every text still `queued` or `scheduled` for her is canceled with `cancel_reason: "opted_out"`, and `message.canceled` fires for each.
3. `contact.stopped` fires, with Maria as a contact whose `stopped` is `true`.
4. Any text sent to her later is accepted with `201` and canceled at once, again with `opted_out`, so your code needs no special case.

The answer, `201`:

```json
{
  "id": "msg_4Tz8RkW1qPnB6yLcVd2e3A",
  "object": "message",
  "state": "canceled",
  "cancel_reason": "opted_out",
  "to": "+15555550123",
  "hold": null
}
```

A text the Mac had already taken (`sending`) when she replied isn't stopped, because it was already in Messages. BlueBot doesn't text her back to confirm; if you want to, type it yourself in Messages. Her reply still arrives as `message.received`, like any other.

Group chats she's in aren't affected. Texts into a group go to everyone in it, and the stop list applies only to texts sent to her alone.

## Starting again

- **She texts START, UNSTOP or YES.** That takes her off the list, but only if she stopped by replying. `contact.unstopped` fires.
- **You take her off by hand.** In the dashboard, or with `DELETE /v1/stops/{person}`. This works whoever put her there.

A START word never lifts a stop that you or an import made. If you stopped someone by hand, only you can undo it.

Texts that were canceled while she was stopped stay canceled. Send them again if they still make sense.

## The stop list API

The stop list is people, by phone number or email address. A key needs `contacts:read` to read it and `contacts:write` to change it.

### Read the list

```bash
curl "https://usebluebot.com/api/v1/stops?limit=50" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY"
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const page = await client.stops.list({
  limit: 50,
});
console.log(page.data);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

page = client.stops.list(limit=50)
print(page.data)
```

The answer, `200`:

```json
{
  "object": "list",
  "data": [
    {
      "object": "stop",
      "handle": "+15555550123",
      "contact_id": "ct_6Mn3QbX8rYv1KdPw5Tz0sH",
      "stopped_at": "2026-11-13T19:41:00.000Z",
      "source": "reply"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

`source` says how each person got there: `reply` (they texted a stop word), `dashboard`, `api`, `import` (a contact list you uploaded marked them) or `admin` (a person at BlueBot). The list pages like every other list. [Pagination](https://usebluebot.com/docs/concepts/pagination)

### Stop texting someone

Priya emails Sam's office to say she doesn't want texts. Sam's CRM adds her to the stop list:

```bash
curl -X POST "https://usebluebot.com/api/v1/stops" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: stop-priya-20261113" \
  -d '{
    "person": "+15555550188"
  }'
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const stop = await client.stops.add({
  person: '+15555550188',
}, { idempotencyKey: 'stop-priya-20261113' });
console.log(stop);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

stop = client.stops.add(person="+15555550188", idempotency_key="stop-priya-20261113")
print(stop)
```

The answer is `201` with the stop. Her waiting texts are canceled, and `contact.stopped` fires, exactly as if she'd replied STOP, except that `source` is `api` and only a person can undo it. Adding someone already on the list changes nothing and keeps the first stop's `source`. A US number may leave off the `+1`, and `(555) 555-0188` works too.

| You got                                                             | It means                                                     | Do next                                                                                                        |
| ------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| `201` with `"object": "stop"`                                       | She's on the list.                                           | Check that `GET /v1/stops` lists her. A text to her now comes back `canceled` with `opted_out` and never goes. |
| `400` [`invalid_request`](https://usebluebot.com/docs/errors/invalid_request) on `person` | BlueBot can't read it as a phone number or an email address. | Send the number with its country code, such as `+15555550188`.                                                 |
| `403` [`insufficient_scope`](https://usebluebot.com/docs/errors/insufficient_scope)       | The key can't change contacts.                               | Use a key with `contacts:write`.                                                                               |

### Text someone again

```bash
curl -X DELETE "https://usebluebot.com/api/v1/stops/%2B15555550188" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY"
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const result = await client.stops.remove('+15555550188');
console.log(result);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

result = client.stops.remove("+15555550188")
print(result)
```

Put the person in the path, with `+` written as `%2B`. The answer is `200` with `"deleted": true`, and `contact.unstopped` fires. If they weren't on the list, it's `404` [`not_found`](https://usebluebot.com/docs/errors/not_found).

Only take someone off when they've asked to hear from you again. A person who replied STOP and then gets texts anyway is the complaint that gets numbers reported.

## Turning stop words off

```bash
curl -X PATCH "https://usebluebot.com/api/v1/settings" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "stop_words": {
      "enabled": false
    }
  }'
```

```ts
import BlueBot from '@usebluebot/sdk';

const client = new BlueBot({ apiKey: process.env.BLUEBOT_API_KEY });

const settings = await client.settings.update({
  stopWords: {
    enabled: false,
  },
});
console.log(settings);
```

```python
import os
from bluebot import BlueBot

client = BlueBot(api_key=os.environ["BLUEBOT_API_KEY"])

settings = client.settings.update(
    stop_words={
        "enabled": False,
    },
)
print(settings)
```

With stop words off, a reply of STOP is an ordinary message: nobody goes on the list and nothing is canceled. The stop list itself still works, so people already on it stay stopped, and adding someone by hand still cancels their texts. The settings answer with this warning: "Stop words are off. Someone who replies STOP will keep getting texts until you stop them by hand."

Keep them on unless your own code reads every reply and keeps its own list. If you turn them off, listen for `message.received` and add people to the stop list yourself when they ask.
