Basecamp Workflow
Manage Basecamp projects, todos, messages, files, and team coordination.
Installation
- Make sure Claude is on your device and in your terminal.
Skills load from
~/.claude/skills/when Claude Code starts up — so you need it on your machine first. If you don't have it yet, install it once with the command below, then runclaudein any terminal to verify.One-time setupnpm i -g @anthropic-ai/claude-codeAlready have it? Skip ahead.
- Paste into Claude Code or into your terminal.
This copies the whole skill folder into
~/.claude/skills/basecamp-basecamp/— the SKILL.md plus any scripts, reference docs, or templates the skill ships with. Safe default: works for every skill.Faster alternative (instruction-only skills)
Skips the clone and grabs only the SKILL.md file. Don't use this if the skill ships Python scripts, reference markdowns, or asset templates — they won't be downloaded and the skill will fail when it tries to load them.
Quick install (SKILL.md only)Sign up to copy - Restart Claude Code.
Quit and reopen Claude Code (or any other agent that loads from
~/.claude/skills/). New skills are picked up on startup. - Just ask Claude.
Skills auto-activate when your request matches the skill's description — no slash command needed. Trigger phrases live in the skill's own frontmatter; you can read them in the “What this skill does” section above.
Prefer to read the source first? Open on GitHub.
When Claude uses it
Interact with Basecamp via the Basecamp CLI. Full API coverage: projects, todos, cards, messages, files, schedule, check-ins, timeline, recordings, templates, webhooks, subscriptions, lineup, chat, gauges, assignments, notifications, and accounts. Use for ANY Basecamp question or action.
What this skill does
/basecamp - Basecamp Workflow Command
Full CLI coverage: 155 endpoints across todos, cards, messages, files, schedule, check-ins, timeline, recordings, templates, webhooks, subscriptions, lineup, chat, gauges, assignments, notifications, and accounts.
Agent Invariants
MUST follow these rules:
- Choose the right output mode —
--jqwhen you need to filter/extract data;--jsonfor full JSON;--mdwhen presenting results to a human (see Output Modes below). Never pipe to externaljq— use--jqinstead. - Parse URLs first with
basecamp url parse "<url>"to extract IDs - Comments are flat - reply to parent recording, not to comments
- Check context via
.basecamp/config.jsonbefore assuming project - Content fields accept Markdown and @mentions — message body and comment content accept Markdown syntax; the CLI converts to HTML automatically. Use Markdown formatting (lists, bold, links, code blocks) for rich content. Four mention syntaxes are available (prefer deterministic for agents):
[@Name](mention:SGID)— zero API calls, embeds SGID directly (preferred for agents)[@Name](person:ID)— one API call, resolves person ID to SGID via pingable set@sgid:VALUE— inline SGID embed for pipeline composability@Name/@First.Last— fuzzy name resolution (may be ambiguous) For todos, documents, and cards, content is sent as-is — use plain text or HTML directly.
- Project scope is mandatory for most commands — via
--in <project>or.basecamp/config.json. Cross-project exceptions:basecamp reports assignedfor assigned work,basecamp assignmentsfor structured assignment views,basecamp reports overduefor overdue todos,basecamp reports schedulefor upcoming schedule across all projects,basecamp recordings <type>for browsing by type,basecamp notificationsfor notifications,basecamp gauges listfor account-wide gauges.
Output Modes
Choosing a mode:
| Goal | Flag | Format |
|---|---|---|
| Filter/extract JSON data | --jq '<expr>' | Built-in jq filter (no external jq needed). Implies --json; filter runs on the envelope. |
| Filter in agent mode | --agent --jq '<expr>' | Filter runs on data-only payload (no envelope), matching --agent contract. |
| Full JSON output | --json | JSON envelope: {ok, data, summary, breadcrumbs, meta} |
| Show results to a user | --md / -m | GFM tables, task lists, structured Markdown |
| Automation / scripting | --agent | Success: raw JSON data (no envelope); errors: {ok:false,...} object; no interactive prompts |
Always pass --json or --md explicitly — auto-detection depends on config and may not produce the format you expect. Use --md when composing reports, summarizing data, or displaying results inline. --agent is for headless integration scripts.
Other modes: --quiet (success: raw JSON, no envelope; errors: {ok:false,...}), --ids-only, --count, --stats (session statistics), --styled (force ANSI), -v / -vv (verbose/trace), --jq '<expr>' (built-in jq filter — see below).
CLI Introspection
Navigate unfamiliar commands with --agent --help — returns structured JSON describing any command:
basecamp todos --agent --help
{"command":"todos","path":"basecamp todos","short":"...","long":"...","usage":"...","notes":["..."],
"subcommands":[{"name":"sweep","short":"...","path":"basecamp todos sweep"}],
"flags":[{"name":"assignee","type":"string","default":"","usage":"..."}],
"inherited_flags":[{"name":"json","shorthand":"j","type":"bool","default":"false","usage":"..."}]}
Walk the tree: start at basecamp --agent --help for top-level commands, then drill into any subcommand. Commands include notes with domain-specific agent hints (e.g., "Cards do NOT support --assignee filtering").
Pagination
basecamp <cmd> --limit 50 # Cap results (default varies by resource)
basecamp <cmd> --all # Fetch all (may be slow for large datasets)
basecamp <cmd> --page 1 # First page only, no auto-pagination
--all and --limit are mutually exclusive. --page cannot combine with either.
Smart Defaults
--assignee meresolves to current user--due tomorrow/--due +3/--due "next week"- natural date parsing- Project from
.basecamp/config.jsonif--innot specified - Multiple identities use named profiles:
basecamp profile create <name>, then select one with global--profile <name>orBASECAMP_PROFILE=<name>.
Quick Reference
Note: Most queries require project scope (via
--in <project>or.basecamp/config.json). Cross-project exceptions:basecamp reports assigned,basecamp assignments,basecamp reports overdue,basecamp reports schedule,basecamp recordings <type>,basecamp notifications,basecamp gauges list.
| Task | Command |
|---|---|
| List projects | basecamp projects list --json |
| My todos (in project) | basecamp todos list --assignee me --in <project> --json |
| My todos (cross-project) | basecamp reports assigned --json (defaults to "me") |
| My schedule (cross-project) | basecamp reports schedule --json (upcoming events across all projects) |
| All todos (cross-project) | basecamp recordings todos --json (no assignee data — cannot filter by person) |
| Overdue todos (in project) | basecamp todos list --overdue --in <project> --json |
| Overdue todos (cross-project) | basecamp reports overdue --json |
| Assign todo | basecamp assign <id> [id...] --to <person> --in <project> --json |
| Assign card | basecamp assign <id> [id...] --card --to <person> --in <project> --json |
| Assign card step | basecamp assign <id> [id...] --step --to <person> --in <project> --json |
| Create todo | basecamp todos create "Task" --in <project> --list <list> --json |
| Create todolist | basecamp todolists create "Name" --in <project> --json |
| Complete todo | basecamp todos complete <id> --json |
| List cards | basecamp cards list --in <project> --json |
| Create card | basecamp cards create "Title" --in <project> --json |
| Move card | basecamp cards move <id> --to <column> [--position N] --in <project> --json |
| Move card to on-hold | basecamp cards move <id> --on-hold --in <project> --json |
| Post message | basecamp messages create "Title" "Body" --in <project> --json |
| Post with @mention | basecamp messages create "Title" "Hey @First.Last, ..." --in <project> --json |
| Post silently | basecamp messages create "Title" "Body" --no-subscribe --in <project> --json |
| Post to chat | basecamp chat post "Message" --in <project> --json |
| Add comment | basecamp comments create <recording_id> "Text" --in <project> --json |
| List attachments | basecamp attachments list <id|url> --json |
| Download attachments | basecamp attachments download <id> --out /tmp/ |
| Show + download | basecamp todos show <id> --download-attachments --json |
| Stream attachment to stdout | basecamp attachments download <id> --file <name> --out - |
| Search | basecamp search "query" --json |
| Parse URL | basecamp url parse "<url>" --json |
| Upload file | basecamp files uploads create <file> [--vault <folder_id>] --in <project> --json |
| Download file | basecamp files download <id> --in <project> |
| Stream file to stdout | basecamp files download <id> --out - --in <project> |
| Download storage URL | basecamp files download "https://storage.3.basecamp.com/.../download/report.pdf" |
| My assignments | basecamp assignments --json (priorities + non-priorities) |
| Overdue assignments | basecamp assignments due overdue --json |
| Completed assignments | basecamp assignments completed --json |
| Notifications | basecamp notifications --json |
| Mark notification read | basecamp notifications read <id> --json |
| Gauges (account-wide) | basecamp gauges list --json |
| Gauge needles | basecamp gauges needles --in <project> --json |
| Create needle | basecamp gauges create --position 75 --color green --in <project> --json |
| Account details | basecamp accounts show --json |
| Watch timeline | basecamp timeline --watch |
URL Parsing
Always parse URLs before acting on them:
basecamp url parse "https://3.basecamp.com/2914079/buckets/41746046/messages/9478142982#__recording_9488783598" --json
Returns: account_id, project_id, type, recording_id, comment_id (from fragment).
URL patterns:
/buckets/27/messages/123- Message 123 in project 27/buckets/27/messages/123#__recording_456- Comment 456 on message 123/buckets/27/card_tables/cards/789- Card 789/buckets/27/card_tables/columns/456- Column 456 (for creating cards)/buckets/27/todos/101- Todo 101/buckets/27/uploads/202- Upload/file 202/buckets/27/documents/303- Document 303/buckets/27/schedule_entries/404- Schedule entry 404
Replying to comments:
# Comments are flat - reply to the parent recording_id, not the comment_id
basecamp url parse "https://...messages/123#__recording_456" --json
# Returns recording_id: 123 (parent), comment_id: 456 (fragment) - comment on 123, not 456
basecamp comments create 123 "Reply" --in <project>
Decision Trees
Finding Content
Need to find something?
├── Know the type + project? → basecamp <type> list --in <project> --json
│ (some groups have default list behavior; use --agent --help if unsure)
├── My assigned work? → basecamp assignments --json (priorities + non-priorities)
│ Or: basecamp reports assigned --json (traditional view, defaults to "me")
├── My overdue assignments? → basecamp assignments due overdue --json
├── My notifications? → basecamp notifications --json
├── Upcoming schedule? → basecamp reports schedule --json (cross-project)
├── Overdue across projects? → basecamp reports overdue --json
├── Browse by type cross-project? → basecamp recordings <type> --json
│ (types: todos, messages, documents, comments, cards, uploads)
│ Note: Defaults to active status; use --status archived for archived items
│ ⚠ No assignee data — cannot filter by person; use reports assigned instead
├── Full-text search? → basecamp search "query" --json
└── Have a URL? → basecamp url parse "<url>" --json
Modifying Content
Want to change something?
├── Have URL? → basecamp url parse "<url>" → use extracted IDs
├── Have ID? → basecamp <resource> update <id> --field value
├── Change status? → basecamp recordings trash|archive|restore <id>
└── Complete todo? → basecamp todos complete <id>
Common Workflows
Link Code to Basecamp Todo
# Get commit info and comment on todo (use printf %q for safe quoting)
COMMIT=$(git rev-parse --short HEAD)
MSG=$(git log -1 --format=%s)
basecamp comments create <todo_id> "Commit $COMMIT: $(printf '%s' "$MSG")" --in <project>
# Complete when done
basecamp todos complete <todo_id>
Track PR in Basecamp
# Create todo for PR work
basecamp todos create "Review PR #42" --in <project> --assignee me --due tomorrow
# When merged
basecamp todos complete <todo_id>
basecamp chat post "Merged PR #42" --in <project>
Bulk Process Overdue Todos
# Preview overdue todos
basecamp todos sweep --overdue --dry-run --in <project>
# Complete all with comment
basecamp todos sweep --overdue --complete --comment "Cleaning up" --in <project>
Mentioning people (preferred — deterministic)
# 1. Look up the person
basecamp people pingable --jq '.data[] | select(.name == "Jane Smith")'
# => {"id": 42000, "attachable_sgid": "BAh7CEkiCG...", "name": "Jane Smith"}
# 2. Use SGID in Markdown mention syntax (zero API calls during post)
basecamp comments create 123 "Hey [@Jane Smith](mention:BAh7CEkiCG...), check this" --in <project>
# Or use person ID (one lookup during post)
basecamp comments create 123 "Hey [@Jane Smith](person:42000), check this" --in <project>
Mentioning people (interactive — may be ambiguous)
# Fuzzy matching: use @First.Last to reduce ambiguity
basecamp comments create <id> "@Jane.Smith, please review this" --in <project>
basecamp messages create "Update" "cc @Jane, @Alex" --in <project>
basecamp chat post "@Jane, done!" --in <project>
# Ambiguous names return an error with suggestions
# Use @First.Last for disambiguation
Move Card Through Workflow
# List columns to get IDs
basecamp cards columns --in <project> --json
# Move card to column
basecamp cards move <card_id> --to <column_id> --in <project>
# Move card to specific position in column (1-indexed)
basecamp cards move <card_id> --to <column_id> --position 1 --in <project>
# Move card to on-hold section of its current column
basecamp cards move <card_id> --on-hold --in <project>
# Move card to on-hold section of a specific column (numeric ID)
basecamp cards move <card_id> --to <column_id> --on-hold --in <project>
# Move card to on-hold section of a named column (requires --card-table)
basecamp cards move <card_id> --to "Column Name" --on-hold --card-table <table_id> --in <project>
Download File from Basecamp
basecamp files download <upload_id> --in <project> --out ./downloads
# Download attachment from a storage URL (no --in needed)
basecamp files download "https://storage.3.basecamp.com/123/blobs/abc/download/report.pdf"
# Stream to stdout (for piping)
basecamp files download <upload_id> --out - --in <project>
Working with Attachments (Multimodal Agent Workflow)
Messages, todos, cards, and documents may contain images and file attachments
(mockups, screenshots, annotated designs). Show commands surface these as
field-scoped collections — content_attachments and/or description_attachments
— keyed by which rich-text attribute contained them. The notice field hints at
the download command.
Step 1: Fetch the recording and check for attachments
basecamp todos show <id> --json
# Response includes description_attachments when attachments are present
# Messages/documents use content_attachments; cards may have both
# The notice field hints: "3 attachment(s) — download: basecamp attachments download <id>"
Step 2 (one-shot): Download attachments with the show command
# --download-attachments fetches + downloads in one shot
basecamp todos show <id> --download-attachments --json
# content_attachments/description_attachments entries now include "path" pointing to local files
# Downloads to OS temp dir by default, or specify: --download-attachments /tmp/att
Step 2 (two-step alternative): Download separately
# Download all at once (shows progress on stderr)
basecamp attachments download <id> --out /tmp/attachments
Step 3: View images with your native file-read tool
For multimodal LLMs (Claude, Gemini), use your file-read tool on the path
from the response to view downloaded images directly — no browser needed.
This surfaces visual context (mockups, screenshots, annotated designs) that
is often the most important part of a Basecamp todo or message.
# Stream a single image to stdout for piping
basecamp attachments download <id> --file mockup.png --out -
# Select by index when names collide
basecamp attachments download <id> --index 2 --out -
Key pattern: When a show command response contains content_attachments
or description_attachments, always download and view them — visual context is
often more important than the text content. Use --download-attachments for
one-shot fetch+download, or follow the breadcrumb hint for two-step control.
Resource Reference
Projects
basecamp projects list --json # List all
basecamp projects show <id> --json # Show details
basecamp projects create "Name" --json # Create
basecamp projects update <id> --name "New" # Update
basecamp projects trash <id> # Move to trash (recoverable)
Archiving a project: the CLI does not have a dedicated archive command, but the underlying status endpoint can be hit via raw API. Same path works for restoring to active or moving to trashed.
basecamp api put "projects/<id>/status/archived" -d '{}' --json # Archive
basecamp api put "projects/<id>/status/active" -d '{}' --json # Unarchive
basecamp api put "projects/<id>/status/trashed" -d '{}' --json # Trash (same as `projects trash`)
Verify with basecamp projects show <id> --jq '.data.status'.
Todos
basecamp todos list --in <project> --json # List in project
basecamp todos list --assignee me --in <project> # My todos
basecamp todos list --overdue --in <project> # Overdue only
basecamp todos list --status completed --in <project> # Completed
basecamp todos list --list <todolist_id> --in <project> # In specific list
basecamp todos create "Task" --in <project> --list <list> --assignee me --due tomorrow
basecamp todos complete <id> [id...] # Complete (multiple OK)
basecamp todos uncomplete <id> # Reopen
basecamp assign <id> [id...] --to <person> --in <project> # Assign to-do (multiple OK)
basecamp unassign <id> [id...] --from <person> --in <project> # Remove to-do assignee (multiple OK)
basecamp assign <id> [id...] --card --to <person> --in <project> # Assign card
basecamp unassign <id> [id...] --card --from <person> --in <project> # Remove card assignee
basecamp assign <id> [id...] --step --to <person> --in <project> # Assign card step
basecamp unassign <id> [id...] --step --from <person> --in <project> # Remove step assignee
basecamp todos position <id> --to 1 # Move to top
basecamp todos position <id> --to 1 --list <id|name|url> # Move to different list
basecamp todos sweep --overdue --complete --comment "Done" --in <project>
Flags: --assignee (todos only - not available on cards/messages), --status (completed/incomplete/archived/trashed), --overdue, --list, --due, --limit, --all
Todo Subtasks (checklist steps): Basecamp to-do subtasks are stored as
Kanban::Step records, even when their parent is a normal Todo. The regular
basecamp todos show response may not include them; use
basecamp recordings list --in <project> --type Kanban::Step and filter by
parent.id to list/check subtasks for a todo.
# Create a subtask under a todo.
# Use the numeric project ID and todo ID in this card-style path.
basecamp api post /buckets/<project_id>/card_tables/cards/<parent_todo_id>/steps.json \
--data '{"title":"Subtask title"}' \
--json
# Read or edit a subtask
basecamp api get /buckets/<project_id>/card_tables/steps/<step_id>.json --json
basecamp api put /buckets/<project_id>/card_tables/steps/<step_id>.json \
--data '{"title":"Updated subtask title"}' \
--json
# List subtasks for a todo
PARENT_TODO_ID=<parent_todo_id> \
basecamp recordings list --in <project> --type Kanban::Step --all \
--jq '.data[] | select(.parent.id==(env.PARENT_TODO_ID | tonumber)) | {id,title,status,parent:.parent.id,url}'
# Assign or set a due date.
# Include the current title and every person who should remain assigned.
basecamp api put /buckets/<project_id>/card_tables/steps/<step_id>.json \
--data '{"title":"Current subtask title","assignee_ids":[<person_id>,<existing_person_id>],"due_on":"<YYYY-MM-DD>"}' \
--json
# Complete or reopen a subtask
basecamp api put /buckets/<project_id>/card_tables/steps/<step_id>/completions.json \
--data '{"completion":"on"}' \
--json
basecamp api put /buckets/<project_id>/card_tables/steps/<step_id>/completions.json \
--data '{"completion":"off"}' \
--json
# Trash a subtask from the todo UI by trashing the step record (Kanban::Step)
basecamp recordings trash <step_id> --in <project> --json
Key points: replace numeric placeholders such as <project_id>,
<parent_todo_id>, and <person_id> before running the examples. Bucket-scoped
API paths require a numeric project/bucket ID; --in <project> can still accept
a project name where CLI commands support name resolution. For creating todo
subtasks, Basecamp accepts the parent todo ID in the
/buckets/<project_id>/card_tables/cards/<parent_todo_id>/steps.json path. To
list subtasks under a todo, use
basecamp recordings list --in <project> --type Kanban::Step with the
parent.id filter shown above.
Completed subtasks have completed: true and a completion object with
created_at and creator. Open subtasks have completed: false and no
completion object. Trashed subtasks may still be readable directly with
status: "trashed" and inherits_status: false, but they no longer appear in
the todo UI.
In testing with todo-backed steps, these bucket-scoped direct GET requests
returned not_found:
/buckets/<project_id>/card_tables/cards/<parent_todo_id>/steps.json,
/buckets/<project_id>/card_tables/cards/<parent_todo_id>.json, and
/buckets/<project_id>/todos/<parent_todo_id>/steps.json. To inspect trashed
subtasks, add --status trashed; archived parents may require
--status archived.
When updating a todo subtask with the raw API, include the existing title along
with metadata changes; omitting it may reset the step title to Untitled.
assignee_ids sets the full assignee list for the step, so include every person
who should remain assigned. The generic
basecamp assign <step_id> --step ... command is intended for card steps and
may fail with Bad Request for todo-backed steps, so prefer assignee_ids on
the raw step update endpoint for todo subtasks.
Todolists
Todolists are containers for todos. Create a todolist before adding todos.
basecamp todolists list --in <project> --json # List todolists
basecamp todolists show <id> --in <project> # Show details
basecamp todolists create "Name" --in <project> --json # Create
basecamp todolists create "Name" --description "Desc" --in <project>
basecamp todolists update <id> --name "New" --in <project> # Update
Cards (Kanban)
Note: Cards do NOT support --assignee filtering like todos. Fetch all cards and filter client-side if needed. If a project has multiple card tables, you must specify --card-table <id>. When you get an "Ambiguous card table" error, the hint shows available table IDs and names.
basecamp cards list --in <project> --json # All cards
basecamp cards list --card-table <id> --in <project> # Specific table (required if multiple)
basecamp cards list --column <id> --in <project> # Cards in column
basecamp cards columns --in <project> --json # List columns (needs --card-table if multiple)
basecamp cards show <id> --in <project> # Card details
basecamp cards create "Title" "<p>Body</p>" --in <project> --column <id>
basecamp cards update <id> --title "New" --due tomorrow --assignee me
basecamp cards move <id> --to <column_id> # Move to column (numeric ID)
basecamp cards move <id> --to "Done" --card-table <table_id> # Move by name (needs table)
basecamp cards move <id> --to "Done" --position 1 --card-table <table_id> # Move to position
basecamp cards move <id> --on-hold # Move to on-hold of current column
basecamp cards move <id> --to <column_id> --on-hold # Move to on-hold of target column
Archived/trashed cards: cards list only returns active cards. For archived or trashed cards, use basecamp recordings cards --status archived --in <project> or --status trashed.
Identifying completed cards: Cards in Done columns have parent.type: "Kanban::DoneColumn" and completed: true. Use this to identify completed cards that haven't been archived.
Limitation: Basecamp does not track when cards are moved between columns. The updated_at field updates on any modification and cannot reliably indicate when a card was completed.
Card Steps (checklists):
basecamp cards steps <card_id> --in <project> # List steps
basecamp cards step create "Step" --card <id> --in <project>
basecamp cards step complete <step_id> --in <project>
basecamp cards step uncomplete <step_id>
Column management:
basecamp cards column show <id> --in <project>
basecamp cards column create "Name" --in <project>
basecamp cards column update <id> --title "New"
basecamp cards column move <id> --position 2
basecamp cards column color <id> --color blue
basecamp cards column on-hold <id> # Enable on-hold section
basecamp cards column watch <id> # Subscribe to column
Messages
basecamp messages list --in <project> --json # List messages
basecamp messages show <id> --in <project> # Show message
basecamp messages create "Title" "Body" --in <project>
basecamp messages create "Draft" "WIP" --draft --in <project> # Create draft
basecamp messages publish <id> # Publish a draft
basecamp messages update <id> --title "New" --body "Updated"
basecamp messages pin <id> --in <project> # Pin to top
basecamp messages unpin <id> # Unpin
Archived/trashed messages: messages list only returns active messages. For archived or trashed messages, use basecamp recordings messages --status archived --in <project> or --status trashed.
Flags: --draft (create as draft), --no-subscribe (silent, no notifications), --subscribe "people" (comma-separated names, emails, IDs, or "me"; mutually exclusive with --no-subscribe), --message-board <id> (if multiple boards)
basecamp messages create "Bot update" "Done" --no-subscribe --in <project>
basecamp messages create "FYI" "Note" --subscribe "Alice,bob@x.com" --in <project>
Comments
basecamp comments list <recording_id> --in <project> --json
basecamp comments create <recording_id> "Text" --in <project>
basecamp comments create <recording_id> "@Jane.Smith, looks good!" --in <project> # With @mention
basecamp comments update <id> "Updated" --in <project>
Files & Documents
basecamp files list --in <project> --json # List all (folders, files, docs)
basecamp files list --vault <folder_id> --in <project> # List folder contents
basecamp files show <id> --in <project> # Show item (auto-detects type)
basecamp files download <id> --in <project> # Download file
basecamp files download <id> --out ./dir # Download to specific dir
basecamp files download "https://storage.../download/f" # Download from storage URL
basecamp files uploads create <file> --in <project> # Upload file to root
basecamp files uploads create <file> --vault <folder_id> --in <project> # Upload to folder
basecamp files folder create "Folder" --in <project>
basecamp files doc create "Doc" "Body" --in <project>
basecamp files doc create "Draft" --draft --in <project>
basecamp files doc create "Notes" "..." --no-subscribe --in <project>
basecamp files update <document_id> --title "New" --content "Updated"
basecamp files update <document_id> --title "New" --in <project> # Preserves existing document content
basecamp files update <document_id> --content "Updated" --in <project> # Preserves existing document title
Document update semantics: basecamp files update <document_id> is safe for partial updates in the CLI: when you pass only --title or only --content, the CLI first fetches the current document and preserves the untouched field.
Subcommands: folders, uploads, documents (each with pagination flags)
Schedule
For upcoming events across all projects, use basecamp reports schedule --json.
basecamp schedule info --in <project> --json # Schedule info
basecamp schedule entries --in <project> --json # List entries
basecamp schedule show <id> --in <project> # Entry details
basecamp schedule show <id> --date 20240315 # Specific occurrence (recurring)
basecamp schedule create "Event" --starts-at "2024-03-15T09:00:00Z" --ends-at "2024-03-15T10:00:00Z" --in <project>
basecamp schedule create "Meeting" --all-day --notify --participants 1,2,3 --in <project>
basecamp schedule create "Sync" --starts-at "..." --ends-at "..." --no-subscribe --in <project>
basecamp schedule update <id> --summary "New title" --starts-at "..."
basecamp schedule settings --include-due --in <project> # Include todos/cards due dates
Flags: --all-day, --notify, --participants <ids>, --no-subscribe, --subscribe "people" (mutually exclusive), --status (active/archived/trashed)
Check-ins
basecamp checkins --in <project> --json # Questionnaire info
basecamp checkins questions --in <project> # List questions
basecamp checkins question <id> --in <project> # Question details
basecamp checkins answers <question_id> --in <project> # List answers
basecamp checkins answer <id> --in <project> # Answer details
basecamp checkins question create "What did you work on?" --in <project>
basecamp checkins question update <id> "New question" --frequency every_week
basecamp checkins answer create <question-id> "My answer" --in <project> # Defaults to today
basecamp checkins answer update <id> "Updated" --in <project>
Schedule options: --frequency (every_day, every_week, every_other_week, every_month, on_certain_days), --days 1,2,3,4,5 (0=Sun), --time "5:00pm"
Timeline
basecamp timeline --json # Account-wide activity
basecamp timeline --in <project> --json # Project activity
basecamp timeline me --json # Your activity
basecamp timeline --person <id> --json # Person's activity
basecamp timeline --watch # Live monitoring (TUI)
basecamp timeline --watch --interval 60 # Poll every 60 seconds
Use --limit N to cap results or --all to fetch everything (default: 100 events). --all and --page cannot be combined with --watch.
Recordings (Cross-project)
Use basecamp recordings <type> for cross-project type browsing. For assigned todos, prefer basecamp reports assigned — recordings do not include assignee data and cannot be filtered by person.
basecamp recordings todos --json # All todos across projects
basecamp recordings todos --all --json # All todos (paginate through all)
basecamp recordings messages --in <project> # Messages in project
basecamp recordings documents --status archived # Archived docs
basecamp recordings cards --sort created_at --direction asc
basecamp recordings cards --status archived --all --json # Include archived cards
Types: todos, messages, documents, comments, cards, uploads
Status filtering: By default, only active recordings are returned. Use --status archived or --status trashed to query other statuses. You may need separate queries to get complete data (e.g., active + archived).
Status management:
basecamp recordings trash <id> --in <project> # Move to trash
basecamp recordings archive <id> --in <project> # Archive
basecamp recordings restore <id> --in <project> # Restore to active
basecamp recordings visibility <id> --visible --in <project> # Show to clients
basecamp recordings visibility <id> --hidden # Hide from clients
Templates
basecamp templates --json # List templates
basecamp templates show <id> --json # Template details
basecamp templates create "Template Name" # Create empty template
basecamp templates update <id> --name "New Name"
basecamp templates delete <id> # Trash template
basecamp templates construct <id> --name "New Project" # Create project (async)
basecamp templates construction <template_id> <construction_id> # Check status
Construct returns construction_id - poll until status="completed" to get project.
Webhooks
basecamp webhooks list --in <project> --json # List webhooks
basecamp webhooks show <id> --in <project> # Webhook details
basecamp webhooks create "https://..." --in <project>
basecamp webhooks create "https://..." --types "Todo,Comment" --in <project>
basecamp webhooks update <id> --active --in <project>
basecamp webhooks update <id> --inactive # Disable
basecamp webhooks delete <id> --in <project>
Event types: Todo, Todolist, Message, Comment, Document, Upload, Vault, Schedule::Entry, Kanban::Card, Question, Question::Answer
Subscriptions
basecamp subscriptions <recording_id> # Who's subscribed
basecamp subscriptions subscribe <id> # Subscribe yourself
basecamp subscriptions unsubscribe <id> # Unsubscribe
basecamp subscriptions add <id> --people 1,2,3 # Add people
basecamp subscriptions remove <id> --people 1,2,3 # Remove people
Lineup (Account-wide Markers)
basecamp lineup list # List all markers
basecamp lineup create "Milestone" "2024-03-15" # Create marker
basecamp lineup create "Launch" tomorrow # Natural date parsing
basecamp lineup update <id> "New Name" "+7"
basecamp lineup delete <id>
Note: Lineup markers are account-wide, not project-scoped.
Gauges
Gauges track project progress with colored needles on a 0-100 scale.
basecamp gauges list --json # All gauges (account-wide)
basecamp gauges needles --in <project> --json # Needles for a project
basecamp gauges needle <id> --json # Needle details
basecamp gauges create --position 75 --color green --in <project>
basecamp gauges create --position 50 --color yellow --description "Halfway" --in <project>
basecamp gauges create --position 25 --notify custom --subscriptions 1,2 --in <project>
basecamp gauges update <id> --description "Updated"
basecamp gauges delete <id>
basecamp gauges enable --in <project> # Enable gauge on project
basecamp gauges disable --in <project> # Disable gauge
Colors: green, yellow, red. Notify: everyone, working_on, custom (with --subscriptions).
Assignments
View your assignments across all projects. Separate from reports assigned — provides structured priority grouping and due-date scoping.
basecamp assignments --json # All (priorities + non-priorities)
basecamp assignments list --json # Same as bare
basecamp assignments completed --json # Completed assignments
basecamp assignments due overdue --json # Overdue
basecamp assignments due due_today --json # Due today
basecamp assignments due due_tomorrow --json # Due tomorrow
basecamp assignments due due_later_this_week --json # Due later this week
Scopes: overdue, due_today, due_tomorrow, due_later_this_week, due_next_week, due_later.
Notifications
basecamp notifications --json # List (page 1)
basecamp notifications list --page 2 --json # Page 2
basecamp notifications read <id> --json # Mark as read
basecamp notifications read <id> <id> --page 2 --json # Mark from page 2
Note: read resolves notification IDs from the specified page. Use --page to match the page you listed.
Accounts
basecamp accounts list --json # List authorized accounts
basecamp accounts use <id> # Set default account
basecamp accounts show --json # Account details, limits, subscription
basecamp accounts update --name "New Name" --json # Rename account
basecamp accounts logo upload <file> --json # Upload logo (PNG/JPEG/GIF/WebP/AVIF/HEIC, 5MB max)
basecamp accounts logo remove --json # Remove logo
Chat
basecamp chat --in <project> --json # List chats
basecamp chat messages --in <project> --json # List messages
basecamp chat post "Hello!" --in <project>
basecamp chat post "@Jane.Smith, check this" --in <project> # With @mention (auto text/html)
basecamp chat line <line_id> --in <project> # Show line
basecamp chat delete <line_id> --in <project> --force # Delete line (permanent, not trashable)
People
basecamp people list --json # All people in account
basecamp people list --project <project> --json # People on project
basecamp me --json # Current user
basecamp people show <id> --json # Person details
basecamp people add <id> --project <project> # Add to project
basecamp people remove <id> --project <project> # Remove from project
Search
basecamp search "query" --json # Full-text search
basecamp search "query" --sort updated_at --limit 20
basecamp search metadata --json # Available search scopes
Generic Show
basecamp show <type> <id> --in <project> --json # Show any recording type (includes up to 100 comments by default)
basecamp show <type> <id> --all-comments --in <project> --json # Fetch the full discussion when you need every comment
basecamp show <type> <id> --no-comments --in <project> --json # Skip the extra comments fetch
# Types: todo, todolist, message, comment, card, card-table, document (or omit <type> for generic lookup)
# Typed show commands also support --comments / --all-comments / --no-comments:
basecamp todos show <id> --comments --json # Opt in to comments on typed show
basecamp cards show <id> --all-comments --json # Fetch all comments on card
basecamp messages show <id> --no-comments --json # Suppress comments
# All commentable show commands: todos, messages, cards, files, todolists, schedule, checkins, forwards, chat
Configuration
The CLI uses two directory namespaces: basecamp for your Basecamp identity and project relationships, basecamp for tool-specific operational data.
~/.config/basecamp/ # Basecamp identity (DO NOT read credentials)
├── credentials.json # OAuth tokens — NEVER read or log
├── client.json # DCR client registration
└── config.json # Global preferences (account_id, base_url, format)
~/.cache/basecamp/ # Tool cache (ephemeral, auto-managed)
├── completion.json # Tab completion cache
└── resilience/ # Circuit breaker state
.basecamp/ # Per-repo config (committed to git)
└── config.json # Project defaults (project_id, account_id, todolist_id)
Per-repo config: .basecamp/config.json
{
"project_id": "12345",
"todolist_id": "67890"
}
Initialize:
basecamp config init
basecamp config set project_id <id>
basecamp config set todolist_id <id>
Config Trust:
Authority keys (base_url, default_profile, profiles) in local/repo configs are blocked until explicitly trusted. This prevents a cloned repo's config from redirecting OAuth tokens.
basecamp config trust # Trust nearest .basecamp/config.json
basecamp config trust /path/to/.basecamp/config.json # Trust specific config file
basecamp config trust --list # Show all trusted configs
basecamp config untrust # Revoke trust for nearest config
basecamp config untrust /path/to/.basecamp/config.json # Revoke trust for specific path
Check context:
cat .basecamp/config.json 2>/dev/null || echo "No project configured"
Global config: ~/.config/basecamp/config.json (account_id, base_url, format preferences)
Error Handling
General diagnostics:
basecamp doctor --json # Check CLI health, auth, connectivity
Rate limiting (429): The CLI handles backoff automatically. If you see 429 errors, reduce request frequency.
Authentication errors:
basecamp auth status # Check auth
basecamp auth login # Re-authenticate
basecamp auth login --scope full # Full access (BC3 OAuth only)
basecamp auth login --device-code # Headless: display URL, paste callback
Network errors / localhost URLs:
# Check for dev config
cat ~/.config/basecamp/config.json
# Should only contain: {"account_id": "<id>"}
# Remove base_url/api_url if pointing to localhost
Not found errors:
basecamp auth status # Verify auth working
cat ~/.config/basecamp/accounts.json # Check available accounts
Required arguments are positional (not flags):
basecamp todos create "Buy milk"(not--content)basecamp cards create "New feature"(not--title)basecamp messages create "Subject" "Body"(not--subject)basecamp chat post "Hello"(not--content)basecamp comments create <id> "Text"(not a flag)basecamp webhooks create "https://..." --in <project>(not--url)basecamp checkins answer create <question-id> "content"(not--question)--date YYYY-MM-DDis optional forcheckins answer create; if omitted, it defaults to today
Missing argument errors (code: "usage"): When a required positional argument is missing, the CLI returns a structured error naming the specific argument. Use this for elicitation:
$ basecamp todos create --json
{"ok": false, "error": "<content> required", "code": "usage",
"hint": "Usage: basecamp todos create <content>"}
$ basecamp comments create 123 --json
{"ok": false, "error": "<content> required", "code": "usage", ...}
The error field names the missing <arg> — use it to prompt the user for the specific value.
URL malformed (curl exit 3): Special characters in content. Use plain text or properly escaped HTML.
Built-in jq Filtering
The CLI has a built-in --jq flag powered by gojq — no external jq binary required. Always prefer --jq over piping to external jq.
# Extract fields from data array
basecamp todos list --in <project> --jq '.data[] | select(.completed == false) | .title'
basecamp todos list --in <project> --jq '.data | length'
basecamp todos list --in <project> --jq '[.data[] | {id, title, status}]'
# Access envelope metadata
basecamp todos list --in <project> --jq '.breadcrumbs[0].cmd'
basecamp todos list --in <project> --jq '.meta.stats.requests'
# Filter and transform
basecamp cards list --in <project> --jq '[.data[] | select(.completed == true) | .title]'
basecamp people list --jq '[.data[] | {name: .name, email: .email_address}]'
--jq implies --json — no need to pass both. String results print as plain text; objects and arrays print as formatted JSON.
Exit Codes
| Exit | Meaning | Fix |
|---|---|---|
| 0 | OK | — |
| 1 | Usage error | Check basecamp <cmd> --help |
| 2 | Not found | Verify ID/URL exists |
| 3 | Auth error | basecamp auth login |
| 4 | Forbidden | Check account/project permissions |
| 5 | Rate limit | Wait and retry (resilience layer handles Retry-After automatically) |
| 6 | Network error | Check connectivity, basecamp doctor |
| 7 | API error | Retry; if persistent, check basecamp doctor |
| 8 | Ambiguous | Be more specific (use ID instead of name) |
Learn More
- API concepts: https://github.com/basecamp/bc3-api#key-concepts
- CLI repo: https://github.com/basecamp/basecamp-cli
- API coverage: See API-COVERAGE.md in the CLI repo
Related skills
Word Document Editor
anthropics
Create, edit, and format Word documents with tables, images, and tracked changes.
Skill Template
anthropics
A template for creating and configuring new Claude skills.
Kanban TUI
Zaloog
Manage tasks, boards, and workflows in your terminal with a CLI kanban tool.
Superpowers
obra
Jesse Vincent's full Claude Code methodology — TDD, brainstorming, git worktrees, all auto-triggering.