# Users API

> Create and manage the people who sign in to Broadcast with the API. List users, set passwords or send reset emails, deactivate or delete accounts, and grant channel and system permissions.

Source: https://sendbroadcast.net/docs/api-users

The Users API manages the people who sign in to Broadcast: the same accounts you
see under **User Management**. Use it to provision a team when you migrate from
another platform, to keep Broadcast in step with your own staff directory, or to
remove access when someone leaves.

It covers three things:

- **Users** at `/api/v1/users`: create, update, deactivate, activate and delete
- **Channel permissions** at `/api/v1/users/{id}/channel_permissions`: what a
  user can do on each channel, set with individual flags, a built-in role or a
  saved preset
- **System permissions** at `/api/v1/users/{id}/system_permissions`:
  installation-wide abilities such as user management or system settings

For what each role and permission means, see [Users & Permissions](https://sendbroadcast.net/docs/users-and-permissions).

## Authentication and permissions

Every Users API endpoint requires an **admin API token**. A channel token gets
`403 Forbidden`, because a user can sign in to the whole installation, not just
one channel.

Warning

**Admin tokens only.** A channel token is rejected with:

```
{ "error": "Admin API token required for user management" }
```

The admin token also needs the **Users** permission:

- **Read** for `GET` endpoints
- **Write** for everything else

A token without the right permission gets `401 Unauthorized`. Tick the Users
row when you create or edit the token under **Application → Admin API Tokens**.
See [API Authentication](https://sendbroadcast.net/docs/api-authentication).

Unlike other admin token requests, these endpoints do **not** need
`broadcast_channel_id`. If your SDK client sends one because it is configured
with a default channel, it is ignored.

## Sudo users are read-only

Sudo access can never be granted or removed through the API, and users who
already have sudo access are read-only here. Updating, deactivating, activating
or deleting a sudo user, or changing their permissions, returns:

```json
{ "error": "Sudo users cannot be changed through the API" }
```

That is a `403 Forbidden`. The rule includes users whose sudo access comes from
their system permissions. Changing a sudo user's email address or password would
hand over the most powerful account on the installation, so those changes are
made in the web interface by another sudo user.

Note

You can still **read** sudo users. The `sudo` field on every user tells you whether they have sudo access, however it was granted.

## User Object

| Field | Description |
|-------|-------------|
| `id` | Unique identifier. Use it in every `/api/v1/users/{id}` path. |
| `email` | Sign-in email address |
| `first_name` | First name |
| `last_name` | Last name |
| `active` | `false` when the user has been deactivated and cannot sign in |
| `locked` | `true` when the account is locked after too many failed sign-ins |
| `sudo` | Whether the user has sudo access |
| `two_factor_enabled` | Whether two-factor authentication is on |
| `last_login_at` | Last successful sign-in, or `null` |
| `created_at` | When the user was created |
| `updated_at` | When the user was last changed |

Single-user responses (get, create, update, deactivate and activate) also
include `system_permissions` and `channel_permissions`, described below.

## List Users

```
GET /api/v1/users
```

### Query Parameters

- `q` (optional): Matches email address, first name or last name
- `status` (optional): `active` or `inactive`. Any other value returns `422`.
- `limit` (optional): Maximum number of users to return, from 1 to 1000
- `offset` (optional): Number of users to skip, 0 or more

A `limit` or `offset` outside those ranges, or that is not a whole number, returns `422 Unprocessable Entity`.

### Request

```bash
curl -X GET "https://your-broadcast-domain.com/api/v1/users?status=active&limit=25" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

### Response

```json
{
  "data": [
    {
      "id": 7,
      "email": "daniel.meyer@example.com",
      "first_name": "Daniel",
      "last_name": "Meyer",
      "active": true,
      "locked": false,
      "sudo": false,
      "two_factor_enabled": false,
      "last_login_at": "2026-09-15T08:12:44Z",
      "created_at": "2026-09-01T10:00:00Z",
      "updated_at": "2026-09-15T08:12:44Z"
    }
  ],
  "total": 1
}
```

`total` counts every user matching the filters, not just the ones on this page.

## Get User

```
GET /api/v1/users/{id}
```

### Request

```bash
curl -X GET "https://your-broadcast-domain.com/api/v1/users/7" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

### Response

```json
{
  "id": 7,
  "email": "daniel.meyer@example.com",
  "first_name": "Daniel",
  "last_name": "Meyer",
  "active": true,
  "locked": false,
  "sudo": false,
  "two_factor_enabled": false,
  "last_login_at": "2026-09-15T08:12:44Z",
  "created_at": "2026-09-01T10:00:00Z",
  "updated_at": "2026-09-15T08:12:44Z",
  "system_permissions": {
    "user_management": false,
    "channel_management": false,
    "system_settings": false,
    "system_monitoring": true,
    "system_backups": false,
    "system_updates": false,
    "billing_management": false
  },
  "channel_permissions": [
    {
      "broadcast_channel_id": 3,
      "broadcast_channel_name": "Product Updates",
      "role": "Editor",
      "permissions": {
        "subscribers_read": true,
        "subscribers_write": true,
        "broadcasts_send": false
      },
      "created_at": "2026-09-01T10:05:00Z",
      "updated_at": "2026-09-01T10:05:00Z"
    }
  ]
}
```

The `permissions` object lists every channel permission flag. It is shortened
here.

## Create User

```
POST /api/v1/users
```

A new user needs a way to sign in. Either set a password, or set
`send_password_reset` to `true` and Broadcast emails them a link to choose their
own. One of the two is required.

### Parameters

- `user[email]` (required): Sign-in email address
- `user[first_name]` (required)
- `user[last_name]` (required)
- `user[password]` (optional): The user's password
- `user[send_password_reset]` (optional): `true` to email a password-reset link
  instead of setting a password

### Request

```bash
curl -X POST "https://your-broadcast-domain.com/api/v1/users" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user": {
      "email": "daniel.meyer@example.com",
      "first_name": "Daniel",
      "last_name": "Meyer",
      "send_password_reset": true
    }
  }'
```

### Response

`201 Created` with the full user, including empty `channel_permissions`. A new
user can't see any channel until you grant access, so follow up with
[Set Channel Permissions](#set-channel-permissions) or
[Bulk Apply Channel Permissions](#bulk-apply-channel-permissions).

Note

The password-reset link expires after two hours. If it lapses, the user can request a new one from the sign-in page.

A missing password and reset flag, or an email address already in use, returns
`422`:

```json
{ "error": "Email has already been taken" }
```

## Requests that are rejected, not ignored

Create and update refuse keys that ask for something these endpoints will not
do. The request fails with `422`, **nothing in it is applied**, and the message
tells you where to go instead:

| Key in `user` | Why it is rejected |
|---------------|--------------------|
| `sudo`, `sudo_access` | Sudo access can't be granted through the API |
| `active`, `is_active` | Use [Deactivate User](#deactivate-user) or [Activate User](#activate-user) |
| `system_permissions` | Use [Update System Permissions](#update-system-permissions) |
| `channel_permissions`, `role`, `preset_id`, `permissions` | Use [Set Channel Permissions](#set-channel-permissions) or [Bulk Apply Channel Permissions](#bulk-apply-channel-permissions) |
| `send_password_reset` (update only) | A reset email can only be sent when the user is created |

```json
{
  "error": "Unprocessable Content",
  "message": "active cannot be set on update. Use POST /api/v1/users/7/deactivate or POST /api/v1/users/7/activate."
}
```

Warning

**Why an error and not a warning?** Other endpoints accept unknown keys and report them in a `warnings` array (see [API Response Warnings](https://sendbroadcast.net/docs/api-response-warnings)). The SDKs log warnings by default, and without a logger nobody sees them. For a request like “make this user sudo” or “deactivate this user”, a warning would look like success, so these keys fail the whole request instead. Any other unrecognized key still produces a warning.

## Update User

```
PATCH /api/v1/users/{id}
```

Send only the fields you want to change: `email`, `first_name`, `last_name` or
`password`, wrapped in `user`.

### Request

```bash
curl -X PATCH "https://your-broadcast-domain.com/api/v1/users/7" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "user": { "last_name": "Meyer-Schulz" } }'
```

### Response

The updated user, with permissions.

## Deactivate User

```
POST /api/v1/users/{id}/deactivate
```

The user can no longer sign in. Their permissions are kept, so activating them
again restores the same access.

```bash
curl -X POST "https://your-broadcast-domain.com/api/v1/users/7/deactivate" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

Returns the user with `"active": false`.

## Activate User

```
POST /api/v1/users/{id}/activate
```

Re-enables sign-in. This also clears a lockout from too many failed sign-in
attempts.

```bash
curl -X POST "https://your-broadcast-domain.com/api/v1/users/7/activate" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

Returns the user with `"active": true` and `"locked": false`.

## Delete User

```
DELETE /api/v1/users/{id}
```

Permanently deletes the user and their permissions. Prefer
[deactivating](#deactivate-user) if you might need the account again.

```bash
curl -X DELETE "https://your-broadcast-domain.com/api/v1/users/7" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

```json
{ "message": "User deleted successfully" }
```

## Channel Permission Object

| Field | Description |
|-------|-------------|
| `broadcast_channel_id` | The channel these permissions apply to |
| `broadcast_channel_name` | The channel's name |
| `role` | `Viewer`, `Editor` or `Manager` when the flags match a built-in role, the name of a saved preset when they match one, otherwise `Custom` |
| `permissions` | Every channel permission flag, `true` or `false` |
| `created_at` | When access to this channel was first granted |
| `updated_at` | When it last changed |

Flag names match the checkboxes in the web interface, without a prefix, for
example `subscribers_read`, `broadcasts_send` or `templates_write`. Read the
current list from any channel permission response.

## List Channel Permissions

```
GET /api/v1/users/{id}/channel_permissions
```

Returns one entry per channel the user can access. Channels with no entry are
channels the user can't see.

```bash
curl -X GET "https://your-broadcast-domain.com/api/v1/users/7/channel_permissions" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

```json
{
  "data": [
    {
      "broadcast_channel_id": 3,
      "broadcast_channel_name": "Product Updates",
      "role": "Viewer",
      "permissions": { "subscribers_read": true, "subscribers_write": false },
      "created_at": "2026-09-01T10:05:00Z",
      "updated_at": "2026-09-01T10:05:00Z"
    }
  ],
  "total": 1
}
```

## Set Channel Permissions

```
PUT /api/v1/users/{id}/channel_permissions/{broadcast_channel_id}
```

Grants access to a channel, or changes existing access. A request **replaces**
the user's whole permission record on that channel. Send exactly one of:

- `permissions`: an object of flag names. Flags you leave out are set to `false`.
- `role`: `Viewer`, `Editor` or `Manager`
- `preset_id`: the id of a saved permission preset

### Request with a role

```bash
curl -X PUT "https://your-broadcast-domain.com/api/v1/users/7/channel_permissions/3" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "role": "Editor" }'
```

### Request with flags

```bash
curl -X PUT "https://your-broadcast-domain.com/api/v1/users/7/channel_permissions/3" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "permissions": { "subscribers_write": true, "broadcasts_read": true } }'
```

### Response

The saved channel permission object.

Note

**The response shows what was saved, which can be more than you sent.** The same rules as the web interface apply: granting a write permission also grants the matching read permission, for example. Each flag granted this way is listed in a `parameter_overridden` warning, and the response always shows the saved flags. Use the response, not your request, as the record of what the user can do.

### Errors

- `422` when the body has none, or more than one, of `permissions`, `role` and
  `preset_id`
- `422` for an unknown role, a preset that does not exist, or an unknown flag
  name (the message names it)
- `422` with `At least one permission must be granted` when no flag ends up
  `true`. To remove access, use [Remove Channel Permissions](#remove-channel-permissions).
- `404` when the channel does not exist

## Remove Channel Permissions

```
DELETE /api/v1/users/{id}/channel_permissions/{broadcast_channel_id}
```

Removes the user's access to one channel.

```bash
curl -X DELETE "https://your-broadcast-domain.com/api/v1/users/7/channel_permissions/3" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

```json
{ "message": "Channel permissions removed successfully" }
```

Returns `404` when the user has no access to that channel.

## Bulk Apply Channel Permissions

```
POST /api/v1/users/{id}/channel_permissions/bulk
```

Applies the same access to many channels in one request. This is the quickest
way to set up a new team member. Send `broadcast_channel_ids` and exactly one of
`permissions`, `role` or `preset_id`. As with
[Set Channel Permissions](#set-channel-permissions), each channel's record is
replaced.

### Request

```bash
curl -X POST "https://your-broadcast-domain.com/api/v1/users/7/channel_permissions/bulk" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "broadcast_channel_ids": [3, 4, 99],
    "role": "Viewer"
  }'
```

### Response

```json
{
  "applied": [
    { "broadcast_channel_id": 3, "broadcast_channel_name": "Product Updates", "role": "Viewer", "permissions": { "subscribers_read": true } },
    { "broadcast_channel_id": 4, "broadcast_channel_name": "Weekly Digest", "role": "Viewer", "permissions": { "subscribers_read": true } }
  ],
  "failed": [
    { "broadcast_channel_id": 99, "error": "Broadcast channel not found" }
  ]
}
```

One bad channel does not stop the rest. Every id comes back in either `applied`
or `failed`. The status is `200` when at least one channel was applied, and
`422`, with the same `applied` and `failed` body, when none were.

## System Permissions

System permissions are installation-wide. Through the API you can read and
change these seven:

| Permission | What it allows |
|------------|----------------|
| `user_management` | Create, edit, and manage user accounts and their permissions |
| `channel_management` | Create and manage broadcast channels |
| `system_settings` | Modify system-wide settings and configuration |
| `system_monitoring` | Access monitoring and performance data |
| `system_backups` | Create and restore backups |
| `system_updates` | Install updates and manage software |
| `billing_management` | Access billing and subscription management |

Sudo access is never part of the API, and neither is admin API token management.

### Get System Permissions

```
GET /api/v1/users/{id}/system_permissions
```

```bash
curl -X GET "https://your-broadcast-domain.com/api/v1/users/7/system_permissions" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```

```json
{
  "user_management": false,
  "channel_management": false,
  "system_settings": false,
  "system_monitoring": true,
  "system_backups": false,
  "system_updates": false,
  "billing_management": false
}
```

### Update System Permissions

```
PATCH /api/v1/users/{id}/system_permissions
```

Changes only the permissions you name. Everything else stays as it is.

```bash
curl -X PATCH "https://your-broadcast-domain.com/api/v1/users/7/system_permissions" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "permissions": { "user_management": true } }'
```

Returns all seven permissions after the change.

Warning

A request that includes `sudo_access`, or any name outside the table above, is rejected with `422` and **nothing** in it is applied, including the valid permissions.

## Error Responses

| Status | Meaning |
|--------|---------|
| `401 Unauthorized` | Missing or invalid token, or the token lacks the Users read or write permission |
| `403 Forbidden` | A channel token was used, or the user has sudo access and cannot be changed |
| `404 Not Found` | The user or channel does not exist |
| `422 Unprocessable Content` | Validation failed. The body explains why. |

## Example: provisioning a team member

Create the user, let them choose a password, and give them Editor access to two
channels:

```bash
# 1. Create the user and email them a password-reset link
curl -X POST "https://your-broadcast-domain.com/api/v1/users" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "user": { "email": "sam@example.com", "first_name": "Sam", "last_name": "Lee", "send_password_reset": true } }'

# 2. Use the returned id to grant Editor on channels 3 and 4
curl -X POST "https://your-broadcast-domain.com/api/v1/users/12/channel_permissions/bulk" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "broadcast_channel_ids": [3, 4], "role": "Editor" }'
```

When they leave, deactivate them:

```bash
curl -X POST "https://your-broadcast-domain.com/api/v1/users/12/deactivate" \
  -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN"
```
