The Assistant API.
Read and write everything Assistant adds to Basecamp from your own scripts and integrations: field values, statuses, tags, saved views, automation rules and WIP limits. Plain HTTPS and JSON, with a token from the dashboard.
Start here
Open app.getassistant.io/mcp, pick the workspace, and create a token under API tokens. A token belongs to one workspace and is either read-only or read and write. Copy it when it is shown: Assistant stores only a hash and cannot show it again. Tokens work while the workspace is on its 7-day trial or subscribed.
The dashboard's MCP page shows the workspace id under API tokens, next to the tokens you created for that workspace.
Every request carries the token as a bearer header, and every path starts with the workspace:
https://api.getassistant.io/accounts/WORKSPACE_ID curl -H "Authorization: Bearer afb_YOUR_TOKEN" \
https://api.getassistant.io/accounts/WORKSPACE_ID/custom-fields curl -X POST \
-H "Authorization: Bearer afb_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"cardId": 7781234567, "projectId": 41234567, "fieldId": 12, "value": 40}' \
https://api.getassistant.io/accounts/WORKSPACE_ID/custom-fields/set-value Two kinds of ids
Projects and items use Basecamp's own ids, the numbers your integrations already get from the Basecamp API.
projectId is the Basecamp project id and cardId is the Basecamp recording id of the
to-do, card, message or document, whatever kind of item it is. Fields, options, saved views and rules use
Assistant's ids, which GET /custom-fields and the other list calls return.
Projects, to-dos, cards, people and comments stay on Basecamp's API. Assistant's API covers only what Assistant stores next to them.
Responses and errors
Every answer is JSON with success. A success carries data; a failure carries
error, a sentence you can show, and often code, which is stable to branch on.
| Status | Code | Meaning |
|---|---|---|
| 401 | API_TOKEN_INVALID | The token is wrong, expired, or revoked. |
| 403 | API_TOKEN_WRONG_WORKSPACE | The token belongs to another workspace than the one in the path. |
| 403 | API_TOKEN_READ_ONLY | A read-only token tried to change something. |
| 403 | API_TOKEN_ROUTE_FORBIDDEN | Tokens cannot reach that route. Only the routes on this page take one. |
| 403 | ACCOUNT_INACTIVE | The workspace is past its trial and not subscribed. |
| 403 | PROJECT_ACCESS_DENIED | Basecamp does not show this person that project or item. |
| 503 | PROJECT_ACCESS_UNVERIFIED | Basecamp could not be asked about access, so nothing was read or written. Try again. |
| 429 | RATE_LIMITED | Over the rate limit. The Retry-After header says how long to wait. |
The value reads (/custom-fields, project-values, account-values,
item-values) send an ETag. Send it back as If-None-Match and an unchanged
answer comes back as an empty 304.
Permissions and limits
- A token acts as the person who created it, with their role. A member's token cannot do what a member cannot do in the extension.
- It reads and writes only projects Basecamp shows that person. Clients in a project see only the items Basecamp shows them.
- 60 requests a minute per token and 300 a minute for the whole workspace. Past that, requests answer 429.
- Each person can hold up to ten live tokens per workspace and revoke any of them from the same page. A revoke takes effect within a minute.
Endpoints
Every path below goes after https://api.getassistant.io/accounts/WORKSPACE_ID.
Field values
Read and write the values on Basecamp items. Status and Tags are fields like any other: Status is a single-select field, Tags is a multi-select field. Values on single-select fields are option ids; multi-select fields take one option at a time through attach and detach.
GET /custom-fields
Every field in the workspace with its options, including the built-in Status and Tags fields. Read this once to map field and option names to ids.
Any connected user, on the projects Basecamp shows them.
Answer
{
"success": true,
"data": [
{
"id": 12,
"name": "Priority",
"type": "single_select",
"fieldKey": null,
"position": 2,
"isRequired": false,
"visibleOn": ["todos", "cards"],
"allowMemberOptionCreate": false,
"options": [
{ "id": 40, "label": "High", "color": "#dc2626", "position": 0 }
]
}
]
} GET /custom-fields/project-values?projectId=BASECAMP_PROJECT_ID
Every item in one Basecamp project that carries a value, with its values. One call per project, not per item.
Any connected user, on the projects Basecamp shows them.
Answer
{
"success": true,
"data": {
"fields": [ ... ],
"items": [
{
"bcItemId": 7781234567,
"values": [
{ "fieldId": 12, "optionId": 40, "option": { "id": 40, "label": "High", "color": "#dc2626" } },
{ "fieldId": 15, "numberValue": 8 },
{ "fieldId": 9, "selectedOptions": [ { "id": 31, "label": "urgent", "color": "#f59e0b" } ] }
]
}
]
}
} GET /custom-fields/account-values
Every value on every item across the workspace, each item tagged with its bcProjectId, plus each project's field settings. The call for a nightly sync.
Any connected user, on the projects Basecamp shows them.
GET /custom-fields/item-values?cardId=BASECAMP_ITEM_ID
The values on one Basecamp item, with the field definitions.
Any connected user, on the projects Basecamp shows them.
POST /custom-fields/set-value
Set one field on one item. Single-select fields take an option id, number fields a number, text fields a string, date fields YYYY-MM-DD, and null clears the value. An item Assistant has not seen yet is registered on the way. Automations that watch the field run after the change.
Any connected user, on the projects Basecamp shows them.
Body
{
"cardId": 7781234567,
"projectId": 41234567,
"fieldId": 12,
"value": 40,
"itemType": "todo"
} POST /custom-fields/set-values
Set up to 100 values in one request. Every entry is checked before anything is written; after that each entry answers for itself, so one rejected value does not fail the rest.
Any connected user, on the projects Basecamp shows them.
Body
{
"projectId": 41234567,
"values": [
{ "cardId": 7781234567, "fieldId": 12, "value": 40 },
{ "cardId": 7781234568, "fieldId": 15, "value": 8 }
]
} Answer
{
"success": true,
"data": {
"results": [
{
"cardId": 7781234567, "fieldId": 12,
"ok": true, "status": 200, "data": { ... }
},
{
"cardId": 7781234568, "fieldId": 15,
"ok": false, "status": 400, "error": "..."
}
],
"succeeded": 1,
"failed": 1
}
} POST /custom-fields/clear-value
Remove the value of one single-value field from one item.
Any connected user, on the projects Basecamp shows them.
Body
{ "cardId": 7781234567, "fieldId": 12 } POST /custom-fields/attach-option
Add one option of a multi-select field, such as a tag, to one item.
Any connected user, on the projects Basecamp shows them.
Body
{ "cardId": 7781234567, "projectId": 41234567, "fieldId": 9, "optionId": 31 } POST /custom-fields/detach-option
Remove one option of a multi-select field from one item.
Any connected user, on the projects Basecamp shows them.
Body
{ "cardId": 7781234567, "projectId": 41234567, "fieldId": 9, "optionId": 31 } Fields and options
Create and change the fields themselves. The built-in Status and Tags fields cannot be renamed or deleted, and a field's type never changes.
POST /custom-fields
Create a field. Types: single_select, multi_select, number, text, date. Leave visibleOn out to show the field on every item kind.
Workspace admins only.
Body
{
"name": "Priority",
"type": "single_select",
"isRequired": false,
"visibleOn": ["todos", "cards"]
} PUT /custom-fields/FIELD_ID
Rename a field, move it, change where it shows, or let members add options to it (allowMemberOptionCreate).
Workspace admins only.
DELETE /custom-fields/FIELD_ID
Delete a field and every value stored for it.
Workspace admins only.
POST /custom-fields/FIELD_ID/options
Add an option to a select field: a status, a tag, a choice. isCompletionStatus marks the status that means done.
Admins, and members on fields that allow member-created options.
Body
{ "label": "In review", "color": "#2563eb", "isCompletionStatus": false } PUT /custom-fields/FIELD_ID/options/OPTION_ID
Rename, recolor or move an option, or mark it default or done.
Admins. Members may change only the color, on fields that allow member-created options.
DELETE /custom-fields/FIELD_ID/options/OPTION_ID
Delete an option and remove it from every item.
Workspace admins only.
GET /custom-fields/project-settings?projectId=BASECAMP_PROJECT_ID
Whether fields are on in one project, and which fields are hidden there.
Any connected user, on the projects Basecamp shows them.
PUT /custom-fields/project-settings
Turn fields on or off in one project and choose which stay hidden.
Workspace admins only.
Body
{ "projectId": 41234567, "enabled": true, "hiddenFieldIds": [15] } Saved board views
Saved Kanban views belong to one person. A token reads and writes only the views of the user who created it.
GET /saved-views
The token user's saved views.
Any connected user, on the projects Basecamp shows them.
POST /saved-views
Save a view: what kind of items it shows, which projects it covers, how it groups, and its filters.
Any connected user, on the projects Basecamp shows them.
Body
{
"name": "Client work by status",
"kind": "cards",
"scope": { "type": "projects", "projectIds": [41234567, 41234568] }
} PUT /saved-views/VIEW_ID
Change a view's name, scope, grouping, filters or position.
Any connected user, on the projects Basecamp shows them.
DELETE /saved-views/VIEW_ID
Delete a view.
Any connected user, on the projects Basecamp shows them.
Automation rules
A rule runs when a Basecamp event happens (triggerType webhook_event with a webhookEventKind such as todo_created or kanban_card_changed) or when a field changes to a value (triggerType field_changed with triggerFieldId and an optional triggerOptionId). Actions run in Basecamp as the person who created the rule. Action types: set_field, attach_option, detach_option, complete_todo, uncomplete_todo, post_comment, assign_people, move_card.
GET /automations
Every rule in the workspace with its trigger and actions.
Workspace admins only.
GET /automations/RULE_ID
One rule with its actions.
Workspace admins only.
POST /automations
Create a rule. projectFilter limits it to some Basecamp projects; null means all.
Workspace admins only.
Body
{
"name": "Done means complete",
"triggerType": "field_changed",
"triggerFieldId": 3,
"triggerOptionId": 22,
"projectFilter": null,
"actions": [
{ "actionType": "complete_todo", "actionConfig": {} },
{ "actionType": "post_comment", "actionConfig": { "content": "Closed from status: {{title}}" } }
]
} PUT /automations/RULE_ID
Change a rule's name, trigger, project filter or actions.
Workspace admins only.
PATCH /automations/RULE_ID/toggle
Switch a rule between on and off.
Workspace admins only.
DELETE /automations/RULE_ID
Delete a rule and its actions.
Workspace admins only.
GET /automations/logs/recent?limit=50
The latest runs: which rule fired on which item, and whether it worked. limit goes up to 100.
Workspace admins only.
WIP limits
A WIP limit caps the cards in one board column. The scope names the board: "account", "project:PROJECT_ID" or "project:PROJECT_ID:tool:TOOL_ID". Column keys look like gfield:FIELD_ID:OPTION_ID for a field-value column or gassignee:PERSON_ID for a person.
GET /wip-limits?scope=project:41234567
The limits set on the columns of one board.
Any connected user, on the projects Basecamp shows them.
PUT /wip-limits
Set or clear the limit on one column. null clears it.
Admins. Members on field-value columns whose field allows member-created options.
Body
{ "scopeKey": "project:41234567", "columnKey": "gfield:3:22", "limit": 5 } Using an AI agent instead?
The same routes sit behind an MCP server, so Claude, ChatGPT, Codex or Cursor can work with your fields by name instead of by id. The MCP setup guide has the commands for each client.
Add Kanban and tags to your Basecamp.
Install the free extension. 7-day trial on all paid features. No card needed.