Access the Attio REST API with managed OAuth authentication. Manage CRM objects, records, tasks, notes, comments, lists, list entries, meetings, call recordings, and workspace data.
# List all objects in workspace
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/attio/v2/objects')
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/attio/{native-api-path}
Maton proxies requests to api.attio.com 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 Attio OAuth connections at https://api.maton.ai.
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=attio&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': 'attio'}).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-06T03:13:17.061608Z",
"last_updated_time": "2026-02-06T03:13:17.061617Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "attio",
"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 Attio 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/attio/v2/objects')
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.
Objects are the schema definitions (like People, Companies, or custom objects).
GET /attio/v2/objects
Returns all system-defined and custom objects in your workspace.
GET /attio/v2/objects/{object}
Get a specific object by slug (e.g., people, companies) or UUID.
Attributes define the fields on objects.
GET /attio/v2/objects/{object}/attributes
Returns all attributes for an object.
Records are the actual data entries (people, companies, etc.).
POST /attio/v2/objects/{object}/records/query
Content-Type: application/json
{
"limit": 50,
"offset": 0,
"filter": {},
"sorts": []
}
Query parameters in body:
limit: Maximum results (default 500)offset: Number of results to skipfilter: Filter criteria objectsorts: Array of sort specificationsGET /attio/v2/objects/{object}/records/{record_id}
POST /attio/v2/objects/{object}/records
Content-Type: application/json
{
"data": {
"values": {
"name": [{"first_name": "John", "last_name": "Doe", "full_name": "John Doe"}],
"email_addresses": ["john@example.com"]
}
}
}
Note: For personal-name type attributes (like name on people), you must include full_name along with first_name and last_name.
PATCH /attio/v2/objects/{object}/records/{record_id}
Content-Type: application/json
{
"data": {
"values": {
"job_title": "Software Engineer"
}
}
}
DELETE /attio/v2/objects/{object}/records/{record_id}
GET /attio/v2/tasks?limit=50
Query parameters:
limit: Maximum results (default 500)offset: Number to skipsort: created_at:asc or created_at:desclinked_object: Filter by object type (e.g., people)linked_record_id: Filter by specific recordassignee: Filter by assignee email/IDis_completed: Filter by completion status (true/false)GET /attio/v2/tasks/{task_id}
POST /attio/v2/tasks
Content-Type: application/json
{
"data": {
"content": "Follow up with customer",
"format": "plaintext",
"deadline_at": "2026-02-15T00:00:00.000000000Z",
"is_completed": false,
"assignees": [],
"linked_records": [
{
"target_object": "companies",
"target_record_id": "16f2fc57-5d22-48b8-b9db-8b0e6d99e9bc"
}
]
}
}
Required fields: content, format, deadline_at, assignees, linked_records
PATCH /attio/v2/tasks/{task_id}
Content-Type: application/json
{
"data": {
"is_completed": true
}
}
DELETE /attio/v2/tasks/{task_id}
GET /attio/v2/workspace_members
GET /attio/v2/workspace_members/{workspace_member_id}
GET /attio/v2/self
Returns workspace info and OAuth scopes for the current access token.
POST /attio/v2/comments
Content-Type: application/json
{
"data": {
"format": "plaintext",
"content": "This is a comment",
"author": {
"type": "workspace-member",
"id": "{workspace_member_id}"
},
"record": {
"object": "companies",
"record_id": "{record_id}"
}
}
}
Required fields: format, content, author
Plus one of:
record: Object with object slug and record_id (for record comments)entry: Object with list slug and entry_id (for list entry comments)thread_id: UUID of existing thread (for replies)POST /attio/v2/comments
Content-Type: application/json
{
"data": {
"format": "plaintext",
"content": "This is a reply",
"author": {
"type": "workspace-member",
"id": "{workspace_member_id}"
},
"thread_id": "{thread_id}"
}
}
GET /attio/v2/lists
GET /attio/v2/lists/{list_id}
POST /attio/v2/lists/{list}/entries/query
Content-Type: application/json
{
"limit": 50,
"offset": 0,
"filter": {},
"sorts": []
}
Query parameters in body:
limit: Maximum results (default 500)offset: Number of results to skipfilter: Filter criteria objectsorts: Array of sort specificationsPOST /attio/v2/lists/{list}/entries
Content-Type: application/json
{
"data": {
"parent_record_id": "{record_id}",
"parent_object": "companies",
"entry_values": {}
}
}
GET /attio/v2/lists/{list}/entries/{entry_id}
PATCH /attio/v2/lists/{list}/entries/{entry_id}
Content-Type: application/json
{
"data": {
"entry_values": {
"status": "Active"
}
}
}
DELETE /attio/v2/lists/{list}/entries/{entry_id}
GET /attio/v2/notes?limit=50
Query parameters:
limit: Maximum results (default 10, max 50)offset: Number to skipparent_object: Object slug containing notesparent_record_id: Filter by specific recordGET /attio/v2/notes/{note_id}
POST /attio/v2/notes
Content-Type: application/json
{
"data": {
"format": "plaintext",
"title": "Meeting Summary",
"content": "Discussed Q1 goals and roadmap priorities.",
"parent_object": "companies",
"parent_record_id": "{record_id}",
"created_by_actor": {
"type": "workspace-member",
"id": "{workspace_member_id}"
}
}
}
Required fields: format, content, parent_object, parent_record_id
DELETE /attio/v2/notes/{note_id}
GET /attio/v2/meetings?limit=50
Query parameters:
limit: Maximum results (default 50, max 200)cursor: Pagination cursor from previous responseUses cursor-based pagination.
GET /attio/v2/meetings/{meeting_id}
Call recordings are accessed through meetings.
GET /attio/v2/meetings/{meeting_id}/call_recordings?limit=50
Query parameters:
limit: Maximum results (default 50, max 200)cursor: Pagination cursor from previous responseGET /attio/v2/meetings/{meeting_id}/call_recordings/{call_recording_id}
Attio supports two pagination methods:
GET /attio/v2/tasks?limit=50&offset=0
GET /attio/v2/tasks?limit=50&offset=50
GET /attio/v2/tasks?limit=50&offset=100
GET /attio/v2/meetings?limit=50
GET /attio/v2/meetings?limit=50&cursor={next_cursor}
Response includes pagination.next_cursor when more results exist.
// Query company records
const response = await fetch(
'https://api.maton.ai/attio/v2/objects/companies/records/query',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ limit: 10 })
}
);
const data = await response.json();
import os
import requests
# Query company records
response = requests.post(
'https://api.maton.ai/attio/v2/objects/companies/records/query',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
json={'limit': 10}
)
data = response.json()
people, companies)full_name when creating recordsformat: "plaintext", deadline_at, assignees array (can be empty), and linked_records array (can be empty)format, content, parent_object, and parent_record_idformat, content, author, plus one of record, entry, or thread_idlimit and offset parameters (or cursor for meetings)curl -g when URLs contain brackets to disable glob parsingjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| Status | Meaning |
|---|---|
| -------- | --------- |
| 400 | Missing Attio connection or validation error |
| 401 | Invalid or missing Maton API key |
| 403 | Insufficient OAuth scopes |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Attio 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
If you receive a 403 error about missing scopes, contact Maton support at support@maton.ai with the specific operations/APIs you need and your use-case.
attio. For example:https://api.maton.ai/attio/v2/objectshttps://api.maton.ai/v2/objects共 2 个版本