# Authentication

> Authenticate BloomText API requests with organization-scoped API keys, scopes, and revocation.

Source: https://www.bloomtext.com/developers/api/authentication/

Every request is authenticated with an API key sent as a Bearer token:

```http filename="Header"
Authorization: Bearer bt_live_4f7c2a9e1b...
```

Requests without a valid key return `401 Unauthorized`. All requests must use HTTPS.

## Getting a key

API keys are issued to organizations with a signed BAA. [Request API access](https://calendly.com/tyler-bloom/bloomtext-homepage-demo-request?utm_campaign=api-access) to get started.

### Your organization is enabled

After the BAA is signed, we turn on API access for your organization.

### An admin creates an app user

The app user represents your integration in conversations. Give it a clear name, like "Intake Bot".

### The admin issues a key with scopes

Pick only the [scopes](#scopes) your integration needs. The key is shown once, so store it in your secrets manager straight away.

> **Warning:** Keep keys server-side. Never put a key in a browser, a mobile app, a public repository, or an AI prompt that leaves your infrastructure.

## Scopes

Each key carries the scopes an admin grants it. A request outside the key's scopes returns `403` with `code: insufficient_scope`. Every endpoint in the [API reference](https://www.bloomtext.com/developers/api/reference/) lists the scope it needs.

| Scope | Allows | Endpoints |
| --- | --- | --- |
| `organization:read` | Read the organization | [Retrieve the organization](https://www.bloomtext.com/developers/api/reference/get-organization/) |
| `users:read` | List and read members | [List users](https://www.bloomtext.com/developers/api/reference/list-users/), [Retrieve a user](https://www.bloomtext.com/developers/api/reference/get-user/) |
| `conversations:read` | List and read conversations | [List](https://www.bloomtext.com/developers/api/reference/list-conversations/), [Retrieve](https://www.bloomtext.com/developers/api/reference/get-conversation/) |
| `messages:read` | Read messages and replies | [List messages](https://www.bloomtext.com/developers/api/reference/list-messages/), [List replies](https://www.bloomtext.com/developers/api/reference/list-replies/) |
| `messages:write` | Send messages and replies | [Send a message](https://www.bloomtext.com/developers/api/reference/create-message/) |
| `reactions:read` | Read reactions | [List reactions](https://www.bloomtext.com/developers/api/reference/list-reactions/) |
| `reactions:write` | Add and remove reactions | [Add](https://www.bloomtext.com/developers/api/reference/create-reaction/), [Remove](https://www.bloomtext.com/developers/api/reference/delete-reaction/) |
| `participants:read` | List participants | [List participants](https://www.bloomtext.com/developers/api/reference/list-participants/) |
| `participants:write` | Add and remove participants | [Add](https://www.bloomtext.com/developers/api/reference/add-participant/), [Remove](https://www.bloomtext.com/developers/api/reference/remove-participant/) |
| `broadcasts:read` | Read broadcasts | [List](https://www.bloomtext.com/developers/api/reference/list-broadcasts/), [Retrieve](https://www.bloomtext.com/developers/api/reference/get-broadcast/), [Messages](https://www.bloomtext.com/developers/api/reference/list-broadcast-messages/) |
| `exports:read` | Check and download exports | [Retrieve an export](https://www.bloomtext.com/developers/api/reference/get-chat-export/) |
| `exports:write` | Start exports | [Export a conversation](https://www.bloomtext.com/developers/api/reference/create-chat-export/) |

### Common scope sets

Posts reminders and nothing else.

```text
conversations:read  messages:write
```

Reads incoming messages, replies, and reacts to acknowledge.

```text
conversations:read  messages:read  messages:write  reactions:write
```

Exports conversation history on a schedule.

```text
conversations:read  exports:write  exports:read
```

## Keys for AI agents

An AI agent acts on its own judgment, so give it less access than a script you wrote line by line.

### Give the agent its own app user

Never share a key between an agent and another integration. A dedicated app user, named so staff recognize it (like "Scheduling Assistant"), lets you audit and revoke the agent on its own.

### Start read-only

Begin with `conversations:read` and `messages:read`. Have the agent post drafts to a staff group for review, and add `messages:write` for patient conversations only once you trust its output.

### Limit where it can go

Add the app user only to the conversations the agent needs. Membership is the boundary: an agent can't read or post anywhere it hasn't been added.

### Keep the key out of the model

Store the key in your agent's secret store or environment, like `BLOOMTEXT_API_KEY`. Never put it in a prompt, a tool description, or chat history.

> **Warning:** The model provider behind your agent sees whatever the agent reads. If the agent can read patient conversations, the provider needs a BAA with your organization.

See [Build an AI agent](https://www.bloomtext.com/developers/api/ai-agents/) and the [MCP server](https://www.bloomtext.com/developers/api/mcp/) for the full setup.

## Conversation access

Scopes decide *what* a key can do. [Conversation membership](https://www.bloomtext.com/developers/api/concepts/#conversations-and-membership) decides *where*. A key can read or write a conversation only while its app user is a participant, and a request for any other conversation returns `403` with `code: conversation_membership_required`.

Adding a participant with `participants:write` is limited to existing organization members. It never widens what your app user can see.

## Rotating and revoking keys

- **Rotate** by issuing a second key for the same app user, deploying it, then revoking the old one. Both work during the overlap.
- **Revoke** from BloomText at any time. Revocation takes effect on the next request.
- **Remove** the app user from a conversation to cut off just that conversation.

## Verify your key

A quick way to check a key works and see which organization it belongs to:

```bash
curl https://api.bloomtext.com/v1/organization \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY"
```

```js
const response = await fetch('https://api.bloomtext.com/v1/organization', {
  headers: { Authorization: `Bearer ${process.env.BLOOMTEXT_API_KEY}` },
})
console.log(response.status, await response.json())
```

```python
import os

import requests

response = requests.get(
    "https://api.bloomtext.com/v1/organization",
    headers={"Authorization": f"Bearer {os.environ['BLOOMTEXT_API_KEY']}"},
)
print(response.status_code, response.json())
```

```go
req, _ := http.NewRequest("GET", "https://api.bloomtext.com/v1/organization", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("BLOOMTEXT_API_KEY"))
res, err := http.DefaultClient.Do(req)
```

```ruby
uri = URI("https://api.bloomtext.com/v1/organization")
request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer #{ENV.fetch("BLOOMTEXT_API_KEY")}"
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
```

```json filename="Response · 200 OK"
{
  "id": "6db1e3f5-9b7f-4f2b-8be1-0f1e1d7d7d8c",
  "name": "Riverside Family Clinic"
}
```
