# Plan · Home & search

> Entitlements, the home screen in one request, my tasks, favorites, inbox, activity, timeline, search, organization labels and milestones. Part of the Dailybot Plan API (Beta).

Language: en
Canonical: https://www.dailybot.com/developers/api/plan-home
Markdown: send header `Accept: text/markdown` on any URL to receive Markdown instead of HTML.
Last Updated: 2026-09-25

---

> **Beta** — Plan is in beta. Everything under `/plan` in the web app, the CLI and agent skill commands for projects, goals, boards and tasks, and the `/v1/plan/` public API may change before general availability. Want to try it with your team? Write to **support@dailybot.com**.

This page is the reference for **Plan · Home & search**. Every endpoint lives under `https://api.dailybot.com/v1/plan/` and answers JSON.

Authenticate with a login session or a CLI user token (`Authorization: Bearer …`), or with an API key (`X-API-KEY`). A **personal API key** acts as its person and can do everything that person can do in Dailybot; an **agent or organization key** never acts as a person and is refused on the endpoints that need one. On an endpoint, the *API key* badge means an agent or organization key is accepted too. See [Authentication for Plan](/developers/plan/authentication), [Authentication](/developers/authentication) and [Errors](/developers/errors) for the rules shared by every Dailybot API.

New to Plan? Read the [overview](/developers/plan) for the model: projects, boards, workflow states, keys, ordering, versions and archive.

## Endpoints in this group

Entitlements, the home screen in one request, my tasks, favorites, inbox, activity, timeline, search, organization labels and milestones. Part of the Dailybot Plan API (Beta).

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/v1/plan/me/tasks/` | The calling user's tasks |
| GET | `/v1/plan/me/tasks/counts/` | Personal task tab counts |
| GET | `/v1/plan/me/recents/` | Recently visited boards for the caller |
| GET | `/v1/plan/inbox/` | Notification-worthy task events for the caller |
| POST | `/v1/plan/inbox/read-all/` | Mark all inbox items read |
| POST | `/v1/plan/inbox/{item_uuid}/read/` | Catch up to one inbox row |
| GET | `/v1/plan/inbox/unread-count/` | Unread inbox count for the caller |
| GET | `/v1/plan/activity/` | Org activity feed for Plan Home |
| GET | `/v1/plan/me/activity-cursor/` | Read the caller's activity read cursor |
| PUT | `/v1/plan/me/activity-cursor/` | Mark activity as read up to a timestamp |
| GET | `/v1/plan/labels/` | List organization labels |
| POST | `/v1/plan/labels/` | Create an organization label |
| PATCH | `/v1/plan/labels/{label_id}/` | Update an organization label |
| DELETE | `/v1/plan/labels/{label_id}/` | Delete an organization label |
| GET | `/v1/plan/entitlements/` | Whether Plan is available here, and the plan ceilings |
| GET | `/v1/plan/timeline/` | The scheduled work over a window, with its dependency edges and goal bands |
| GET | `/v1/plan/search/` | Search tasks and containers visible to the caller |
| GET | `/v1/plan/pulse/` | The home screen in one request (HomePulse), or a board-health aggregate (TaskPulse) |
| GET | `/v1/plan/me/favorites/` | Your pinned boards and saved views |
| POST | `/v1/plan/me/favorites/` | Pin a board or a saved view |
| PATCH | `/v1/plan/me/favorites/{favorite_id}/` | Move one pin within your list |
| DELETE | `/v1/plan/me/favorites/{favorite_id}/` | Unpin |

### GET `/v1/plan/me/tasks/` · Beta

**The calling user's tasks**

Your tasks, the same as `GET /v1/plan/tasks/?owner=me` plus the `scope` choice. Needs a person: an agent or organization key gets `403 insufficient_scope`, never an empty list; a personal API key works.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** Page-number pagination

#### Query parameters

##### Pagination

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `page` | integer | No | 1-based page number. |
| `page_size` | integer | No | Rows per page. Default 50, maximum 100. Out-of-range values are clamped, never rejected: asking for 500 returns 100. |

##### Filters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `scope` | string | No | Which sense of "mine": `owned` is `owner = me`; `participating` means you are on the card; `involved` is the union of owned, participating and created by you, which is what a person means by "my tasks". |
| `state` | array | No | Repeatable; values are OR-ed. Each value is **either** a workflow-state uuid **or** one of two lifecycle tokens:
- `open`  - the state categories that are not terminal: `backlog`, `todo`, `in_progress`.
- `done`  - the terminal categories: `done`, `canceled`.
The tokens are decided by `state.category` alone and never consult `completed_at`, so a client that classifies rows by the category on the state chip agrees with this filter by construction.
Mixing is allowed: a uuid and a token in the same request are OR-ed like any other repeated value. **Any other value is `400 invalid_filter_value`** with `extra.parameter: "state"` - including `overdue`, which is not a lifecycle state. Overdue is a due-date question: see `due_before`. |
| `category` | array | No | The five fixed state categories. There is no `blocked` category — blocked-ness is a relation; use `blocked=true`. |
| `priority` | array | No | 1=urgent, 2=high, 3=medium, 4=low, 5=none. Repeatable. |
| `blocked` | boolean | No | Derived from relations, not from a status. This is the query the product answers with a link rather than a state.
`blocked=true` means a **live** blocker: a `blocks` relation whose source task is neither archived nor in a terminal category. A blocker that is itself `done` or `canceled` blocks nothing and does not match.
**Orthogonal to lifecycle.** A finished task can still carry a live blocker, so `blocked=true` alone returns terminal rows too. Work a person can act on is `blocked=true&state=open` — that combination is what reproduces the `blocked` tile on `GET /v1/plan/pulse/`. |

##### Dates

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `due_before` | string | No | Inclusive. Alone, this means overdue **or** due by that date - it does not exclude work that is already finished.
**Overdue is spelled `due_before=<today>&state=open`.** That pairing is the supported spelling, it is what reproduces the `overdue` tile on `GET /v1/plan/pulse/`, and there is deliberately no `state=overdue` sugar: `state` is a lifecycle dimension and overdue is a date one, so a single spelling keeps the two from drifting. `state=overdue` answers `400 invalid_filter_value`, which is evidence about that spelling and not about the capability. |

##### Sorting & expansion

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `sort` | string | No | One field, optionally `-`-prefixed. Every ordering appends a stable internal tiebreak so a row cannot appear on two pages. An unsupported value is `400 invalid_sort`, never a silent fallback. |

##### Archived rows

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `include_archived` | boolean | No | Include archived rows alongside live ones. Distinct from `is_archived`, which selects one set or the other: `include_archived=true` is the union. Lists return live rows unless you opt in. |

#### Task object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `key` | string | Yes | Human-readable key `KEY-n`, for example `ENG-142`. Retired keys keep resolving. |
| `title` | string | Yes | The task's title. Max 255 characters. |
| `description` | string | null | No | Free-form description. |
| `board` | uuid | No | The board. |
| `state` | WorkflowState | Yes | The task's workflow state (its column). See [WorkflowState](#plan-me-tasks-list-workflowstate). |
| `priority` | integer | No | 1 urgent, 2 high, 3 medium, 4 low, 5 none. From 1 to 5. |
| `estimate` | integer | null | No | Estimate on the board's scale. |
| `owner` | UserRef | null | No | The person accountable for the task. See [UserRef](#plan-me-tasks-list-userref). |
| `executor` | ActorRef | null | No | The actor doing the work, when different from the owner (for example an agent). See [ActorRef](#plan-me-tasks-list-actorref). |
| `executors` | object[] | No | Every agent that has executed a write on this task on someone's behalf, newest first: `{uuid, name, username, avatar, first_at, last_at}`. Separate from `executor`, which stays the current ball-holder. Only on task detail and single-task write responses; omitted on list rows. |
| `participant_count` | integer | No | Number of participants. |
| `start_date` | date | null | No | Planned start date. |
| `due_date` | date | null | No | Due date. |
| `milestone` | null | {uuid, name, date} | No | The milestone this task counts toward. All fields are always present. |
| `parent_task` | null | {uuid, key, title} | No | The parent task, for a sub-task. One level of nesting only. All fields are always present. |
| `subtask_count` | integer | No | Number of sub-tasks. |
| `subtask_done_count` | integer | No | Number of finished sub-tasks. |
| `attachment_count` | integer | No | Number of attachments. |
| `open_blocker_count` | integer | No | Number of live blockers. |
| `labels` | array<Label> | No | Organization labels on the task. See [Label](#plan-me-tasks-list-label). |
| `rank` | string | null | No | Opaque order within the column. Never compute it: move with `after` / `before`. |
| `blocked` | boolean | No | Tasks with a live blocker. |
| `blocked_since` | date-time | null | No | When the task became blocked. |
| `completed_at` | date-time | null | No | When it was completed, or `null`. |
| `is_archived` | boolean | Yes | Whether the row is archived. Archive is the delete: archived rows stay readable and restorable. |
| `subscribed` | boolean | No | Whether you watch this task. |
| `version` | integer | Yes | Increments on every write. Send it back as `If-Match` to refuse a stale update. |
| `created_by` | ActorRef | null | No | Who created the row. See [ActorRef](#plan-me-tasks-list-actorref). |
| `created_at` | date-time | No | When the row was created. |
| `updated_at` | date-time | No | When the row last changed. |

#### WorkflowState object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `name` | string | Yes | Display name. Max 48 characters. |
| `category` | enum | Yes | One of the five fixed categories. It never changes after create. One of `backlog`, `todo`, `in_progress`, `done`, `canceled`. |
| `position` | integer | Yes | Column position, left to right. Minimum 0. |
| `color` | string | No | Display color (hex). |
| `is_default` | boolean | No | Whether new tasks land in this state by default. |
| `is_archived` | boolean | No | Whether the row is archived. Archive is the delete: archived rows stay readable and restorable. |
| `task_count` | integer | No | Number of live tasks. |

#### UserRef object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `name` | string | No | Display name. |
| `avatar_url` | string | null | No | — |
| `has_photo` | boolean | No | — |

#### ActorRef object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `kind` | string | Yes | — |
| `uuid` | string | Yes | Stable public identifier. |
| `name` | string | No | Display name. |
| `username` | string | null | No | — |
| `avatar_url` | string | null | No | — |
| `has_photo` | boolean | No | — |

#### Label object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `name` | string | Yes | Display name. Max 64 characters. |
| `color` | string | No | Display color (hex). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `count` | integer | Yes | Total number of rows. |
| `next` | uri | Yes | URL of the next page, or `null`. |
| `previous` | uri | Yes | URL of the previous page, or `null`. |
| `results` | array<Task> | Yes | The rows on this page. See [Task](#plan-me-tasks-list-task). |

#### Error codes

| Status | When |
|--------|------|
| `400` | An invalid filter value. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/me/tasks/?scope=involved&state=open" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks mine --scope involved --json
```

