v1.0.2

Basecamp

byungkyu byungkyu ← All skills

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.

Downloads
1.6k
Stars
2
Versions
3
Updated
2026-02-24

Install

npx clawhub@latest install basecamp

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.

Important: All Basecamp API URLs must end with .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":

Response Headers:
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.

Important: Do not construct pagination URLs manually. Always use the URL provided in the 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 jq or other commands, environment variables like $MATON_API_KEY may 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.