# Create a board

> Create an empty private board on a personal or team account.

Canonical page: https://www.rundiffusion.com/docs/api/boards/create
Endpoint: POST /api/v2/boards
Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

---

A new board is empty and `PRIVATE`. That is deliberate rather than a default you
should override at creation time: a board is never born visible to a team, and
widening it is a separate, explicit call to
[Set board access settings](/docs/api/boards/access). The account you create it
on comes from the account selection, so a team board and a personal one differ
only in whether `team_id` is sent.

`POST /api/v2/boards`

Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

## Request

**Headers**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | string | Yes | Bearer token. This endpoint accepts an OAuth device flow token, a Personal API Access Token, or a Company API Access Token. See [Authentication](/docs/api/authentication). |

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | Yes | Board name, up to 100 characters. Cannot be blank. |
| `description` | string | No | Optional description, up to 1000 characters. |

cURL:

```bash
curl -X POST https://api2.rundiffusion.com/api/v2/boards \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Hero shots"}'
```

JavaScript:

```javascript
const board = await fetch('https://api2.rundiffusion.com/api/v2/boards', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ title: 'Hero shots' }),
}).then(r => r.json());
```

Python:

```python
board = requests.post(
    f"{BASE}/boards",
    headers=HEADERS,
    json={"title": "Hero shots"},
).json()
```

## Response

`201 Created` with the board in the same shape
[Get a board](/docs/api/boards/get) returns, so `nodes` and `members` are present
and empty. A picture is assigned for you: every board in the product has one, and
a board without would show as a blank tile beside the rest. It is not settable
through the API; change it in the app.

```json
{
  "id": "Tq8vNc…",
  "created_at": "2026-08-01T09:14:00+00:00",
  "title": "Hero shots",
  "description": null,
  "access_level": "PRIVATE",
  "owner_user_id": "k3PqV9…",
  "team_id": null,
  "avatar_url": "https://rundiffusion.com/...",
  "nodes": [],
  "members": []
}
```

Keep the `id`. It is the `board_id` every other board call takes, and the
`board_id` filter on [Library](/docs/api/library/list).

From here, [Add a board node](/docs/api/boards/nodes-create) puts nodes on it,
and [Set board access settings](/docs/api/boards/access) shares it with a team.

## Errors

**Status codes**

| Name | Type | Description |
| --- | --- | --- |
| `400` | INVALID_REQUEST | A blank title, or a field over its length limit. |
| `401` | TOKEN_INVALID | Missing or invalid credential. |
| `403` | ACCOUNT_HEADER_INVALID | The selected account is not one this credential may act on. |
| `429` | RATE_LIMITED | Too many requests. Board writes have a tighter budget than reads. See [Rate limits](/docs/api/rate-limits#boards). |