##### Response

```bash
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "00000000-0000-4000-8000-000000000005",
      "key": "ENG-142",
      "title": "Ship the delta feed",
      "description": null,
      "board": "00000000-0000-4000-8000-000000000002",
      "state": {
        "uuid": "00000000-0000-4000-8000-000000000003",
        "name": "In Progress",
        "category": "in_progress",
        "position": 2
      },
      "priority": 2,
      "estimate": 3,
      "owner": {
        "uuid": "00000000-0000-4000-8000-00000000000c",
        "name": "Ada L."
      },
      "executor": null,
      "participant_count": 1,
      "start_date": "2026-09-28",
      "due_date": "2026-10-15",
      "milestone": null,
      "parent_task": null,
      "subtask_count": 1,
      "subtask_done_count": 1,
      "attachment_count": 1,
      "open_blocker_count": 1,
      "labels": [],
      "rank": "aU",
      "blocked": false,
      "blocked_since": null,
      "completed_at": null,
      "is_archived": false,
      "subscribed": true,
      "version": 7,
      "created_by": {
        "kind": "user",
        "uuid": "00000000-0000-4000-8000-00000000000c",
        "name": "Ada L."
      },
      "created_at": "2026-09-25T10:14:02Z",
      "updated_at": "2026-09-25T10:14:02Z"
    }
  ]
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/me/tasks/counts/` · Beta

**Personal task tab counts**

