# Add a board node

> Place a tool on a board as a new node, at the end or at a chosen position.

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

---

A node is one tool placed on a board. The same tool can appear on a board more
than once, which is why a node has its own ID and its own name.

`POST /api/v2/boards/{board_id}/nodes`

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

The node lands at the end of the board unless you send a `position`. Its name
starts as the tool's name; rename it afterwards with
[Edit a board node](/docs/api/boards/nodes-update) if the board holds the same
tool twice.

Adding a node needs edit access to the board, described under
[Who can edit a board](/docs/api/boards/update#who-can-edit-a-board).

> **Boards hold a limited number of nodes**
>
> A board is capped by the plan it belongs to. Once it is full, adding answers
> `403 PLAN_LIMIT_REACHED` and the `details` object carries `limit` and
> `current`, so you can tell a full board from a permission problem without
> reading the message.
>
> Only adding is capped. A board that is already over its limit, which happens
> when a plan changes, still reads, reorders, and deletes normally.

## 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 add to. |

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `tool_id` | string | Yes | The tool to add. Get one from [List tools](/docs/api/tools/list). |
| `position` | object | No | Where to put the node. Omit it to add to the end. See [Position object](#position-object) below. |

### Position object

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `list_view_index` | integer | No | The index the new node takes, starting at 0. The node already at that index, and every node after it, gains one: a node at 5 becomes 6. An index at or past the end of the board lands at the end, so it is never an error to aim too high. |

Every way of saying nothing lands the node at the end: no `position` at all, an
empty `position`, or a `list_view_index` of `null`.

cURL:

```bash
curl -X POST https://api2.rundiffusion.com/api/v2/boards/$BOARD_ID/nodes \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tool_id": "Tl9xBv…", "position": {"list_view_index": 0}}'
```

JavaScript:

```javascript
// Put the node first on the board instead of last.
const node = await fetch(
  `https://api2.rundiffusion.com/api/v2/boards/${boardId}/nodes`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      tool_id: toolId,
      position: { list_view_index: 0 },
    }),
  },
).then(r => r.json());

console.log(node.id, node.position.list_view_index);
```

Python:

```python
# Put the node first on the board instead of last.
node = requests.post(
    f"{BASE}/boards/{board_id}/nodes",
    headers=HEADERS,
    json={"tool_id": tool_id, "position": {"list_view_index": 0}},
).json()

print(node["id"], node["position"]["list_view_index"])
```

## Response

`201 Created` with the [Node object](/docs/api/boards/get#node-object). Read its
`position.list_view_index` rather than assuming: an index past the end lands at
the end, so it is not always the one you sent.

```json
{
  "id": "Nd4kWs…",
  "created_at": "2026-08-01T09:30:00+00:00",
  "title": "Flux Dev",
  "description": "Text to image",
  "tool_id": "Tl9xBv…",
  "position": { "list_view_index": 0 }
}
```

An insert renumbers the rest of the board in the same write, so no reader sees a
half-applied order, and the other nodes keep their relative order.

An index past the end adds to the end rather than failing. A board can gain a
node between your read and your write, and putting the node last is still what
you asked for.

To move nodes already on a board, send the whole order to
[Reorder board nodes](/docs/api/boards/nodes-reorder).

> **Not every tool can be added**
>
> A tool that is not runnable has no complete definition to place on a board, and
> adding it would produce a node that fails the first time somebody used it. Those
> answer `404 TOOL_NOT_FOUND`, the same as an ID that does not exist. If a tool
> appears in [List tools](/docs/api/tools/list) but cannot be added, that is worth
> reporting to [our support team](https://www.rundiffusion.com/contact).

## Errors

**Status codes**

| Name | Type | Description |
| --- | --- | --- |
| `400` | INVALID_REQUEST | A missing or blank tool_id, or a negative position.list_view_index. An index past the end is not an error: it adds to the end. |
| `401` | TOKEN_INVALID | Missing or invalid credential. |
| `403` | PERMISSION_DENIED | You can see the board but may not change what is on it. |
| `403` | PLAN_LIMIT_REACHED | The board already holds as many nodes as the plan allows. details carries limit and current. Remove a node to add more. |
| `404` | BOARD_NOT_FOUND | No such board, or not one this credential can see. |
| `404` | TOOL_NOT_FOUND | The tool_id does not exist, or is not one that can be placed on a board. |
| `429` | RATE_LIMITED | Too many requests. Board writes have a tighter budget than reads. See [Rate limits](/docs/api/rate-limits#boards). |
