Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
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.
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.
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Access the Basecamp 4 API with managed OAuth authentication. Manage projects, to-dos, messages, schedules, documents, and team collaboration.
# 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
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.
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"
Sign in or create an account at maton.ai Go to maton.ai/settings Copy your API key
Manage your Basecamp OAuth connections at https://ctrl.maton.ai.
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
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
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.
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
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.
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://..." }
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
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
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
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>" }
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" }
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
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!" }
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>" }
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
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" }
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.
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
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} ] }
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
const response = await fetch( 'https://gateway.maton.ai/basecamp/projects.json', { headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}` } } ); const projects = await response.json();
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()
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
StatusMeaning400Missing Basecamp connection or bad request401Invalid or missing Maton API key404Resource not found, deleted, or no access429Rate limited (check Retry-After header)507Account limit reached (e.g., project limit)5xxServer error (retry with exponential backoff)
Check that the MATON_API_KEY environment variable is set: echo $MATON_API_KEY 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
Ensure your URL path starts with basecamp. For example: Correct: https://gateway.maton.ai/basecamp/projects.json Incorrect: https://gateway.maton.ai/projects.json
Basecamp 4 API Documentation Authentication Guide API Reference Maton Community Maton Support
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.