Caller-scoped counts for owned, participating, involved, and overdue work.
The four top-level integers are **population totals** across every lifecycle: `owned` counts every task assigned to the caller whether it is open, done or canceled. `overdue` is the exception and is `owned`-only, already excluding archived and terminal work.
`by_scope` carries the **status-qualified** numbers, so a badge can say "N open" without a second request. `open` is decided by the state CATEGORY, exactly as `?state=open` decides it; `overdue` means open AND past due; `blocked` uses the one live-blocker predicate. Every number is computed in the same single aggregate over the same visibility root.
`by_scope.<scope>.total` equals the top-level integer of the same name by construction.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### MyTaskCounts object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `owned` | integer | Yes | Tasks you own (all lifecycles). |
| `participating` | integer | Yes | Tasks you participate in. |
| `involved` | integer | Yes | Owned, participating or created by you. |
| `overdue` | integer | Yes | Open tasks past their due date. |
| `by_scope` | object | Yes | Status-qualified counts per scope: `{total, open, overdue, blocked}`. Shape: `{owned, participating, involved} — each {total, open, overdue, blocked: integer} (all required)`. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | MyTaskCounts | Yes | A [MyTaskCounts](#plan-me-tasks-counts-mytaskcounts) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/me/tasks/counts/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks counts
```

##### Response

```bash
{
  "owned": 1,
  "participating": 1,
  "involved": 1,
  "overdue": 1,
  "by_scope": {}
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/me/recents/` · Beta

**Recently visited boards for the caller**

Boards you opened recently, newest first, as recorded by `POST /v1/plan/boards/{board_id}/visit/`. Hidden, archived and other organizations' boards are omitted rather than raised. Needs a person (an agent or organization key gets `403`; a personal API key works).

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### RecentBoardList object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `count` | integer | Yes | Total number of rows. |
| `limit` | integer | Yes | Page size applied. |
| `results` | array | Yes | The rows on this page. Items: `{board: uuid, name: string, key: string, visited_at: date-time}`. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | RecentBoardList | Yes | A [RecentBoardList](#plan-me-recents-list-recentboardlist) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/me/recents/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### Response

```bash
{
  "count": 1,
  "limit": 1,
  "results": []
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/inbox/` · Beta

**Notification-worthy task events for the caller**

Your inbox: task events worth your attention, newest first, as a page. `mentioned=true` keeps only the events where someone mentioned you; `type` keeps one event type. Both combine, and `count` and paging are exact, so there are no empty pages.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** Page-number pagination

#### Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `page` | integer | No | 1-based page number. |
| `page_size` | integer | No | Rows per page. Default 50, maximum 100. Out-of-range values are clamped, never rejected: asking for 500 returns 100. |
| `type` | string | No | Only events of this type, such as `task.owner_changed` (the Assigned tab). Combines with `mentioned` (AND). |
| `mentioned` | boolean | No | `true` keeps only the events where someone mentioned you. Must be `true` or `false`. |

#### ActivityEvent object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | string | Yes | Stable public identifier. |
| `type` | string | Yes | The event type. New types are added over time: ignore ones you do not recognise. |
| `actor` | object | Yes | Who acted. |
| `executed_by_agent` | object | null | No | The agent that executed this on behalf of the person, or `null` when no agent was named: an object with `uuid`, `name`, `username` and `avatar`. The person in the author field is still the author; the agent is shown as the one who executed it. |
| `created_at` | string | Yes | When the row was created. |
| `task` | object | No | Shape: `{uuid, key, title, board {uuid, key, name} | null} | null`. |
| `payload` | object | Yes | Ids, enum values, numbers, booleans and dates only, never user-written text. |
| `changes` | array | Yes | Resolved field changes, `[{field, from, to}]`. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `count` | integer | Yes | Total number of rows. |
| `next` | uri | Yes | URL of the next page, or `null`. |
| `previous` | uri | Yes | URL of the previous page, or `null`. |
| `results` | array<ActivityEvent> | Yes | The rows on this page. See [ActivityEvent](#plan-inbox-list-activityevent). |

#### Error codes

| Status | When |
|--------|------|
| `400` | An undeclared parameter or a bad value (`invalid_filter_value`). |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/inbox/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks inbox --json
```

##### Response

```bash
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "00000000-0000-4000-8000-00000000000f",
      "type": "task.moved",
      "actor": {},
      "created_at": "example",
      "task": {},
      "payload": {},
      "changes": []
    }
  ]
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### POST `/v1/plan/inbox/read-all/` · Beta

**Mark all inbox items read**

Marks every inbox item read by moving your read cursor to now. The response is the new `last_seen_at`.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `last_seen_at` | date-time | Yes | Everything at or before this time counts as read. |

#### Error codes

| Status | When |
|--------|------|
| `400` | The agent name is invalid (`invalid_agent_attribution`). |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS -X POST "https://api.dailybot.com/v1/plan/inbox/read-all/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks inbox-read-all
```

##### Response

```bash
{
  "last_seen_at": "2026-09-25T10:14:02Z"
}
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### POST `/v1/plan/inbox/{item_uuid}/read/` · Beta

**Catch up to one inbox row**

Marks this row **and everything older** read, and answers with the new `unread_count`.
The inbox has no per-item read state, by design: read/unread is derived from a single cursor rather than a flag per row. Marking row five read while one to four stay unread has no representation in that model, and giving it one means a second source of truth that must agree with the cursor forever. What a watermark CAN express is "I have caught up to here", and in a newest-first feed that is what clicking a row usually means.
A row this actor cannot see is `404`, so an event uuid from another organization cannot move somebody else's cursor.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `item_uuid` | string | Yes | The inbox row's `uuid`. |

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `last_seen_at` | date-time | Yes | Everything at or before this time counts as read. |
| `unread_count` | integer | Yes | Unread items. |

#### Error codes

| Status | When |
|--------|------|
| `400` | The agent name is invalid (`invalid_agent_attribution`). |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |
| `404` | Not found, or not visible to you. Both cases return the same body. |

#### Example (curl)

```bash
curl -sS -X POST "https://api.dailybot.com/v1/plan/inbox/00000000-0000-4000-8000-00000000000f/read/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks inbox-read 00000000-0000-4000-8000-00000000000f
```

##### Response

```bash
{
  "last_seen_at": "2026-09-25T10:14:02Z",
  "unread_count": 3
}
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/inbox/unread-count/` · Beta

**Unread inbox count for the caller**

How many inbox items you have not read yet, for a badge. It takes the same filters as the inbox list, so each tab's badge counts exactly that tab's rows: `mentioned=true` for Mentions, `type=task.owner_changed` for Assigned. With no parameters it counts the whole inbox. Cheaper than listing the inbox.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `type` | string | No | Only events of this type, such as `task.owner_changed` (the Assigned tab). Combines with `mentioned` (AND). |
| `mentioned` | boolean | No | `true` keeps only the events where someone mentioned you. Must be `true` or `false`. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `unread_count` | integer | Yes | Unread items. |

#### Error codes

| Status | When |
|--------|------|
| `400` | An undeclared parameter or a bad value (`invalid_filter_value`). |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/inbox/unread-count/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks inbox-unread
```

##### Response

```bash
{
  "unread_count": 3
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/activity/` · Beta

**Org activity feed for Plan Home**

Paginated events you may open, enriched with task cards and resolved `changes[{field, from, to}]` for display. Tasks you cannot see are omitted even when their board is visible.

Only the parameters listed here are accepted: any other query parameter is `400 invalid_filter_value`.

- **Auth:** API key (`X-API-KEY`), CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** Page-number pagination

#### Query parameters

##### Pagination

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `page` | integer | No | 1-based page number. |
| `page_size` | integer | No | Rows per page. Default 50, maximum 100. Out-of-range values are clamped, never rejected: asking for 500 returns 100. |
| `limit` | integer | No | Alias for `page_size`, translated server-side. |
| `offset` | integer | No | Alias translated to `page` server-side. |

##### Filters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `type` | string | No | Filter to one event type. `event_type` is an alias. |
| `actor` | uuid | No | Only events by this person (user uuid). |
| `project` | uuid | No | Only events in this project (uuid). |
| `board` | uuid | No | Only events on this board (uuid). |
| `task` | uuid | No | Only events about this task (uuid). |
| `since` | string | No | ISO datetime: events recorded at or after this time (`observed_at`). |
| `until` | string | No | ISO datetime: events recorded at or before this time (`observed_at`). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `count` | integer | Yes | Total number of rows. |
| `next` | uri | Yes | URL of the next page, or `null`. |
| `previous` | uri | Yes | URL of the previous page, or `null`. |
| `results` | array<ActivityEvent> | Yes | The rows on this page. See [ActivityEvent](#plan-inbox-list-activityevent). |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/activity/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks activity --last-week --json
dailybot plan tasks activity --board 00000000-0000-4000-8000-000000000002 --since 2026-09-20T00:00:00Z --json
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Works with a login session, a CLI user token, a personal API key, or an agent or organization key. A personal key sees what its person sees; an agent or organization key acts as a system actor and sees organization-visible boards only.

### GET `/v1/plan/me/activity-cursor/` · Beta

**Read the caller's activity read cursor**

Your activity read cursor: the moment up to which you have read the activity feed. It is `null` until you set it.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### ActivityCursor object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `last_seen_at` | date-time | null | Yes | Everything at or before this time counts as read. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | ActivityCursor | Yes | A [ActivityCursor](#plan-me-activity-cursor-get-activitycursor) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/me/activity-cursor/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks cursor
```

##### Response

```bash
{
  "last_seen_at": null
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### PUT `/v1/plan/me/activity-cursor/` · Beta

**Mark activity as read up to a timestamp**

Stores your activity read cursor at `last_seen_at`, so another client can pick up where you left off.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Request body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `last_seen_at` | date-time | Yes | Everything at or before this time counts as read. |
| `agent_name` | string | No | The name of the agent that executed this write on the person's behalf (max 128 characters, blank means no agent). Takes priority over the `X-Dailybot-Agent-Name` header. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | ActivityCursor | Yes | A [ActivityCursor](#plan-me-activity-cursor-get-activitycursor) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. `invalid_agent_attribution` means the agent name is invalid. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |

#### Example (curl)

```bash
curl -sS -X PUT "https://api.dailybot.com/v1/plan/me/activity-cursor/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "last_seen_at": "2026-09-25T10:14:02Z"
  }'
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks cursor --now
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/labels/` · Beta

**List organization labels**

The organization's label taxonomy, shared with forms and check-ins. Prefer this endpoint for settings screens; the board-scoped list is the same taxonomy behind a board access check.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `page` | integer | No | 1-based page number. |
| `page_size` | integer | No | Rows per page. Default 50, maximum 100. Out-of-range values are clamped, never rejected: asking for 500 returns 100. |
| `search` | string | No | Case-insensitive substring match on the label name only. Empty means no filter; a value that matches nothing returns an empty list. |
| `include_archived` | boolean | No | Include archived rows alongside live ones. Distinct from `is_archived`, which selects one set or the other: `include_archived=true` is the union. Lists return live rows unless you opt in. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `next` | string|null | Yes | URL of the next page, or `null`. |
| `previous` | string|null | Yes | URL of the previous page, or `null`. |
| `results` | array<Label> | Yes | The rows on this page. |

#### Error codes

| Status | When |
|--------|------|
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/labels/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### Response

```bash
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "00000000-0000-4000-8000-00000000000b",
      "name": "backend",
      "color": "#2563eb"
    }
  ]
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### POST `/v1/plan/labels/` · Beta

**Create an organization label**

Creates a label in the organization taxonomy. Same shape as `POST /boards/{board_id}/labels/`.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Request body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `name` | string | Yes | Display name. |
| `color` | string | No | Display color (hex). |
| `description` | string | No | Free-form description. |
| `agent_name` | string | No | The name of the agent that executed this write on the person's behalf (max 128 characters, blank means no agent). Takes priority over the `X-Dailybot-Agent-Name` header. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | Label | Yes | A [Label](#plan-me-tasks-list-label) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. `invalid_agent_attribution` means the agent name is invalid. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS -X POST "https://api.dailybot.com/v1/plan/labels/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "backend",
    "color": "#2563eb"
  }'
```

#### Scenario examples

##### Response

```bash
{
  "uuid": "00000000-0000-4000-8000-00000000000b",
  "name": "backend",
  "color": "#2563eb"
}
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### PATCH `/v1/plan/labels/{label_id}/` · Beta

**Update an organization label**

Partial update of name, color, description, or `is_archived`. Archiving hides the label from the default list without hard-deleting it. Restore is the same field the other way: `{"is_archived": false}`. Read a retired label back with `GET /v1/plan/labels/?include_archived=true`.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `label_id` | string | Yes | The label's uuid. |

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Request body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `name` | string | No | Display name. |
| `color` | string | No | Display color (hex). |
| `description` | string | No | Free-form description. |
| `is_archived` | boolean | No | Whether the row is archived. Archive is the delete: archived rows stay readable and restorable. |
| `agent_name` | string | No | The name of the agent that executed this write on the person's behalf (max 128 characters, blank means no agent). Takes priority over the `X-Dailybot-Agent-Name` header. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | Label | Yes | A [Label](#plan-me-tasks-list-label) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. `invalid_agent_attribution` means the agent name is invalid. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |
| `404` | Not found, or not visible to you. Both cases return the same body. |

#### Example (curl)

```bash
curl -sS -X PATCH "https://api.dailybot.com/v1/plan/labels/00000000-0000-4000-8000-00000000000b/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "is_archived": true
  }'
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### DELETE `/v1/plan/labels/{label_id}/` · Beta

**Delete an organization label**

Hard-deletes when the label has no task assignments. Otherwise `409 label_in_use`. Prefer `PATCH` with `is_archived: true` to retire a label that is still on cards.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `label_id` | string | Yes | The label's uuid. |

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Error codes

| Status | When |
|--------|------|
| `400` | The agent name is invalid (`invalid_agent_attribution`). |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |
| `404` | Not found, or not visible to you. Both cases return the same body. |
| `409` | The label is still on tasks (`label_in_use`). Archive it with `PATCH` instead. |

#### Example (curl)

```bash
curl -sS -X DELETE "https://api.dailybot.com/v1/plan/labels/00000000-0000-4000-8000-00000000000b/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### GET `/v1/plan/entitlements/` · Beta

**Whether Plan is available here, and the plan ceilings**

Whether Plan is available to your organization, and its plan ceilings. It is the one Plan endpoint that never answers `402`, so call it before deciding whether to show the product.

`enabled` is the same check every other endpoint applies. `reason` is `null` when enabled; otherwise `rollout` (your organization is not enabled for the Beta yet) or `usage` (an admin turned Plan off). `boards` and `projects` report `{used, limit}`, even when disabled; `limit` is `null` when the plan has no cap. The free plan includes up to 3 boards and 1 project. `used` counts live rows only, so archiving frees a slot, and `used > limit` can happen on grandfathered plans.

A guest gets `403 guest_not_allowed` here, not `200` with `enabled: false`, and never sees plan ceilings.

- **Auth:** API key (`X-API-KEY`), CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Entitlements object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `enabled` | boolean | Yes | Whether Plan is enabled for your organization. |
| `reason` | enum | null | Yes | Why Plan is not enabled: `rollout` or `usage`; `null` when enabled. One of `rollout`, `usage`. |
| `boards` | object | Yes | Shape: `{used: integer, limit: integer|null} (both required)`. |
| `projects` | object | Yes | Linked projects. Shape: `{used: integer, limit: integer|null} (both required)`. |
| `labels` | object | Yes | Organization labels on the task. Shape: `{enabled: boolean}`. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | Entitlements | Yes | A [Entitlements](#plan-entitlements-get-entitlements) object. |

#### Error codes

| Status | When |
|--------|------|
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/entitlements/" \
  -H "X-API-KEY: $DAILYBOT_API_KEY"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks entitlements
```

##### Response

```bash
{
  "enabled": false,
  "reason": null,
  "boards": {},
  "projects": {},
  "labels": {}
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Works with a login session, a CLI user token, a personal API key, or an agent or organization key. A personal key sees what its person sees; an agent or organization key acts as a system actor and sees organization-visible boards only.

### GET `/v1/plan/timeline/` · Beta

**The scheduled work over a window, with its dependency edges and goal bands**

The same filtered set as the task list, asked a scheduling question. `rows` are the cards that OVERLAP the window; `unscheduled` counts the matching cards with no dates at all; `dependencies` carries only edges whose both ends are in `rows`, because an arrow to a row the reader cannot see is a line to nowhere on the screen and a disclosure off it.
Window selection (first match wins):
- `from` + `to` (ISO dates) — explicit range; **`from` may be in the past** (e.g. today−7 … today+21). Aliases: `window_from` / `window_to`.
- `window_days` — forward helper: today through today+N (inclusive span).
- omitted — default forward window of 14 days from today (with a board filter).

- **Auth:** API key (`X-API-KEY`), CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Query parameters

##### Filters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `from` | string | No | First day of an explicit window (ISO date). May be in the past. Pair with `to`. Alias: `window_from`. |
| `to` | string | No | Last day of an explicit window (ISO date). Must be ≥ `from`. Alias: `window_to`. |
| `window_from` | string | No | Alias for `from`. |
| `window_to` | string | No | Alias for `to`. |
| `window_days` | integer | No | Forward-from-today helper. Ignored when both `from` and `to` (or their aliases) are present. Default when no explicit window is 14. |
| `board` | array | No | Board uuids **or** board keys (`ENG`). Repeatable; values are OR-ed. Keys resolve within your organization only; a key that names no board of yours contributes nothing and never returns a 404. Retired keys keep resolving. |
| `project` | array | No | Project uuids. Repeatable; values are OR-ed. |
| `goal` | string | No | Repeatable. Matches a task's own goal, or the goal it inherits from its project when it has none, the same rule every progress roll-up uses. |
| `team` | string | No | Repeatable. The board's team. It narrows what you see and never widens it. |
| `owner` | array | No | A user uuid, `me`, or `unowned`. Repeatable; values are OR-ed, tokens included: `owner=me&owner=unowned` returns your tasks and the unowned ones. `me` with an agent or organization key is `400 actor_required`. |
| `category` | array | No | The five fixed state categories. There is no `blocked` category — blocked-ness is a relation; use `blocked=true`. |
| `label` | array | No | Label uuids — v4 only, at most 50, matching the shared label filter's existing cap. A non-v4 value is `400 invalid_label_filter`. |
| `include_unscheduled` | string | No | When `1` or `true`, `unscheduled` is `{count, results[]}` (capped) instead of a bare integer count. |

#### Timeline object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `window` | object | Yes | The window covered. Shape: `{from: string, to: string}`. |
| `bands` | array | No | Goals live across the window. Items: `{uuid, name, status, period_start, period_end}`. |
| `rows` | array | Yes | Tasks that overlap the window. Items: `{uuid, key, title, state (state name), category, owner (UserRef|null), goal (uuid|null), start_date, due_date, completed_at, is_blocked, is_overdue}`. |
| `dependencies` | array | No | Dependency edges whose both ends are in `rows`. Items: `{source (task uuid), target (task uuid), relation_type}`. |
| `unscheduled` | integer | {count: integer, results: array} | Yes | Matching tasks with no dates at all. |
| `truncated` | boolean | No | `true` when more changes are waiting: poll again immediately. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | Timeline | Yes | A [Timeline](#plan-timeline-timeline) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |
| `429` | Rate limit reached. Wait the number of seconds in `Retry-After`. |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/timeline/?board=ENG&from=2026-09-18&to=2026-10-16" \
  -H "X-API-KEY: $DAILYBOT_API_KEY"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks timeline --today
```

##### Response

```bash
{
  "window": {},
  "bands": [],
  "rows": [],
  "dependencies": [],
  "unscheduled": 1,
  "truncated": false
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Works with a login session, a CLI user token, a personal API key, or an agent or organization key. A personal key sees what its person sees; an agent or organization key acts as a system actor and sees organization-visible boards only.

### GET `/v1/plan/search/` · Beta

**Search tasks and containers visible to the caller**

Search tasks (title, key, description) and, with `types`, projects, boards and goals you can see.

Matching is case-insensitive substring search, not full-text ranking. `score` is a coarse ordering aid between 0.6 and 1.0 (exact key 1.0, title or name hit 0.9 / 0.85, description hit 0.6), not calibrated relevance, and `snippet` is a window around the first match. The response repeats this in `approximation`. Hidden tasks and private boards never appear.

- **Auth:** API key (`X-API-KEY`), CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `q` | string | Yes | The query string. Fewer than 2 characters is refused with `search_query_too_short`; more than 256 with `search_query_too_long`. |
| `types` | array | No | Entity kinds to include: `task`, `project`, `board`, `goal` (default: all). Repeatable, or comma-separated: `?types=task,board` and `?types=task&types=board` are equivalent. Unknown values are `400 invalid_filter_value`. |
| `board` | array | No | Board uuids **or** board keys (`ENG`). Repeatable; values are OR-ed. Keys resolve within your organization only; a key that names no board of yours contributes nothing and never returns a 404. Retired keys keep resolving. |
| `project` | array | No | Project uuids. Repeatable; values are OR-ed. |
| `limit` | integer | No | Alias for `page_size`, translated server-side. |

#### SearchResponse object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `q` | string | Yes | The query you sent. |
| `limit` | integer | Yes | Page size applied. From 1 to 100. |
| `approximation` | string | Yes | How matching works (substring search, not full-text ranking). |
| `results` | array | Yes | The rows on this page. Items: `{type: task|project|board|goal, uuid, title (tasks) or name (containers), key? (tasks), score?, snippet?}`. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | SearchResponse | Yes | A [SearchResponse](#plan-search-searchresponse) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | `q` is shorter than 2 characters (`search_query_too_short`) or longer than 256 (`search_query_too_long`), or a `types` value is unknown. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `403` | Authenticated but not allowed: missing scope (`insufficient_scope`, which is also what an agent or organization key gets on an operation that needs a person, and what a personal key gets when its explicit Plan scopes do not cover the endpoint) or a guest account (`guest_not_allowed`). |
| `429` | Rate limit reached. Wait the number of seconds in `Retry-After`. |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/search/?q=delta&types=task,board" \
  -H "X-API-KEY: $DAILYBOT_API_KEY"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks search -q "delta" --json
```

##### Response

```bash
{
  "q": "delta",
  "limit": 1,
  "approximation": "substring",
  "results": []
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Works with a login session, a CLI user token, a personal API key, or an agent or organization key. A personal key sees what its person sees; an agent or organization key acts as a system actor and sees organization-visible boards only.

### GET `/v1/plan/pulse/` · Beta

**The home screen in one request (HomePulse), or a board-health aggregate (TaskPulse)**

Two modes, chosen by the presence of `group_by`.

**HomePulse** (no `group_by`): the Plan home in one request — `generated_at`, integer `counts`, your `my_preview`, board, goal and timeline teasers, `agent_summary`, and the optional `include` bands. The population is stated as `scope: "viewer_visible"`: every live, non-terminal task you can see — not the whole organization. Each tile names the query that reproduces it: `open` → `?state=open`, `overdue` → `?due_before=<today>&state=open`, `blocked` → `?blocked=true&state=open`.

**TaskPulse** (with `group_by`, e.g. `group_by=state`): a board-health aggregate with totals, throughput and cycle time. Counts are integers only and there is no per-person breakdown. Answers `If-None-Match` with `304`.

- **Auth:** API key (`X-API-KEY`), CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Query parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `board` | array | No | Board uuids **or** board keys (`ENG`). Repeatable; values are OR-ed. Keys resolve within your organization only; a key that names no board of yours contributes nothing and never returns a 404. Retired keys keep resolving. |
| `project` | array | No | Project uuids. Repeatable; values are OR-ed. |
| `window_days` | integer | No | The trailing window for throughput and cycle time. |
| `group_by` | string | No | **Presence selects TaskPulse mode** (a board-health aggregate). Omit it entirely for HomePulse; there is no default. The enum is closed and contains no person dimension: per-person output is refused by design. |
| `include` | string | No | HomePulse only (no `group_by`). Comma-separated opt-in bands so a home screen renders from one request: `projects` → `projects_preview` (visible live projects, progress, newest update), `attention` → `attention` (your open work that is overdue or blocked), `activity` → `recent_activity`, `goal_progress` → `progress` and `projects` on each `goals_preview` row. A band you do not ask for is absent and costs nothing. An unknown token is `400 invalid_filter_value`. |

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `If-None-Match` | string | No | The ETag from your previous read. A match answers `304`. |

#### HomePulse object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `generated_at` | date-time | Yes | When the response was computed. |
| `scope` | enum | Yes | The population counted: `viewer_visible` (everything you can see). One of `viewer_visible`. |
| `open` | integer | No | Tasks in a `backlog`, `todo` or `in_progress` state. |
| `overdue` | integer | No | Open tasks past their due date. |
| `blocked` | integer | No | Tasks with a live blocker. |
| `unread_count` | integer | Yes | Unread items. |
| `counts` | HomePulseCounts {open_tasks, open_tasks_on_goal_linked_projects, overdue_tasks, blocked_tasks, active_boards, active_projects, active_goals: integer} | Yes | Integer counts across what you can see. All fields are always present. |
| `my_preview` | object | Yes | Your overdue and due-today work. Shape: `{overdue: integer, due_today: integer, top_tasks: array of {uuid, title, due_date, priority, board (uuid)}} (all required)`. |
| `recent_boards` | array | Yes | Items: `{uuid, name, key, project (uuid|null), updated_at, viewer{is_member, can_see_content, can_manage}}`. |
| `featured_boards` | array | Yes | Items: `{uuid, name, key, project (uuid|null), updated_at, viewer{is_member, can_see_content, can_manage}}`. |
| `goals_preview` | array | Yes | Items: `{uuid, name, status, period_start, period_end}; with include=goal_progress also progress (GoalProgress|null) and projects [{uuid, name, …}]`. |
| `timeline_teaser` | object | Yes | Work due soon. Shape: `{window_from: string, window_to: string, due_soon_count: integer, rows: array} (all required)`. |
| `agent_summary` | object | Yes | Boards where agents act, and approvals waiting. Shape: `{boards_advisory, boards_autonomous, pending_approvals: integer} (all required)`. |
| `projects_preview` | array | No | Present only with `include=projects`. Items: `{uuid, name, health, progress (ProjectProgress|null), latest_update (ProjectUpdate|null)}`. |
| `attention` | array | No | Present only with `include=attention`. Items: `{uuid, key, title, due_date, priority, board, state{uuid, name, category}, overdue, blocked}`. |
| `recent_activity` | array<ActivityEvent> | No | Present only with `include=activity`. See [ActivityEvent](#plan-inbox-list-activityevent). |

#### TaskPulse object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `board` | Board | null | No | The board. See [Board](#plan-pulse-board). |
| `window_days` | integer | Yes | — |
| `generated_at` | date-time | Yes | When the response was computed. |
| `group_by` | enum | Yes | The grouping dimension. One of `state`, `category`, `label`, `priority`, `board`, `age`. |
| `groups` | array | Yes | One entry per column (or group), in order. Always present: `key`, `count`. Items: `{key: string, name: string|null, count: integer, oldest_age_days: integer|null}`. |
| `totals` | object | Yes | Shape: `{total, blocked, unassigned, overdue , created_in_window, completed_in_window: integer}`. |
| `throughput` | array | No | All fields are always present. Items: `{week_start: string, created: integer, completed: integer}`. |
| `cycle_time_days_p50` | number | null | No | — |
| `cycle_time_days_p90` | number | null | No | — |

#### Board object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `key` | string | Yes | The board's key: the prefix of its tasks' keys. Renaming it keeps the old key reserved and resolving. |
| `name` | string | Yes | Display name. Max 120 characters. |
| `project` | Project | No | The project. See [Project](#plan-pulse-project). |
| `team` | uuid | null | No | The team. |
| `visibility` | enum | Yes | `org` (everyone in the organization) or `members` (explicit members only). One of `org`, `members`. |
| `effective_visibility` | string | No | Whether the board is effectively visible to the whole organization (`org`) or only to members (`members`). A board inside a `members` project is `members` here, while `visibility` stays the board's own stored setting. |
| `estimate_scale` | enum | No | How estimates are expressed on this board. One of `none`, `fibonacci`, `linear`. |
| `default_view` | SavedView | null | No | The board's default saved view, or `null`. See [SavedView](#plan-pulse-savedview). |
| `archive_after_days` | integer | null | No | Archive done tasks automatically after this many days, or `null` to keep them. |
| `task_count` | integer | No | Number of live tasks. |
| `wip_limits` | object | No | Work-in-progress limits per column. |
| `is_archived` | boolean | No | Whether the row is archived. Archive is the delete: archived rows stay readable and restorable. |
| `created_at` | date-time | No | When the row was created. |
| `updated_at` | date-time | No | When the row last changed. |
| `viewer` | object | No | What you can do with this row. Shape: `{is_member, can_see_content, can_manage: boolean} (all required)`. |
| `states` | array<WorkflowState> | No | The board's workflow states, in column order. See [WorkflowState](#plan-me-tasks-list-workflowstate). |

#### Project object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `name` | string | Yes | Display name. Max 120 characters. |
| `slug` | string | No | URL-friendly name. Max 48 characters. |
| `description` | string | null | No | Free-form description. |
| `lead` | UserRef | null | No | The project's lead. See [UserRef](#plan-me-tasks-list-userref). |
| `goals` | array | No | Goals this project points at. A project can serve several goals. Always present: `uuid`. Items: `{uuid, name}`. |
| `goal` | object | No | The goal, when there is exactly one. Shape: `{uuid, name}|null`. |
| `board_count` | integer | No | Number of live boards in the project. |
| `health` | enum | No | Declared health. One of `not_set`, `on_track`, `at_risk`, `off_track`. |
| `start_date` | date | null | No | Planned start date. |
| `target_date` | date | null | No | Planned end date. |
| `progress` | ProjectProgress | null | No | Progress roll-up over the tasks you can see. See [ProjectProgress](#plan-pulse-projectprogress). |
| `is_archived` | boolean | Yes | Whether the row is archived. Archive is the delete: archived rows stay readable and restorable. |
| `archived_at` | date-time | null | No | When the row was archived. |
| `created_at` | date-time | No | When the row was created. |
| `updated_at` | date-time | No | When the row last changed. |
| `viewer` | object | No | What you can do with this row. Shape: `{can_see_content: boolean, can_manage: boolean} (both required)`. |

#### SavedView object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `name` | string | Yes | Display name. Max 64 characters. |
| `view_mode` | enum | No | How the filtered set is drawn. Reads always return `board` for the kanban layout. One of `list`, `board`, `timeline`, `calendar`. |
| `group_by` | enum | No | The grouping dimension. One of `state`, `owner`, `priority`, `category`. |
| `sort` | string | No | A sort key, `-` prefixed for descending. |
| `filters` | object | Yes | The view's filters, in the shared task filter grammar. |
| `schema_version` | integer | No | Version of the view's stored format. |
| `visibility` | enum | No | `personal` (default) is yours alone. `shared` and `board_default` (the default view for that board or project) are readable by everyone who can see the board or project. Setting them needs a board manager on board views, and project oversight (an organization admin or a manager of all teams) on project views; otherwise `403 view_visibility_forbidden`. One of `personal`, `shared`, `board_default`. |
| `collapsed` | object | array | string | number | boolean | No | Client UI state stored verbatim (which groups are collapsed). Only size and depth are validated. |
| `columns` | object | array | string | number | boolean | No | Client UI state stored verbatim (which columns are shown). Only size and depth are validated. |
| `uuid` | uuid | No | Stable public identifier. |
| `scope` | enum | No | Which container the view belongs to: `board` or `project`. Read-only. One of `board`, `project`. |
| `board` | uuid | null | No | The board's uuid when `scope` is `board`; `null` for a project view. Read-only. |
| `owner` | object | No | Who owns the view. Shape: `{uuid, name}`. |
| `created_at` | date-time | No | When the row was created. |
| `updated_at` | date-time | No | When the row last changed. |

#### ProjectProgress object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `total` | integer | Yes | All tasks counted. |
| `completed` | integer | Yes | Tasks in a `done` or `canceled` state. |
| `open` | integer | No | Tasks in a `backlog`, `todo` or `in_progress` state. |
| `blocked` | integer | No | Tasks with a live blocker. |
| `overdue` | integer | No | Open tasks past their due date. |
| `percent_complete` | integer | Yes | `completed` as a percentage of `total`. |

#### Error codes

| Status | When |
|--------|------|
| `304` | Not modified: the `If-None-Match` ETag you sent still matches. |
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `404` | Not found, or not visible to you. Both cases return the same body. |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/pulse/?include=projects,attention,activity,goal_progress" \
  -H "X-API-KEY: $DAILYBOT_API_KEY"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks status --json
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Works with a login session, a CLI user token, a personal API key, or an agent or organization key. A personal key sees what its person sees; an agent or organization key acts as a system actor and sees organization-visible boards only.

### GET `/v1/plan/me/favorites/` · Beta

**Your pinned boards and saved views**

Every pin you own, in `rank` order (1 is the top). A pin whose target you can no longer see (an archived or hidden board, or a saved view that is gone or no longer shared) is omitted rather than raised. The list uses the standard list envelope but is never paged: `next` and `previous` are always `null`, and it holds up to 50 pins. Needs a person: agent and organization keys are refused; a personal API key works.

- **Auth:** CLI Bearer (read)
- **Rate limit:** `default`
- **Pagination:** Page-number pagination

#### Favorite object

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `uuid` | uuid | Yes | Stable public identifier. |
| `target_type` | enum | Yes | What is pinned: `board` or `view`. One of `board`, `view`. |
| `target_uuid` | uuid | Yes | The board's or the saved view's uuid, per `target_type`. |
| `rank` | integer | Yes | 1-based position in your list. Minimum 1. |
| `created_at` | date-time | Yes | When the row was created. |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `count` | integer | Yes | Total number of rows. |
| `next` | uri | Yes | URL of the next page, or `null`. |
| `previous` | uri | Yes | URL of the previous page, or `null`. |
| `results` | array<Favorite> | Yes | The rows on this page. See [Favorite](#plan-me-favorites-list-favorite). |

#### Error codes

| Status | When |
|--------|------|
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |

#### Example (curl)

```bash
curl -sS "https://api.dailybot.com/v1/plan/me/favorites/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan tasks favorites --json
```

##### Response

```bash
{
  "count": 1,
  "next": null,
  "previous": null,
  "results": [
    {
      "uuid": "00000000-0000-4000-8000-000000000012",
      "target_type": "board",
      "target_uuid": "00000000-0000-4000-8000-000000000012",
      "rank": "aU",
      "created_at": "2026-09-25T10:14:02Z"
    }
  ]
}
```

#### Notes

- Scope: `tasks:read`.
- Rate limit: 120 reads per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### POST `/v1/plan/me/favorites/` · Beta

**Pin a board or a saved view**

New pins land at the bottom of your list. Pinning something already pinned returns the existing pin rather than a duplicate. A target you cannot see is `404`, never `403`. You can pin up to 50 boards and views; one more is `400 favorite_limit_reached`.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `Idempotency-Key` | string | No | A key you generate for this intent. A replay with the same key and body returns the first response without a second side effect and carries `Idempotency-Replayed: true`. Keys are kept for 24 hours. The same key with a different body is `409 idempotency_key_payload_mismatch`; a repeat while the first call is still running gets `409 idempotency_in_progress` for up to 120 seconds. |
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Request body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `target_type` | enum | Yes | What is pinned: `board` or `view`. One of `board`, `view`. |
| `target_uuid` | uuid | Yes | The board's or the saved view's uuid, per `target_type`. |
| `agent_name` | string | No | The name of the agent that executed this write on the person's behalf (max 128 characters, blank means no agent). Takes priority over the `X-Dailybot-Agent-Name` header. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | Favorite | Yes | A [Favorite](#plan-me-favorites-list-favorite) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. `invalid_agent_attribution` means the agent name is invalid. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `404` | Not found, or not visible to you. Both cases return the same body. |

#### Example (curl)

```bash
curl -sS -X POST "https://api.dailybot.com/v1/plan/me/favorites/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "target_type": "board",
    "target_uuid": "00000000-0000-4000-8000-000000000012"
  }'
```

#### Scenario examples

##### CLI

```bash
dailybot plan board star 00000000-0000-4000-8000-000000000002
dailybot plan tasks view star 00000000-0000-4000-8000-000000000010
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### PATCH `/v1/plan/me/favorites/{favorite_id}/` · Beta

**Move one pin within your list**

Send one of `after` (place it just below that pin), `before` (just above it) or `rank` (1-based position, clamped to the list). When more than one is sent, `after` wins, then `before`. Ranks are renumbered to `1..n`. A neighbour that is not one of your pins is `404`.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `favorite_id` | uuid | Yes | The pin's uuid. |

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Request body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `rank` | integer | No | 1-based target position, clamped to the list. Minimum 1. |
| `before` | uuid | No | Place the pin just above this pin. |
| `after` | uuid | No | Place the pin just below this pin. |
| `agent_name` | string | No | The name of the agent that executed this write on the person's behalf (max 128 characters, blank means no agent). Takes priority over the `X-Dailybot-Agent-Name` header. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Response body

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `(body)` | Favorite | Yes | A [Favorite](#plan-me-favorites-list-favorite) object. |

#### Error codes

| Status | When |
|--------|------|
| `400` | Validation failed, or a filter, sort or `include` value was not recognised. The response `code` says which. `invalid_agent_attribution` means the agent name is invalid. |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `404` | Not found, or not visible to you. Both cases return the same body. |

#### Example (curl)

```bash
curl -sS -X PATCH "https://api.dailybot.com/v1/plan/me/favorites/{favorite_id}/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

### DELETE `/v1/plan/me/favorites/{favorite_id}/` · Beta

**Unpin**

Removes the pin, never its target. The remaining pins are renumbered to `1..n`.

- **Auth:** CLI Bearer (write)
- **Rate limit:** `default`
- **Pagination:** No pagination

#### Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `favorite_id` | uuid | Yes | The pin's uuid. |

#### Headers

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `X-Dailybot-Agent-Name` | string | No | The name of the agent that executed this write on the person's behalf. Use it on multipart and body-less writes (DELETE, archive, restore); on JSON writes send the body field `agent_name` instead, which wins if both are present. Percent-encode the value as UTF-8. Control characters are stripped; a blank value means no agent. More than 128 characters, or a value that cannot be decoded, is `400 invalid_agent_attribution` (never truncated). An agent-type key, which is not bound to a person, gets `400 invalid_agent_attribution` if it sends it. The stamp never changes a permission answer. See [Agent attribution](/developers/plan/conventions#agent-attribution). |

#### Error codes

| Status | When |
|--------|------|
| `400` | The agent name is invalid (`invalid_agent_attribution`). |
| `401` | Missing, expired or malformed credential (`credential_absent`, `credential_expired`, `credential_malformed`). |
| `402` | Plan is not enabled for your organization yet (`plan_upgrade_required`). Expected during the Beta: write to support@dailybot.com. |
| `404` | Not found, or not visible to you. Both cases return the same body. |

#### Example (curl)

```bash
curl -sS -X DELETE "https://api.dailybot.com/v1/plan/me/favorites/{favorite_id}/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"
```

#### Scenario examples

##### CLI

```bash
dailybot plan board unstar 00000000-0000-4000-8000-000000000002
dailybot plan tasks view unstar 00000000-0000-4000-8000-000000000010
```

#### Notes

- Scope: `tasks:write`.
- Rate limit: 60 writes per minute per actor.
- Needs a person: call it with a login session, a CLI user token or a personal API key. An agent or organization key gets `403 insufficient_scope`.

---

## Developer portal navigation

**Getting Started**

- [Overview](/developers)
- [Quick start](/developers/getting-started)
- [Authentication](/developers/authentication)

**API Reference**

- [API Overview](/developers/api)
- [Users](/developers/api/users)
- [Organization](/developers/api/organization)
- [Teams](/developers/api/teams)
- [Invitations](/developers/api/invitations)
- [Check-ins](/developers/api/check-ins)
- [Forms](/developers/api/forms)
- [Labels](/developers/api/labels)
- [Report channels](/developers/api/report-channels)
- [Templates](/developers/api/templates)
- [Kudos](/developers/api/kudos)
- [Mood tracking](/developers/api/mood)
- [Important dates](/developers/api/important-dates)
- [Messaging](/developers/api/messaging)
- [Automations](/developers/api/workflows)
- [Webhooks](/developers/api/webhooks)
- [Commands platform](/developers/api/commands-platform)
- [Agents](/developers/api/agents)
- [OAuth2](/developers/api/oauth2)
- [Integrations](/developers/api/integrations)
- [CLI](/developers/api/cli)
- [Plan · Projects](/developers/api/plan-projects)
- [Plan · Goals](/developers/api/plan-goals)
- [Plan · Boards](/developers/api/plan-boards)
- [Plan · Tasks](/developers/api/plan-tasks)
- [Plan · Comments & files](/developers/api/plan-collaboration)
- [Plan · Home & search](/developers/api/plan-home) (this page)
- [Plan · Notifications & reports](/developers/api/plan-notifications)

**Dailybot Plan**

- [Overview](/developers/plan)
- [Concepts](/developers/plan/concepts)
- [Quickstart](/developers/plan/quickstart)
- [Authentication & scopes](/developers/plan/authentication)
- [Agents on Plan](/developers/plan/agents)
- [Conventions](/developers/plan/conventions)
- [Errors](/developers/plan/errors)
- [CLI for Plan](/developers/plan/cli)
- [Agent skill](/developers/plan/agent-skill)
- [Recipe: live board](/developers/plan/recipes/board-live-updates)
- [Recipe: home in one request](/developers/plan/recipes/home-in-one-request)
- [Recipe: bulk create](/developers/plan/recipes/bulk-create)
- [Recipe: move on PR merge](/developers/plan/recipes/move-on-pr-merge)
- [Recipe: goal progress](/developers/plan/recipes/goal-progress)
- [Recipe: webhooks](/developers/plan/recipes/webhooks)

**API guides**

- [Errors & Status Codes](/developers/errors)
- [Rate Limits](/developers/rate-limits)
- [Conventions](/developers/conventions)
- [API Changelog](/developers/api-changelog)
- [Recipes](/developers/recipes)

**Developer Features**

- [Custom commands](/developers/custom-commands)
- [Serverless commands](/developers/serverless)
- [Webhooks & events](/developers/webhooks)
- [Automation API trigger](/developers/workflow-trigger)
- [Activity API](/developers/activity-api)

**CLI**

- [Overview](/developers/cli)
- [Authentication](/developers/cli-authentication)
- [Command reference](/developers/cli-reference)
- [CI/CD recipes](/developers/cli-ci-cd)
- [Configuration](/developers/cli-configuration)
- [Troubleshooting](/developers/cli-troubleshooting)

**Agent Skill**

- [Overview](/developers/agent-skill)
- [Skills catalog](/skills)

---

## Site navigation

**Product:**
- [Home](/)
- [Product](/product)
- [Pricing](/pricing)
- [Enterprise](/enterprise)
- [Integrations](/integrations)
- [Templates](/templates)

**Resources:**
- [Blog](/blog)
- [Academy](/academy)
- [Changelog](/changelog)
- [Help Center](/help)
- [Developers](/developers)
- [Agents](/agents)

**Company:**
- [About](/about)
- [Careers](/careers)
- [Security](/security)
- [Contact Sales](/demo)

**Connect:**
- [LinkedIn](https://www.linkedin.com/company/dailybot/)
- [X/Twitter](https://twitter.com/dailybot)
- [GitHub](https://github.com/Dailybot-Inc)
- [YouTube](https://www.youtube.com/channel/UC3uM9V52vwX7e3vQpCc4qvA)

