companycam
// CompanyCam API integration with managed OAuth. Photo documentation platform for contractors. Use this skill when users want to manage projects, photos, users, tags, groups, or documents in CompanyCam. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
CompanyCam
Access the CompanyCam API with managed OAuth authentication. Manage projects, photos, users, tags, groups, documents, and webhooks for contractor photo documentation.
Quick Start
# List projects
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/companycam/v2/projects')
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/companycam/v2/{resource}
Replace {resource} with the actual CompanyCam API endpoint path. The gateway proxies requests to api.companycam.com/v2 and automatically injects your OAuth token.
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
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your CompanyCam 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=companycam&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': 'companycam'}).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": "d274cf68-9e76-464c-92e3-ff274c44526e",
"status": "ACTIVE",
"creation_time": "2026-02-12T01:56:32.259046Z",
"last_updated_time": "2026-02-12T01:57:38.944271Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "companycam",
"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 CompanyCam 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/companycam/v2/projects')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', 'd274cf68-9e76-464c-92e3-ff274c44526e')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Company
Get Company
GET /companycam/v2/company
Returns the current company information.
Users
Get Current User
GET /companycam/v2/users/current
List Users
GET /companycam/v2/users
Query parameters:
page- Page numberper_page- Results per page (default: 25)status- Filter by status (active, inactive)
Create User
POST /companycam/v2/users
Content-Type: application/json
{
"first_name": "John",
"last_name": "Doe",
"email_address": "john@example.com",
"user_role": "standard"
}
User roles: admin, standard, limited
Get User
GET /companycam/v2/users/{id}
Update User
PUT /companycam/v2/users/{id}
Content-Type: application/json
{
"first_name": "John",
"last_name": "Smith"
}
Delete User
DELETE /companycam/v2/users/{id}
Projects
List Projects
GET /companycam/v2/projects
Query parameters:
page- Page numberper_page- Results per page (default: 25)query- Search querystatus- Filter by statusmodified_since- Unix timestamp for filtering
Create Project
POST /companycam/v2/projects
Content-Type: application/json
{
"name": "New Construction Project",
"address": {
"street_address_1": "123 Main St",
"city": "Los Angeles",
"state": "CA",
"postal_code": "90210",
"country": "US"
}
}
Get Project
GET /companycam/v2/projects/{id}
Update Project
PUT /companycam/v2/projects/{id}
Content-Type: application/json
{
"name": "Updated Project Name"
}
Delete Project
DELETE /companycam/v2/projects/{id}
Archive Project
PATCH /companycam/v2/projects/{id}/archive
Restore Project
PUT /companycam/v2/projects/{id}/restore
Project Photos
List Project Photos
GET /companycam/v2/projects/{project_id}/photos
Query parameters:
page- Page numberper_page- Results per pagestart_date- Filter by start date (Unix timestamp)end_date- Filter by end date (Unix timestamp)user_ids- Filter by user IDsgroup_ids- Filter by group IDstag_ids- Filter by tag IDs
Add Photo to Project
POST /companycam/v2/projects/{project_id}/photos
Content-Type: application/json
{
"uri": "https://example.com/photo.jpg",
"captured_at": 1609459200,
"coordinates": {
"lat": 34.0522,
"lon": -118.2437
},
"tags": ["exterior", "front"]
}
Project Comments
List Project Comments
GET /companycam/v2/projects/{project_id}/comments
Add Project Comment
POST /companycam/v2/projects/{project_id}/comments
Content-Type: application/json
{
"comment": {
"content": "Work completed successfully"
}
}
Project Labels
List Project Labels
GET /companycam/v2/projects/{project_id}/labels
Add Labels to Project
POST /companycam/v2/projects/{project_id}/labels
Content-Type: application/json
{
"labels": ["priority", "urgent"]
}
Delete Project Label
DELETE /companycam/v2/projects/{project_id}/labels/{label_id}
Project Documents
List Project Documents
GET /companycam/v2/projects/{project_id}/documents
Upload Document
POST /companycam/v2/projects/{project_id}/documents
Content-Type: application/json
{
"uri": "https://example.com/document.pdf",
"name": "Contract.pdf"
}
Project Checklists
List Project Checklists
GET /companycam/v2/projects/{project_id}/checklists
Create Checklist from Template
POST /companycam/v2/projects/{project_id}/checklists
Content-Type: application/json
{
"checklist_template_id": "template_id"
}
Get Project Checklist
GET /companycam/v2/projects/{project_id}/checklists/{checklist_id}
Project Users
List Assigned Users
GET /companycam/v2/projects/{project_id}/assigned_users
Assign User to Project
PUT /companycam/v2/projects/{project_id}/assigned_users/{user_id}
Project Collaborators
List Collaborators
GET /companycam/v2/projects/{project_id}/collaborators
Photos
List All Photos
GET /companycam/v2/photos
Query parameters:
page- Page numberper_page- Results per page
Get Photo
GET /companycam/v2/photos/{id}
Update Photo
PUT /companycam/v2/photos/{id}
Content-Type: application/json
{
"photo": {
"captured_at": 1609459200
}
}
Delete Photo
DELETE /companycam/v2/photos/{id}
List Photo Tags
GET /companycam/v2/photos/{id}/tags
Add Tags to Photo
POST /companycam/v2/photos/{id}/tags
Content-Type: application/json
{
"tags": ["exterior", "completed"]
}
List Photo Comments
GET /companycam/v2/photos/{id}/comments
Add Photo Comment
POST /companycam/v2/photos/{id}/comments
Content-Type: application/json
{
"comment": {
"content": "Great progress!"
}
}
Tags
List Tags
GET /companycam/v2/tags
Create Tag
POST /companycam/v2/tags
Content-Type: application/json
{
"display_value": "Exterior",
"color": "#FF5733"
}
Get Tag
GET /companycam/v2/tags/{id}
Update Tag
PUT /companycam/v2/tags/{id}
Content-Type: application/json
{
"display_value": "Interior",
"color": "#3498DB"
}
Delete Tag
DELETE /companycam/v2/tags/{id}
Groups
List Groups
GET /companycam/v2/groups
Create Group
POST /companycam/v2/groups
Content-Type: application/json
{
"name": "Roofing Team"
}
Get Group
GET /companycam/v2/groups/{id}
Update Group
PUT /companycam/v2/groups/{id}
Content-Type: application/json
{
"name": "Updated Team Name"
}
Delete Group
DELETE /companycam/v2/groups/{id}
Checklists
List All Checklists
GET /companycam/v2/checklists
Query parameters:
page- Page numberper_page- Results per pagecompleted- Filter by completion status (true/false)
Webhooks
List Webhooks
GET /companycam/v2/webhooks
Create Webhook
POST /companycam/v2/webhooks
Content-Type: application/json
{
"url": "https://example.com/webhook",
"scopes": ["project.created", "photo.created"]
}
Available scopes:
project.createdproject.updatedproject.deletedphoto.createdphoto.updatedphoto.deleteddocument.createdlabel.createdlabel.deleted
Get Webhook
GET /companycam/v2/webhooks/{id}
Update Webhook
PUT /companycam/v2/webhooks/{id}
Content-Type: application/json
{
"url": "https://example.com/new-webhook",
"enabled": true
}
Delete Webhook
DELETE /companycam/v2/webhooks/{id}
Pagination
CompanyCam uses page-based pagination:
GET /companycam/v2/projects?page=2&per_page=25
Query parameters:
page- Page number (default: 1)per_page- Results per page (default: 25)
Code Examples
JavaScript - List Projects
const response = await fetch(
'https://gateway.maton.ai/companycam/v2/projects?per_page=10',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const projects = await response.json();
console.log(projects);
Python - List Projects
import os
import requests
response = requests.get(
'https://gateway.maton.ai/companycam/v2/projects',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
params={'per_page': 10}
)
projects = response.json()
for project in projects:
print(f"{project['name']}: {project['id']}")
Python - Create Project with Photo
import os
import requests
headers = {'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
base_url = 'https://gateway.maton.ai/companycam/v2'
# Create project
project_response = requests.post(
f'{base_url}/projects',
headers=headers,
json={
'name': 'Kitchen Renovation',
'address': {
'street_address_1': '456 Oak Ave',
'city': 'Denver',
'state': 'CO',
'postal_code': '80202',
'country': 'US'
}
}
)
project = project_response.json()
print(f"Created project: {project['id']}")
# Add photo to project
photo_response = requests.post(
f'{base_url}/projects/{project["id"]}/photos',
headers=headers,
json={
'uri': 'https://example.com/kitchen-before.jpg',
'tags': ['before', 'kitchen']
}
)
photo = photo_response.json()
print(f"Added photo: {photo['id']}")
Notes
- Project IDs and other IDs are returned as strings
- Timestamps are Unix timestamps (seconds since epoch)
- Photos can be added via URL (uri parameter)
- Comments must be wrapped in a
commentobject - Webhooks use
scopesparameter (notevents) - User roles:
admin,standard,limited - IMPORTANT: When using curl commands, use
curl -gwhen URLs contain brackets to disable glob parsing - IMPORTANT: When piping curl output to
jq, environment variables may not expand correctly. Use Python examples instead.
Rate Limits
| Operation | Limit |
|---|---|
| GET requests | 240 per minute |
| POST/PUT/DELETE | 100 per minute |
When rate limited, the API returns a 429 status code. Implement exponential backoff for retries.
Error Handling
| Status | Meaning |
|---|---|
| 400 | Bad request or missing CompanyCam connection |
| 401 | Invalid or missing Maton API key |
| 404 | Resource not found |
| 422 | Validation error (check error messages) |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from CompanyCam API |
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment 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
Troubleshooting: Invalid App Name
- Ensure your URL path starts with
companycam. For example:
- Correct:
https://gateway.maton.ai/companycam/v2/projects - Incorrect:
https://gateway.maton.ai/v2/projects