Basecamp API integration with managed OAuth. Manage projects, to-dos, messages, schedules, documents, and team collaboration. Use this skill when users want to create and manage projects, to-do lists, schedule events, or collaborate with teams in Basecamp. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
Install
Documentation
Basecamp
Access the Basecamp 4 API with managed OAuth authentication. Manage projects, to-dos, messages, schedules, documents, and team collaboration.
Quick Start
List all projects
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/basecamp/projects.json')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/basecamp/{resource}.json
The gateway proxies requests to 3.basecampapi.com/{account_id}/ and automatically injects your OAuth token and account ID.
.json.
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
1. Sign in or create an account at [maton.ai](https://maton.ai)
2. Go to [maton.ai/settings](https://maton.ai/settings)
3. Copy your API key
Connection Management
Manage your Basecamp OAuth connections at https://ctrl.maton.ai.
List Connections
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=basecamp&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'basecamp'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "71e313c8-9100-48c6-8ea1-6323f6fafd04",
"status": "ACTIVE",
"creation_time": "2026-02-08T03:12:39.815086Z",
"last_updated_time": "2026-02-08T03:12:59.259878Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "basecamp",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Basecamp connections, specify which one to use with the Maton-Connection header:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/basecamp/projects.json')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '71e313c8-9100-48c6-8ea1-6323f6fafd04')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
User Info
#### Get Current User
GET /basecamp/my/profile.json
Response:
{
"id": 51197030,
"name": "Chris Kim",
"email_address": "chris@example.com",
"admin": true,
"owner": true,
"time_zone": "America/Los_Angeles",
"avatar_url": "https://..."
}
People Operations
#### List People
GET /basecamp/people.json
Response:
[
{
"id": 51197030,
"name": "Chris Kim",
"email_address": "chris@example.com",
"admin": true,
"owner": true,
"employee": true,
"time_zone": "America/Los_Angeles"
}
]
#### Get Person
GET /basecamp/people/{person_id}.json
#### List Project Members
GET /basecamp/projects/{project_id}/people.json
Project Operations
#### List Projects
GET /basecamp/projects.json
Response:
[
{
"id": 46005636,
"status": "active",
"name": "Getting Started",
"description": "Quickly get up to speed with everything Basecamp",
"created_at": "2026-02-05T22:59:26.087Z",
"url": "https://3.basecampapi.com/6153810/projects/46005636.json",
"dock": [...]
}
]
#### Get Project
GET /basecamp/projects/{project_id}.json
The project response includes a dock array with available tools (message_board, todoset, vault, chat, schedule, etc.). Each dock item has:
- -
id: The tool's ID - -
name: Tool type (e.g., "todoset", "message_board") - -
enabled: Whether the tool is active - -
url: Direct URL to access the tool
#### Create Project
POST /basecamp/projects.json
Content-Type: application/json
{
"name": "New Project",
"description": "Project description"
}
#### Update Project
PUT /basecamp/projects/{project_id}.json
Content-Type: application/json
{
"name": "Updated Project Name",
"description": "Updated description"
}
#### Delete (Trash) Project
DELETE /basecamp/projects/{project_id}.json
To-Do Operations
#### Get Todoset
First, get the todoset ID from the project's dock:
GET /basecamp/buckets/{project_id}/todosets/{todoset_id}.json
#### List Todolists
GET /basecamp/buckets/{project_id}/todosets/{todoset_id}/todolists.json
Response:
[
{
"id": 9550474442,
"title": "Basecamp essentials",
"description": "",
"completed": false,
"completed_ratio": "0/5",
"url": "https://..."
}
]
#### Create Todolist
POST /basecamp/buckets/{project_id}/todosets/{todoset_id}/todolists.json
Content-Type: application/json
{
"name": "New Todo List",
"description": "List description"
}
#### Get Todolist
GET /basecamp/buckets/{project_id}/todolists/{todolist_id}.json
#### List Todos
GET /basecamp/buckets/{project_id}/todolists/{todolist_id}/todos.json
Response:
[
{
"id": 9550474446,
"content": "Start here",
"description": "",
"completed": false,
"due_on": null,
"assignees": []
}
]
#### Create Todo
POST /basecamp/buckets/{project_id}/todolists/{todolist_id}/todos.json
Content-Type: application/json
{
"content": "New todo item",
"description": "Todo description",
"due_on": "2026-02-15",
"assignee_ids": [51197030]
}
Response:
{
"id": 9555973289,
"content": "New todo item",
"completed": false
}
#### Update Todo
PUT /basecamp/buckets/{project_id}/todos/{todo_id}.json
Content-Type: application/json
{
"content": "Updated todo",
"description": "Updated description"
}
#### Complete Todo
POST /basecamp/buckets/{project_id}/todos/{todo_id}/completion.json
Returns 204 on success.
#### Uncomplete Todo
DELETE /basecamp/buckets/{project_id}/todos/{todo_id}/completion.json
Message Board Operations
#### Get Message Board
GET /basecamp/buckets/{project_id}/message_boards/{message_board_id}.json
#### List Messages
GET /basecamp/buckets/{project_id}/message_boards/{message_board_id}/messages.json
#### Create Message
POST /basecamp/buckets/{project_id}/message_boards/{message_board_id}/messages.json
Content-Type: application/json
{
"subject": "Message Subject",
"content": "<p>Message body with HTML</p>",
"category_id": 123
}
#### Get Message
GET /basecamp/buckets/{project_id}/messages/{message_id}.json
#### Update Message
PUT /basecamp/buckets/{project_id}/messages/{message_id}.json
Content-Type: application/json
{
"subject": "Updated Subject",
"content": "<p>Updated content</p>"
}
Schedule Operations
#### Get Schedule
GET /basecamp/buckets/{project_id}/schedules/{schedule_id}.json
#### List Schedule Entries
GET /basecamp/buckets/{project_id}/schedules/{schedule_id}/entries.json
#### Create Schedule Entry
POST /basecamp/buckets/{project_id}/schedules/{schedule_id}/entries.json
Content-Type: application/json
{
"summary": "Team Meeting",
"description": "Weekly sync",
"starts_at": "2026-02-15T14:00:00Z",
"ends_at": "2026-02-15T15:00:00Z",
"all_day": false,
"participant_ids": [51197030]
}
#### Update Schedule Entry
PUT /basecamp/buckets/{project_id}/schedule_entries/{entry_id}.json
Content-Type: application/json
{
"summary": "Updated Meeting",
"starts_at": "2026-02-15T15:00:00Z",
"ends_at": "2026-02-15T16:00:00Z"
}
Vault (Documents & Files) Operations
#### Get Vault
GET /basecamp/buckets/{project_id}/vaults/{vault_id}.json
#### List Documents in Vault
GET /basecamp/buckets/{project_id}/vaults/{vault_id}/documents.json
#### Create Document
POST /basecamp/buckets/{project_id}/vaults/{vault_id}/documents.json
Content-Type: application/json
{
"title": "Document Title",
"content": "<p>Document content with HTML</p>"
}
#### List Uploads in Vault
GET /basecamp/buckets/{project_id}/vaults/{vault_id}/uploads.json
Campfire (Chat) Operations
#### List All Campfires
GET /basecamp/chats.json
#### Get Campfire
GET /basecamp/buckets/{project_id}/chats/{chat_id}.json
#### List Campfire Lines (Messages)
GET /basecamp/buckets/{project_id}/chats/{chat_id}/lines.json
#### Create Campfire Line
POST /basecamp/buckets/{project_id}/chats/{chat_id}/lines.json
Content-Type: application/json
{
"content": "Hello from the API!"
}
Comments Operations
#### List Comments on Recording
GET /basecamp/buckets/{project_id}/recordings/{recording_id}/comments.json
#### Create Comment
POST /basecamp/buckets/{project_id}/recordings/{recording_id}/comments.json
Content-Type: application/json
{
"content": "<p>Comment text</p>"
}
Recording Status Operations
All content items (todos, messages, documents, etc.) are "recordings" that can be archived or trashed.
#### Trash Recording
PUT /basecamp/buckets/{project_id}/recordings/{recording_id}/status/trashed.json
#### Archive Recording
PUT /basecamp/buckets/{project_id}/recordings/{recording_id}/status/archived.json
#### Unarchive Recording
PUT /basecamp/buckets/{project_id}/recordings/{recording_id}/status/active.json
Templates Operations
#### List Templates
GET /basecamp/templates.json
#### Create Project from Template
POST /basecamp/templates/{template_id}/project_constructions.json
Content-Type: application/json
{
"name": "New Project from Template",
"description": "Description"
}
Pagination
Basecamp uses Link header pagination with rel="next":
Link: <https://3.basecampapi.com/.../page=2>; rel="next"
X-Total-Count: 150
Follow the Link header URL for the next page. When next is absent, you've reached the last page.
Link header.
Key Concepts
Buckets and Projects
A "bucket" is a project's content container. The bucket ID is the same as the project ID in URLs:
/buckets/{project_id}/todosets/{todoset_id}.json
Dock
Each project has a "dock" containing available tools. Always check that a tool is enabled: true before using it:
{
"dock": [
{"name": "todoset", "id": 123, "enabled": true},
{"name": "message_board", "id": 456, "enabled": false}
]
}
Recordings
All content items (todos, messages, documents, comments, etc.) are "recordings" with:
- -
status: "active", "archived", or "trashed" - -
parent: navigation to container - -Unique IDs that can be used across endpoints
Code Examples
JavaScript
const response = await fetch(
'https://gateway.maton.ai/basecamp/projects.json',
{
headers: {
'Authorization': Bearer ${process.env.MATON_API_KEY}
}
}
);
const projects = await response.json();
Python
import os
import requests
response = requests.get(
'https://gateway.maton.ai/basecamp/projects.json',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
projects = response.json()
Notes
- -All API paths must end with
.json - -The gateway automatically injects the account ID
- -Uses Basecamp 4 API (bc3-api)
- -Timestamps are in ISO 8601 format
- -HTML content uses
<div>,<p>,<strong>,<em>,<a>,<ul>,<ol>,<li>tags - -Rate limit: ~50 requests per 10 seconds per IP
- -IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments
Error Handling
| Status | Meaning |
|--------|---------|
| 400 | Missing Basecamp connection or bad request |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found, deleted, or no access |
| 429 | Rate limited (check Retry-After header) |
| 507 | Account limit reached (e.g., project limit) |
| 5xx | Server error (retry with exponential backoff) |
Troubleshooting: API Key Issues
1. Check that the MATON_API_KEY environment variable is set:
echo $MATON_API_KEY
2. Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Troubleshooting: Invalid App Name
1. Ensure your URL path starts with basecamp. For example:
- -Correct:
https://gateway.maton.ai/basecamp/projects.json - -Incorrect:
https://gateway.maton.ai/projects.json
Resources
- -[Basecamp 4 API Documentation](https://github.com/basecamp/bc3-api)
- -[Authentication Guide](https://github.com/basecamp/bc3-api/blob/master/sections/authentication.md)
- -[API Reference](https://github.com/basecamp/bc3-api#endpoints)
- -[Maton Community](https://discord.com/invite/dBfFAcefs2)
- -[Maton Support](mailto:support@maton.ai)
Launch an agent with Basecamp on Termo.