Skill: byungkyu/basecamp (version 1.0.3)

--- name: basecamp description: | 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. metadata: author: maton version: "1.0" clawdbot: emoji: 🧠 requires: env: - MATON_API_KEY --- # Basecamp Access the Basecamp 4 API with managed OAuth authentication. Manage projects, to-dos, messages, schedules, documents, and team collaboration. ## Quick Start ```bash # List all projects python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://api.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://api.maton.ai/basecamp/{resource}.json ``` Maton 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`: ```bash 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://api.maton.ai`. ### List Connections ```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://api.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 ```bash python <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'basecamp'}).encode() req = urllib.request.Request('https://api.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 ```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://api.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:** ```json { "connection": { "connection_id": "{connection_id}", "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 ```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://api.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: ```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://api.maton.ai/basecamp/projects.json') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Maton-Connection', '{connection_id}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ``` If you have multiple connections, always include this header to ensure requests go to the intended account. ## Security & Permissions - Access is scoped to projects, to-dos, messages, schedules, documents, and team collaboration within the connected Basecamp account. - **All write operations require explicit user approval.** Before executing any create, update, or delete call, confirm the target resource and intended effect with the user. ## API Reference ### User Info #### Get Current User ```bash GET /basecamp/my/profile.json ``` **Response:** ```json { "id": 51197030, "name": "Chris Kim", "email_address": "[REDACTED_SECRET]", "admin": true, "owner": true, "time_zone": "America/Los_Angeles", "avatar_url": "https://..." } ``` ### People Operations #### List People ```bash GET /basecamp/people.json ``` **Response:** ```json [ { "id": 51197030, "name": "Chris Kim", "email_address": "[REDACTED_SECRET]", "admin": true, "owner": true, "employee": true, "time_zone": "America/Los_Angeles" } ] ``` #### Get Person ```bash GET /basecamp/people/{person_id}.json ``` #### List Project Members ```bash GET /basecamp/projects/{project_id}/people.json ``` ### Project Operations #### List Projects ```bash GET /basecamp/projects.json ``` **Response:** ```json [ { "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 ```bash 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 ```bash POST /basecamp/projects.json Content-Type: application/json { "name": "New Project", "description": "Project description" } ``` #### Update Project ```bash PUT /basecamp/projects/{project_id}.json Content-Type: application/json { "name": "Updated Project Name", "description": "Updated description" } ``` #### Delete (Trash) Project ```bash DELETE /basecamp/projects/{project_id}.json ``` ### To-Do Operations #### Get Todoset First, get the todoset ID from the project's dock: ```bash GET /basecamp/buckets/{project_id}/todosets/{todoset_id}.json ``` #### List Todolists ```bash GET /basecamp/buckets/{project_id}/todosets/{todoset_id}/todolists.json ``` **Response:** ```json [ { "id": 9550474442, "title": "Basecamp essentials", "description": "", "completed": false, "completed_ratio": "0/5", "url": "https://..." } ] ``` #### Create Todolist ```bash POST /basecamp/buckets/{project_id}/todosets/{todoset_id}/todolists.json Content-Type: application/json { "name": "New Todo List", "description": "List description" } ``` #### Get Todolist ```bash GET /basecamp/buckets/{project_id}/todolists/{todolist_id}.json ``` #### List Todos ```bash GET /basecamp/buckets/{project_id}/todolists/{todolist_id}/todos.json ``` **Response:** ```json [ { "id": 9550474446, "content": "Start here", "description": "", "completed": false, "due_on": null, "assignees": [] } ] ``` #### Create Todo ```bash 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:** ```json { "id": 9555973289, "content": "New todo item", "completed": false } ``` #### Update Todo ```bash PUT /basecamp/buckets/{project_id}/todos/{todo_id}.json Content-Type: application/json { "content": "Updated todo", "description": "Updated description" } ``` #### Complete Todo ```bash POST /basecamp/buckets/{project_id}/todos/{todo_id}/completion.json ``` Returns 204 on success. #### Uncomplete Todo ```bash DELETE /basecamp/buckets/{project_id}/todos/{todo_id}/completion.json ``` ### Message Board Operations #### Get Message Board ```bash GET /basecamp/buckets/{project_id}/message_boards/{message_board_id}.json ``` #### List Messages ```bash GET /basecamp/buckets/{project_id}/message_boards/{message_board_id}/messages.json ``` #### Create Message ```bash 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 ```bash GET /basecamp/buckets/{project_id}/messages/{message_id}.json ``` #### Update Message ```bash 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 ```bash GET /basecamp/buckets/{project_id}/schedules/{schedule_id}.json ``` #### List Schedule Entries ```bash GET /basecamp/buckets/{project_id}/schedules/{schedule_id}/entries.json ``` #### Create Schedule Entry ```bash 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 ```bash 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 ```bash GET /basecamp/buckets/{project_id}/vaults/{vault_id}.json ``` #### List Documents in Vault ```bash GET /basecamp/buckets/{project_id}/vaults/{vault_id}/documents.json ``` #### Create Document ```bash 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 ```bash GET /basecamp/buckets/{project_id}/vaults/{vault_id}/uploads.json ``` ### Campfire (Chat) Operations #### List All Campfires ```bash GET /basecamp/chats.json ``` #### Get Campfire ```bash GET /basecamp/buckets/{project_id}/chats/{chat_id}.json ``` #### List Campfire Lines (Messages) ```bash GET /basecamp/buckets/{project_id}/chats/{chat_id}/lines.json ``` #### Create Campfire Line ```bash 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 ```bash GET /basecamp/buckets/{project_id}/recordings/{recording_id}/comments.json ``` #### Create Comment ```bash 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 ```bash PUT /basecamp/buckets/{project_id}/recordings/{recording_id}/status/trashed.json ``` #### Archive Recording ```bash PUT /basecamp/buckets/{project_id}/recordings/{recording_id}/status/archived.json ``` #### Unarchive Recording ```bash PUT /basecamp/buckets/{project_id}/recordings/{recording_id}/status/active.json ``` ### Templates Operations #### List Templates ```bash GET /basecamp/templates.json ``` #### Create Project from Template ```bash 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: ```json { "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 ```javascript const response = await fetch( 'https://api.maton.ai/basecamp/projects.json', { headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}` } } ); const projects = await response.json(); ``` ### Python ```python import os import requests response = requests.get( 'https://api.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` - Maton 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: ```bash echo $MATON_API_KEY ``` 2. Verify the API key is valid by listing connections: ```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://api.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://api.maton.ai/basecamp/projects.json` - Incorrect: `https://api.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:[REDACTED_SECRET])


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)