Plan · Goals
Goals say what the work is for. They point at projects; nothing lives inside a goal. Part of the Dailybot Plan API (Beta).
On this page
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 [email protected].
List goals
The goals you can see, as a page. Filter by status, owned_by, a date inside the goal's period with active_on, or text with search. include=progress,projects adds the progress roll-up and linked projects.
Query parameters
Sorting & expansion
| Name | Type | Required | Description |
|---|---|---|---|
| include | string | Optional | Comma-separated roll-ups to embed: progress, projects. Absent by default because each is an aggregate. An unknown token is 400 invalid_filter_value; an empty value is a no-op. |
Pagination
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | Optional | 1-based page number. |
| page_size | integer | Optional | 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 |
|---|---|---|---|
| search | string | Optional | Matches title and key. Longer than 256 characters is 400 search_query_too_long, not truncated. q is an alias. |
| status | string | Optional | Repeatable. Filters by declared status. |
| owned_by | string | Optional | The accountable person's uuid. Named owned_by rather than owner because the task grammar's owner accepts me and unowned, and one parameter name that means two different value spaces is how a client sends the wrong one. |
| active_on | string | Optional | Goals whose period covers this date - the roadmap's own question. |
Archived rows
| Name | Type | Required | Description |
|---|---|---|---|
| include_archived | boolean | Optional | 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. |
Goal object
| Name | Type | Required | Description |
|---|---|---|---|
| uuid | uuid | Required | Stable public identifier. |
| name | string | Required | Display name. Max 120 characters. |
| description | string | null | Optional | Free-form description. |
| status | enum | Required | Current status. One of not_started, on_track, at_risk, off_track, achieved, missed. |
| period_start | date | Required | First day of the goal's period. |
| period_end | date | Required | Last day of the goal's period. |
| owner | UserRef | null | Optional | The person accountable for the task. See UserRef. |
| team | TeamRef | null | Optional | The team. See TeamRef. |
| progress | GoalProgress | null | Optional | Progress roll-up over the tasks you can see. See GoalProgress. |
| project_count | integer | Optional | Number of linked projects. |
| projects | array | Optional | Linked projects. Items: {uuid, name, slug, health, lead}. |
| is_archived | boolean | Required | Whether the row is archived. Archive is the delete: archived rows stay readable and restorable. |
| completed_at | date-time | null | Optional | When it was completed, or null. |
| archived_at | date-time | null | Optional | When the row was archived. |
| created_at | date-time | Optional | When the row was created. |
| updated_at | date-time | Optional | When the row last changed. |
| viewer | object | Required | What you can do with this row. Shape: {can_manage: boolean}. |
UserRef object
TeamRef object
GoalProgress object
| Name | Type | Required | Description |
|---|---|---|---|
| total | integer | Required | All tasks counted. |
| completed | integer | Required | Tasks in a done or canceled state. |
| open | integer | Optional | Tasks in a backlog, todo or in_progress state. |
| blocked | integer | Optional | Tasks with a live blocker. |
| overdue | integer | Optional | Open tasks past their due date. |
| percent_complete | integer | Required | completed as a percentage of total. |
| is_partial | boolean | Required | true when some of the goal's work is hidden from you, so the numbers cover only what you can see. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| count | integer | Required | Total number of rows. |
| next | uri | Required | URL of the next page, or null. |
| previous | uri | Required | URL of the previous page, or null. |
| results | array<Goal> | Required | The rows on this page. See Goal. |
Errors
| 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 [email protected]. |
| 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`. |
curl -sS "https://api.dailybot.com/v1/plan/goals/?include=progress,projects" \
-H "X-API-KEY: $DAILYBOT_API_KEY"dailybot plan goal list --include progress --include projects{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"uuid": "00000000-0000-4000-8000-000000000006",
"name": "Q4 Roadmap",
"description": null,
"status": "on_track",
"period_start": "2026-09-28",
"period_end": "2026-10-15",
"owner": {
"uuid": "00000000-0000-4000-8000-00000000000c",
"name": "Ada L."
},
"team": null,
"progress": {
"total": 10,
"completed": 4,
"percent_complete": 40,
"is_partial": false
},
"project_count": 1,
"projects": [],
"is_archived": false,
"completed_at": null,
"archived_at": null,
"created_at": "2026-09-25T10:14:02Z",
"updated_at": "2026-09-25T10:14:02Z",
"viewer": {}
}
]
}Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- 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.
Create a goal
Creates a goal with a period and a declared status. A live goal with the same name is 409 goal_name_conflict. Every non-guest member can call it (with a login session or a personal API key); an agent or organization key cannot.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Idempotency-Key | string | Optional | 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 | Optional | 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. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Display name. Max 120 characters. |
| description | string | Optional | Free-form description. Max 2000 characters. |
| period_start | date | Required | First day of the goal's period. |
| period_end | date | Required | Last day of the goal's period. |
| owner | uuid | null | Optional | The owner's user uuid. The person must already be able to see the board (400 participant_cannot_access_board otherwise). |
| team | uuid | null | Optional | The team. |
| status | enum | Optional | Current status. One of not_started, on_track, at_risk, off_track, achieved, missed. |
| agent_name | string | Optional | 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. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | Goal | Required | A Goal object. |
Errors
| 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 [email protected]. |
| 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`). |
| 409 | A live goal already has this name (`goal_name_conflict`). |
| 429 | Rate limit reached. Wait the number of seconds in `Retry-After`. |
curl -sS -X POST "https://api.dailybot.com/v1/plan/goals/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"name": "Q4 Roadmap",
"period_start": "2026-10-01",
"period_end": "2026-12-31",
"status": "on_track"
}'dailybot plan goal create -n "Q4 Roadmap" --period-start 2026-10-01 --period-end 2026-12-31Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
One goal, with its derived progress
Always returns progress, projects and project_count; the include parameter is not needed here.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | Goal | Required | A Goal object. |
Errors
| 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 [email protected]. |
| 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. |
| 429 | Rate limit reached. Wait the number of seconds in `Retry-After`. |
curl -sS "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/" \
-H "X-API-KEY: $DAILYBOT_API_KEY"dailybot plan goal get 00000000-0000-4000-8000-000000000006Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- 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.
Update a goal, or declare its status
Changes a goal's fields or declares its status (on_track, at_risk, …). Send only the fields you change. Every non-guest member can call it (with a login session or a personal API key); an agent or organization key cannot.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Optional | Display name. Max 120 characters. |
| description | string | Optional | Free-form description. Max 2000 characters. |
| period_start | date | Optional | First day of the goal's period. |
| period_end | date | Optional | Last day of the goal's period. |
| owner | uuid | null | Optional | The owner's user uuid. The person must already be able to see the board (400 participant_cannot_access_board otherwise). |
| team | uuid | null | Optional | The team. |
| status | enum | Optional | Current status. One of not_started, on_track, at_risk, off_track, achieved, missed. |
| agent_name | string | Optional | 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. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | Goal | Required | A Goal object. |
Errors
| 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 [email protected]. |
| 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 | A live goal already has this name (`goal_name_conflict`). |
| 429 | Rate limit reached. Wait the number of seconds in `Retry-After`. |
curl -sS -X PATCH "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "at_risk"
}'dailybot plan goal update 00000000-0000-4000-8000-000000000006 --status at_riskTry it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
Archive a goal. The projects survive, unpointed
Archives the goal. Nothing lives inside a goal, so its projects stay where they are, no longer pointing at it. Send ?dry_run=true first to see the consequence without archiving.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| dry_run | boolean | Optional | Preview the consequence without performing it. The response has the same shape, {operation, dry_run, reversible, restore_path, consequence, affects}, but nothing is written and no event is emitted. Show consequence to a person before acting. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Idempotency-Key | string | Optional | 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 | Optional | 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. |
DryRunPreview object
What the call answers with ?dry_run=true: the consequence, without performing it. Nothing is written and no event is emitted.
| Name | Type | Required | Description |
|---|---|---|---|
| operation | string | Required | The operation that would run. |
| dry_run | boolean | Required | Always true. |
| reversible | boolean | Required | Whether the operation can be undone. |
| restore_path | string | null | Required | The path that would undo it, or null when there is none. |
| consequence | string | Required | A sentence to show a person before acting. It states the cascade rather than summarising it. |
| affects | object | Required | What the operation would touch, as counts (integers) by kind. |
| would_refuse | boolean | Optional | Workflow state archive only: true when the real call would be refused. |
| refusal_code | string | Optional | Workflow state archive only: the error code the real call would answer with. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | Goal | DryRunPreview | Required | A Goal object. With ?dry_run=true, a DryRunPreview object instead. |
Errors
| 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 [email protected]. |
| 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. |
| 429 | Rate limit reached. Wait the number of seconds in `Retry-After`. |
curl -sS -X POST "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/archive/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN"dailybot plan goal archive 00000000-0000-4000-8000-000000000006 --dry-runTry it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
Bring an archived goal back
The inverse of archive/, mirroring boards/{board_id}/restore/. Restoring a goal that is already live is a 200 no-op, not an error. Goal names are unique among LIVE goals, so if the name was taken while this one was archived the restore answers 409 goal_name_conflict — the one edge that separates a real restore from a flag flip.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | Goal | Required | A Goal object. |
Errors
| 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 [email protected]. |
| 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 | A live goal already has this name (`goal_name_conflict`). |
curl -sS -X POST "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/restore/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN"dailybot plan goal restore 00000000-0000-4000-8000-000000000006Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
Link a project to a goal (from the goal page)
Links a project to the goal, from the goal's side. A project can serve several goals. The response is the goal with its projects.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| project | uuid | Required | The project. |
| agent_name | string | Optional | 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. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | Goal | Required | A Goal object. |
Errors
| 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 [email protected]. |
| 404 | Not found, or not visible to you. Both cases return the same body. |
curl -sS -X POST "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/projects/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"project": "00000000-0000-4000-8000-000000000001"
}'dailybot plan goal link 00000000-0000-4000-8000-000000000006 00000000-0000-4000-8000-000000000001Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
Unlink a project from a goal
Unlinks a project from the goal. The project itself is untouched.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
| project_id | string | Required | The project's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Errors
| 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 [email protected]. |
| 404 | Not found, or not visible to you. Both cases return the same body. |
curl -sS -X DELETE "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/projects/00000000-0000-4000-8000-000000000001/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN"dailybot plan goal unlink 00000000-0000-4000-8000-000000000006 00000000-0000-4000-8000-000000000001 --yesTry it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
List a goal's attachments
The goal's attachments, ordered by position. Anyone who can see the goal can list its attachments; a goal you cannot see is 404. Each url is a download link. Do not store it: keep the attachment uuid and read it again when you need the file. To show an image in the goal's description, reference it as attachment:{uuid} and resolve it when you render, using the fresh url from this list.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | Optional | 1-based page number. |
| page_size | integer | Optional | Rows per page. Default 50, maximum 100. Out-of-range values are clamped, never rejected: asking for 500 returns 100. |
TaskAttachment object
| Name | Type | Required | Description |
|---|---|---|---|
| uuid | uuid | Required | Stable public identifier. |
| filename | string | Required | File name. |
| content_type | string | Required | MIME type. |
| size | integer | Required | Size in bytes. |
| url | string | Required | Where to download the file. |
| thumbnail_url | uri | null | Optional | Thumbnail for images. |
| width | integer | null | Optional | — |
| height | integer | null | Optional | — |
| status | enum | Required | Current status. One of pending, ready, scanning, rejected. |
| uploaded_by | ActorRef | null | Optional | Who uploaded the file. See ActorRef. |
| executed_by_agent | object | null | Optional | 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 | date-time | Required | When the row was created. |
ActorRef object
Response
| Name | Type | Required | Description |
|---|---|---|---|
| count | integer | Required | Total number of rows. |
| next | uri | Required | URL of the next page, or null. |
| previous | uri | Required | URL of the previous page, or null. |
| results | array<TaskAttachment> | Required | The rows on this page. See TaskAttachment. |
Errors
| 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 [email protected]. |
| 404 | The goal or attachment does not exist or you cannot see it (`not_found`), never a 403. |
curl -sS "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/attachments/" \
-H "X-API-KEY: $DAILYBOT_API_KEY"dailybot plan goal attachments 00000000-0000-4000-8000-000000000006 --json{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"uuid": "00000000-0000-4000-8000-000000000009",
"filename": "screenshot.png",
"content_type": "image/png",
"size": 1,
"url": "https://your.app/files/screenshot.png",
"thumbnail_url": null,
"width": null,
"height": null,
"status": "ready",
"uploaded_by": {
"kind": "user",
"uuid": "00000000-0000-4000-8000-00000000000c",
"name": "Ada L."
},
"created_at": "2026-09-25T10:14:02Z"
}
]
}Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- 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.
Upload an attachment to a goal
Attach a file to a goal. Send multipart/form-data with the file field and an optional caption; there is no presign flow here. The limit is 5 MiB in every environment: a larger file is 400 attachment_too_large, with extra.max_size_bytes. The file type is checked from its content against the same list as task attachments (attachment_invalid_type). A goal holds at most 50 attachments (attachment_limit_reached).
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| file | binary | Required | The file to upload (max 5 MiB this way). |
| caption | string | Optional | Optional caption. Max 255 characters. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | TaskAttachment | Required | A TaskAttachment object. |
Errors
| Status | When |
|---|---|
| 400 | The file is missing, too large (`attachment_too_large`, over 5 MiB), of an unsupported type (`attachment_invalid_type`), or the limit of 50 is reached (`attachment_limit_reached`). `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 [email protected]. |
| 403 | Not a non-guest member acting with a login session or a personal API key (`insufficient_scope`); an agent or organization key always gets this. |
| 404 | The goal or attachment does not exist or you cannot see it (`not_found`), never a 403. |
curl -sS -X POST "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/attachments/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN" \
-F "file=@./screenshot.png" \
-F "caption=Staging dashboard"dailybot plan goal attach 00000000-0000-4000-8000-000000000006 ./okr-brief.pdf --caption "OKR brief"Try it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
Download a goal attachment's bytes
Streams the file with the content type recorded at upload, X-Content-Type-Options: nosniff and Cache-Control: no-store. It never redirects to storage. Anyone who can see the goal can download it.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
| attachment_id | string | Required | The attachment's uuid. |
Errors
| 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 [email protected]. |
| 404 | The goal or attachment does not exist or you cannot see it (`not_found`), never a 403. |
curl -sS "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/attachments/00000000-0000-4000-8000-000000000009/content/" \
-H "X-API-KEY: $DAILYBOT_API_KEY"dailybot plan goal attachment get 00000000-0000-4000-8000-000000000006 00000000-0000-4000-8000-000000000009 -o ./okr-brief.pdfTry it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- 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.
Remove an attachment from a goal
Removes the attachment from the goal.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
| attachment_id | string | Required | The attachment's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Errors
| 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 [email protected]. |
| 403 | Not a non-guest member acting with a login session or a personal API key (`insufficient_scope`); an agent or organization key always gets this. |
| 404 | The goal or attachment does not exist or you cannot see it (`not_found`), never a 403. |
curl -sS -X DELETE "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/attachments/00000000-0000-4000-8000-000000000009/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN"dailybot plan goal attachment delete 00000000-0000-4000-8000-000000000006 00000000-0000-4000-8000-000000000009 --yesTry it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
Rename a goal attachment
Changes the display file name; the stored bytes do not change. The rules are the container's own: organization administrators only.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| goal_id | string | Required | The goal's uuid. |
| attachment_id | uuid | Required | The attachment's uuid. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| X-Dailybot-Agent-Name | string | Optional | 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. |
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| filename | string | Required | The new file name (1–255 characters). The stored bytes do not change. |
| agent_name | string | Optional | 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. |
Response
| Name | Type | Required | Description |
|---|---|---|---|
| (body) | TaskAttachment | Required | A TaskAttachment object. |
Errors
| Status | When |
|---|---|
| 400 | Validation failed; the response `code` says which field. `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 [email protected]. |
| 403 | You are not an organization administrator (`insufficient_scope`), or you are a guest (`guest_not_allowed`). An agent or organization key is refused here too. |
| 404 | The parent or the attachment does not exist or you cannot see it (`not_found`), never a 403. |
curl -sS -X PATCH "https://api.dailybot.com/v1/plan/goals/00000000-0000-4000-8000-000000000006/attachments/00000000-0000-4000-8000-000000000009/" \
-H "Authorization: Bearer $DAILYBOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filename": "spec-v2.pdf"
}'dailybot plan goal attachments rename 00000000-0000-4000-8000-000000000006 00000000-0000-4000-8000-000000000009 spec-v2.pdfTry it
This is a copy-only helper — the request is not sent from your browser. Paste the command into your terminal to execute it.
- Scope: `tasks:admin` — container writes. A non-guest member can call it with a login session or a personal API key (a key with explicit Plan scopes needs `tasks:write`, which covers it); an agent or organization key gets `403 insufficient_scope`.
- 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`.
This page is the reference for Plan · Goals. 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, Authentication and Errors for the rules shared by every Dailybot API.
New to Plan? Read the overview for the model: projects, boards, workflow states, keys, ordering, versions and archive.