# File Assets API

> Upload, list, read and delete images in a channel's file library through the Broadcast API, and use their public URLs in emails and as the brand kit logo.

Source: https://sendbroadcast.net/docs/api-file-assets

Note

**Requires Broadcast 2.48.0 or later.**

The File Assets API manages the channel's file library, the same files you see under **File Assets** in Broadcast. Each file has a public URL that never expires. Use the URL in emails and templates, or send it as the logo in the [Brand Kit API](https://sendbroadcast.net/docs/api-channel-design).

The file library holds images: PNG, JPEG and GIF files under 10 MB.

## Required Permissions

- **File Assets read**: list files and get a file
- **File Assets write**: upload and delete files

File Assets read and write are off on every token until you tick them, including tokens created before 2.48.0. Write does not include read: if an integration uploads and then lists files, tick both. Set the permissions under **Access Tokens** (and **Admin API Tokens**). See [API Authentication](https://sendbroadcast.net/docs/api-authentication).

A token without the permission gets `401 Unauthorized`, the same as the other API endpoints:

```json
{ "error": "Unauthorized" }
```

An admin API token must name the channel with `broadcast_channel_id`: as a query parameter for `GET` and `DELETE`, and in the body (or as a form field) for `POST`.

## File Object

```json
{
  "id": 42,
  "filename": "logo.png",
  "content_type": "image/png",
  "byte_size": 18734,
  "url": "https://your-broadcast-domain.com/files/eyJfcmFpbHMi...--3f2a...-logo.png",
  "created_at": "2026-10-14T09:30:00Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer | The file's ID. |
| `filename` | string | The file name, with its extension. |
| `content_type` | string | `image/png`, `image/jpeg` or `image/gif`. |
| `byte_size` | integer | The size of the file in bytes. |
| `url` | string | The public URL of the file. It never expires and needs no token. |
| `created_at` | string | When the file was uploaded (ISO 8601). |

## Which Files a Channel Sees

A channel sees the files linked to it, and the files linked to no channel. This is the same rule as the **File Assets** page. A file of another channel is a `404`.

When you upload a file, Broadcast links it to every channel, as the **File Assets** page does. Send `channel_restricted: true` to link it to the token's channel only.

## List Files

`GET /api/v1/file_assets`

```bash
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "https://your-broadcast-domain.com/api/v1/file_assets?limit=20"
```

| Parameter | Description |
|-----------|-------------|
| `filter` | Finds files by file name. The closest matches come first. |
| `limit` | The number of files to return, from 1 to 1000. Another value is a `422`. |
| `offset` | The number of files to skip, 0 or more. Another value is a `422`. |

Without `limit`, the response holds every matching file. Without `filter`, the newest files come first.

```json
{
  "data": [
    {
      "id": 42,
      "filename": "logo.png",
      "content_type": "image/png",
      "byte_size": 18734,
      "url": "https://your-broadcast-domain.com/files/eyJfcmFpbHMi...--3f2a...-logo.png",
      "created_at": "2026-10-14T09:30:00Z"
    }
  ],
  "total": 1
}
```

`total` counts every file that matches, before `limit` and `offset`.

## Get a File

`GET /api/v1/file_assets/:id`

```bash
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-broadcast-domain.com/api/v1/file_assets/42
```

Returns `200` with the file object. A file that does not exist, or that the channel cannot see, returns `404`:

```json
{ "error": "File asset not found" }
```

## Upload a File

`POST /api/v1/file_assets`

Send the file in one of two ways. Both return `201` with the file object.

### As a multipart upload

Send `multipart/form-data` with the file in a `file` part. The file name and the content type come from the part.

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -F "file=@logo.png;type=image/png" \
  https://your-broadcast-domain.com/api/v1/file_assets
```

### As base64 in a JSON body

Send the bytes base64-encoded in `data`. Use this form when your client cannot send multipart, for example an AI agent through the MCP server.

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  --data-raw "{
    \"filename\": \"logo.png\",
    \"content_type\": \"image/png\",
    \"data\": \"$(base64 < logo.png | tr -d '\n')\"
  }" \
  https://your-broadcast-domain.com/api/v1/file_assets
```

| Field | Required | Description |
|-------|----------|-------------|
| `filename` | Yes | The file name, with its extension, such as `logo.png`. Broadcast keeps only the last part of a path, so `images/logo.png` is stored as `logo.png`. |
| `data` | Yes | The file, base64-encoded (standard alphabet, with padding). Broadcast ignores line breaks and spaces. |
| `content_type` | No | `image/png`, `image/jpeg` or `image/gif`. When you leave it out, Broadcast finds the type from the bytes and the file name. |
| `channel_restricted` | No | `true` links the file to the token's channel only. The default is `false`. Also accepted as a multipart form field. Any value other than `true` or `false` (or `"true"`, `"false"`, `1`, `0`) is a `422`. |

Broadcast never downloads a file from a URL. Upload the bytes.

### Safe retries

Send an `Idempotency-Key` header to make a retry safe. Broadcast keeps the first response for 24 hours and returns it again for the same key, with the `Idempotency-Replayed: true` header, so the file is stored only once. The same key with a different body is a `422`. See [Transactional Email API](https://sendbroadcast.net/docs/api-transactional-email) for the full rules.

### Errors

A `422` stores nothing:

- The data is not valid base64.
- The file is 10 MB or larger.
- The file is not a PNG, JPEG or GIF image.
- The body has neither a `file` part nor `data`, or `data` has no `filename`.
- `channel_restricted` is not `true` or `false`.

```json
{
  "error": "Unprocessable Content",
  "message": "data is not valid base64"
}
```

```json
{
  "error": "Stored file has an invalid content type (authorized content types are PNG, JPG, GIF)"
}
```

A key that the endpoint does not know is ignored with an `unrecognized_parameter` warning (see [API Response Warnings](https://sendbroadcast.net/docs/api-response-warnings)).

## Delete a File

`DELETE /api/v1/file_assets/:id`

```bash
curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  https://your-broadcast-domain.com/api/v1/file_assets/42
```

```json
{ "message": "File asset deleted successfully" }
```

Warning

**Emails that were already sent show the file from its URL.** After you delete the file, those emails show a broken image.

You cannot delete a file that is a channel's brand kit logo. Broadcast returns a `422`:

```json
{
  "error": "This file is the channel's Brand Kit logo. Choose another logo (or none) in Settings > Brand Kit or with PATCH /api/v1/channel/design, then delete the file."
}
```

When the file is the logo of another channel, the message is different:

```json
{
  "error": "This file is the Brand Kit logo of another channel. Choose another logo for that channel, then delete the file."
}
```

Set another logo (or `null`) with the [Brand Kit API](https://sendbroadcast.net/docs/api-channel-design) first.

## Set the Brand Kit Logo

1. Upload the image with `POST /api/v1/file_assets`.
2. Send the `url` from the response as `channel_design.brand.logo_url` to `PATCH /api/v1/channel/design`.

```json
{ "channel_design": { "brand": { "logo_url": "https://your-broadcast-domain.com/files/eyJfcmFpbHMi...--3f2a...-logo.png" } } }
```

The logo must be linked to the channel. An upload without `channel_restricted` is linked to every channel, so any channel can use it as the logo.
