> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blujay.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Opt-outs

> Stop messages to a contact or an address, lift an opt-out, and handle RECIPIENT_SUPPRESSED on sends.

When someone asks you to stop messaging them, record an opt-out in Blujay. Blujay then rejects API sends to that person and won't enroll them in workflows. Opt-outs are stored by phone number or email address, so they hold even if the contact is deleted and created again.

On a Dedicated number, Blujay also opts a contact out automatically when they text exactly `STOP` and nothing else. Other wording, such as "please stop", does nothing by itself: read the reply and opt them out with the API. See [Plans](/plans.md) for which numbers are Dedicated.

## Opt out a contact

`POST /v1/contacts/{contact_id}/suppress` opts out the contact's phone number, or their email if they have no phone number. `reason` is optional (up to 500 characters) and is stored with the opt-out. The body can be empty.

```bash curl theme={null}
curl https://api.blujay.io/v1/contacts/contact_abc123/suppress \
  -H "Authorization: Bearer blu_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Asked not to be contacted" }'
```

```json Response (200) theme={null}
{
  "status": "success",
  "data": {
    "contact_id": "contact_abc123",
    "phone_number": "+15555550123",
    "email": "ava@example.com",
    "name": "Ava Chen",
    "tags": ["VIP"],
    "stage": "Active",
    "assigned_team_member": null,
    "opted_out": true,
    "opted_out_at": "2026-10-14T17:45:00.000Z",
    "opted_out_source": "api",
    "messaging_allowed": false,
    "created_at": "2026-10-14T17:00:00.000Z",
    "updated_at": "2026-10-14T17:45:00.000Z"
  },
  "trace_id": "trc_0c2e4a6b8d1f",
  "timestamp": "2026-10-14T17:45:00.060Z"
}
```

`opted_out_at` and `opted_out_source` appear only while the contact is opted out. `opted_out_source` says how the opt-out was made:

| `opted_out_source` | Meaning |
| - | - |
| `api` | Set through this API. |
| `inbound_stop` | The person texted `STOP` to a Dedicated number. |
| `manual` | A team member opted them out in Blujay. |
| `internal` | Set by Blujay, for example a do-not-contact flag carried over from the app. |

Opting out a contact that is already opted out succeeds again and returns the contact.

## Opt out an address

To opt out a phone number or email that may not belong to a contact yet, use `POST /v1/contacts/suppressions` with the `address`. Use this when you import a do-not-contact list from another system.

```bash curl theme={null}
curl https://api.blujay.io/v1/contacts/suppressions \
  -H "Authorization: Bearer blu_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "address": "+15555550125", "reason": "Imported do-not-contact list" }'
```

```json Response (200) theme={null}
{
  "status": "success",
  "data": {
    "address_type": "phone",
    "address": "+15555550125",
    "opted_out": true,
    "opted_out_at": "2026-10-14T17:46:00.000Z",
    "opted_out_source": "api",
    "reason": "Imported do-not-contact list"
  },
  "trace_id": "trc_6a8c0e2b4d1f",
  "timestamp": "2026-10-14T17:46:00.040Z"
}
```

`address` must be a US phone number in any accepted format (see [Contacts](/contacts.md#create-a-contact)) or an email address; anything else returns `400` [`VALIDATION_ERROR`](/errors/validation-error). If the address belongs to a contact, that contact shows `opted_out: true`. Opting out an address that is already opted out succeeds again.

## Handle RECIPIENT\_SUPPRESSED

A send to someone who has opted out returns `403` [`RECIPIENT_SUPPRESSED`](/errors/recipient-suppressed) before anything is queued:

* `POST /v1/chats` checks every recipient in `to`.
* `POST /v1/chats/{chat_id}/messages` checks the person in a direct chat, and everyone currently in a group chat. One opted-out participant blocks sends to the whole group.

Don't retry these sends. Remove the person from your audience, or for a group, [remove them from the group](/messaging/group-chats.md#add-or-remove-a-participant) or start a new group without them.

Workflows respect opt-outs too. Enrolling an opted-out contact doesn't start a run; the response has `no_op_reason: "contact_opted_out"`. See [Workflows](/workflows.md).

## Check opt-out status

Every contact you read includes `opted_out` and `messaging_allowed`. Check them with `GET /v1/contacts/{contact_id}`, or look a person up by phone number or email with `GET /v1/contacts/search`, before you send. See [Contacts](/contacts.md#fields-to-check-before-you-send).

There is no webhook for opt-outs yet, so an integration that mirrors opt-outs into its own system has to poll. Read the contacts you care about and watch `opted_out`; `opted_out_source: "inbound_stop"` tells you the person texted STOP.

## Remove an opt-out

`POST /v1/contacts/{contact_id}/unsuppress` removes the contact's opt-out and returns the contact with `opted_out: false`. Only do this when the person has agreed to hear from you again.

```bash curl theme={null}
curl -X POST https://api.blujay.io/v1/contacts/contact_abc123/unsuppress \
  -H "Authorization: Bearer blu_your_api_key"
```

The API removes opt-outs only through a contact. To lift an opt-out on an address that has no contact, create the contact first, then call `unsuppress`.

## Keep opt-outs when contacts are deleted

Opt-outs belong to the phone number or email address, not to the contact record. When you delete an opted-out contact with `DELETE /v1/contacts/{contact_id}`, the opt-out stays. If you later create a contact with the same phone number, it starts out with `opted_out: true`, and sends to that number are still rejected.

## Limitations

* No webhook for opt-outs. `contact.suppressed` and `contact.unsuppressed` exist in the schema but return `400` [`FEATURE_NOT_ENABLED`](/errors/feature-not-enabled) on subscribe. Poll `opted_out`.
* No endpoint lists opt-outs, and `GET /v1/contacts` can't filter by `opted_out`. Keep your own list from the responses you get, or read contacts one by one.
* Automatic STOP handling applies to Dedicated numbers and to the exact text `STOP` only. Free Plan and AI Agent numbers, and any other wording, need you to opt the person out.
* An opt-out on an address with no contact can only be lifted by creating the contact first.
* An opt-out blocks the whole group: `403` [`RECIPIENT_SUPPRESSED`](/errors/recipient-suppressed) on every send to a group that contains an opted-out participant.

## Next steps

> **[Contacts](/contacts.md)**
>
> Create, find and update the people you message.

> **[Sending rules](/messaging/sending-rules.md)**
>
> Who you can message on each plan.

> **[Group chats](/messaging/group-chats.md)**
>
> Manage who is in a group before you send to it.

> **[Errors](/errors.md)**
>
> See every error code and how to fix it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.