# Get a board

> Read one board with its nodes in display order, and its members.

Canonical page: https://www.rundiffusion.com/docs/api/boards/get
Endpoint: GET /api/v2/boards/{board_id}
Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

---

One board, with the two things a listing leaves out: the nodes on it, in the
order they appear, and who it is shared with.

`GET /api/v2/boards/{board_id}`

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). |

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `board_id` | string | Yes | The board to read, from [List boards](/docs/api/boards/list) or from a [Library](/docs/api/library/list) item's board_id. |

cURL:

```bash
curl https://api2.rundiffusion.com/api/v2/boards/$BOARD_ID \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN"
```

JavaScript:

```javascript
const board = await fetch(
  `https://api2.rundiffusion.com/api/v2/boards/${boardId}`,
  {
    headers: {
      Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}`,
    },
  },
).then(r => r.json());

for (const node of board.nodes) {
  console.log(node.position.list_view_index, node.title, node.tool_id);
}
```

Python:

```python
board = requests.get(
    f"{BASE}/boards/{board_id}",
    headers=HEADERS,
).json()

for node in board["nodes"]:
    print(node["position"]["list_view_index"], node["title"], node["tool_id"])
```

## Response

`200 OK` with the board, its nodes, and its members.

```json
{
  "id": "Tq8vNc…",
  "created_at": "2026-07-30T14:02:11+00:00",
  "title": "Hero shots",
  "description": "Landing page art",
  "access_level": "RESTRICTED",
  "owner_user_id": "k3PqV9…",
  "team_id": "Kp7mZq…",
  "avatar_url": "https://rundiffusion.com/...",
  "nodes": [
    {
      "id": "Nd4kWs…",
      "created_at": "2026-07-30T14:03:02+00:00",
      "title": "Flux Dev",
      "description": "Text to image",
      "tool_id": "Tl9xBv…",
      "position": { "list_view_index": 0 }
    },
    {
      "id": "Nd7mQp…",
      "created_at": "2026-07-30T14:05:44+00:00",
      "title": "Upscale",
      "description": null,
      "tool_id": "Tl2cRn…",
      "position": { "list_view_index": 1 }
    }
  ],
  "members": [
    {
      "user_id": "k3PqV9…",
      "email": "sam@example.com",
      "created_at": "2026-07-30T14:02:11+00:00"
    }
  ]
}
```

### Board object

The board's own fields are the same ones
[List boards](/docs/api/boards/list#board-object) returns, plus two arrays this
endpoint adds:

| Name | Type | Description |
| --- | --- | --- |
| `nodes` | array | The nodes on the board, already in display order. Empty on a new board. See [Node object](#node-object) below. |
| `members` | array | Who the board is shared with. Empty at PRIVATE and SHARED, where membership is not what decides access. See [Board member object](#board-member-object) below. |

### Node object

| Name | Type | Description |
| --- | --- | --- |
| `id` | string | Node ID, unique within the board. Send it as node_id on the node endpoints. Example: Nd4kWs…. |
| `created` | string (ISO 8601) | When it was created, in UTC. |
| `title` | string \| null | The node's name on this board. Starts as the tool's name and can be renamed per board, so two nodes running the same tool can be told apart. |
| `description` | string \| null | Optional description, null when unset. |
| `tool_id` | string | The tool this node runs. Pass it to [Get a tool](/docs/api/tools/get) for the field schema, and to [Generate](/docs/api/generate/create) to run it. Example: Tl9xBv…. |
| `position` | object | Where the node sits on the board. See [Position object](#position-object) below. |

### Position object

| Name | Type | Description |
| --- | --- | --- |
| `list_view_index` | integer | The node's place in the board's list, starting at 0 and counting up by 1 with no gaps. It always matches the index of the node in the nodes array, so you can rely on either. |

> **list_view_index is a position, not a stored value**
>
> It is derived from the board's ordering every time you read it, so it is always
> dense and always starts at 0, whatever the board's history. Removing a node
> closes the gap immediately: delete the node at index 0 of three and the
> remaining two read 0 and 1 on the next fetch.
>
> That also means an index is only meaningful for the board as you just read it.
> To move a node, send the whole order to
> [Reorder nodes](/docs/api/boards/nodes-reorder) rather than assuming an index
> is still current.

### Board member object

| Name | Type | Description |
| --- | --- | --- |
| `user_id` | string | The member's user ID. Matches uid from [Identity](/docs/api/me). |
| `email` | string \| null | The board member's email. Null when this user is no longer on the team the board belongs to. |
| `created` | string (ISO 8601) | When it was created, in UTC. |

## Errors

**Status codes**

| Name | Type | Description |
| --- | --- | --- |
| `401` | TOKEN_INVALID | Missing or invalid credential. |
| `404` | BOARD_NOT_FOUND | No such board, or not one this credential can see. A board you lack access to is reported the same way as one that does not exist, deliberately: a 403 would confirm it exists, which is what PRIVATE and RESTRICTED are for. |
| `429` | RATE_LIMITED | Too many requests. See [Rate limits](/docs/api/rate-limits#boards). |
