Access the CompanyCam API with managed OAuth authentication. Manage projects, photos, users, tags, groups, documents, and webhooks for contractor photo documentation.
# List projects
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
https://api.maton.ai/companycam/v2/{resource}
Maton proxies requests to api.companycam.com/v2 and automatically injects your OAuth token.
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"
Manage your CompanyCam OAuth connections at https://api.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'companycam'}).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
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:
{
"connection": {
"connection_id": "{connection_id}",
"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.
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
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://api.maton.ai/companycam/v2/projects')
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.
GET /companycam/v2/company
Returns the current company information.
GET /companycam/v2/users/current
GET /companycam/v2/users
Query parameters:
page - Page numberper_page - Results per page (default: 25)status - Filter by status (active, inactive)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 /companycam/v2/users/{id}
PUT /companycam/v2/users/{id}
Content-Type: application/json
{
"first_name": "John",
"last_name": "Smith"
}
DELETE /companycam/v2/users/{id}
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 filteringPOST /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 /companycam/v2/projects/{id}
PUT /companycam/v2/projects/{id}
Content-Type: application/json
{
"name": "Updated Project Name"
}
DELETE /companycam/v2/projects/{id}
PATCH /companycam/v2/projects/{id}/archive
PUT /companycam/v2/projects/{id}/restore
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 IDsPOST /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"]
}
GET /companycam/v2/projects/{project_id}/comments
POST /companycam/v2/projects/{project_id}/comments
Content-Type: application/json
{
"comment": {
"content": "Work completed successfully"
}
}
GET /companycam/v2/projects/{project_id}/labels
POST /companycam/v2/projects/{project_id}/labels
Content-Type: application/json
{
"labels": ["priority", "urgent"]
}
DELETE /companycam/v2/projects/{project_id}/labels/{label_id}
GET /companycam/v2/projects/{project_id}/documents
POST /companycam/v2/projects/{project_id}/documents
Content-Type: application/json
{
"uri": "https://example.com/document.pdf",
"name": "Contract.pdf"
}
GET /companycam/v2/projects/{project_id}/checklists
POST /companycam/v2/projects/{project_id}/checklists
Content-Type: application/json
{
"checklist_template_id": "template_id"
}
GET /companycam/v2/projects/{project_id}/checklists/{checklist_id}
GET /companycam/v2/projects/{project_id}/assigned_users
PUT /companycam/v2/projects/{project_id}/assigned_users/{user_id}
GET /companycam/v2/projects/{project_id}/collaborators
GET /companycam/v2/photos
Query parameters:
page - Page numberper_page - Results per pageGET /companycam/v2/photos/{id}
PUT /companycam/v2/photos/{id}
Content-Type: application/json
{
"photo": {
"captured_at": 1609459200
}
}
DELETE /companycam/v2/photos/{id}
GET /companycam/v2/photos/{id}/tags
POST /companycam/v2/photos/{id}/tags
Content-Type: application/json
{
"tags": ["exterior", "completed"]
}
GET /companycam/v2/photos/{id}/comments
POST /companycam/v2/photos/{id}/comments
Content-Type: application/json
{
"comment": {
"content": "Great progress!"
}
}
GET /companycam/v2/tags
POST /companycam/v2/tags
Content-Type: application/json
{
"display_value": "Exterior",
"color": "#FF5733"
}
GET /companycam/v2/tags/{id}
PUT /companycam/v2/tags/{id}
Content-Type: application/json
{
"display_value": "Interior",
"color": "#3498DB"
}
DELETE /companycam/v2/tags/{id}
GET /companycam/v2/groups
POST /companycam/v2/groups
Content-Type: application/json
{
"name": "Roofing Team"
}
GET /companycam/v2/groups/{id}
PUT /companycam/v2/groups/{id}
Content-Type: application/json
{
"name": "Updated Team Name"
}
DELETE /companycam/v2/groups/{id}
GET /companycam/v2/checklists
Query parameters:
page - Page numberper_page - Results per pagecompleted - Filter by completion status (true/false)GET /companycam/v2/webhooks
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.deletedGET /companycam/v2/webhooks/{id}
PUT /companycam/v2/webhooks/{id}
Content-Type: application/json
{
"url": "https://example.com/new-webhook",
"enabled": true
}
DELETE /companycam/v2/webhooks/{id}
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)const response = await fetch(
'https://api.maton.ai/companycam/v2/projects?per_page=10',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const projects = await response.json();
console.log(projects);
import os
import requests
response = requests.get(
'https://api.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']}")
import os
import requests
headers = {'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
base_url = 'https://api.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']}")
comment objectscopes parameter (not events)admin, standard, limitedcurl -g when URLs contain brackets to disable glob parsingjq, environment variables may not expand correctly. Use Python examples instead.| 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.
| 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 |
MATON_API_KEY environment variable is set:echo $MATON_API_KEY
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
companycam. For example:https://api.maton.ai/companycam/v2/projectshttps://api.maton.ai/v2/projects共 3 个版本