Troubleshooting
If requests fail, run zero doctor check-connector --env-name ATTIO_TOKEN or zero doctor check-connector --url https://api.attio.com/v2/self --method GET
How It Works
Attio is an AI-native CRM. Your workspace is organized into objects (standard ones: companies, people, deals, users, workspaces; plus any custom objects you create). Each object contains records, and records can be grouped into lists via entries. Records can also be annotated with notes, tasks, comments (on threads), and attribute history.
Workspace
└── Objects (companies, people, deals, ...custom)
└── Records
├── attribute values (history supported)
├── entries (memberships in lists)
├── notes
├── tasks
└── comment threads
Base URL: https://api.attio.com
Authentication
All requests use Bearer token auth:
Authorization: Bearer $ATTIO_TOKEN
Prerequisites
Connect the Attio connector at app.vm0.ai/connectors.
Environment Variables
| Variable | Description |
|---|---|
ATTIO_TOKEN |
Attio workspace access token |
Key Endpoints
1. Identify the Current Token
Check which workspace the token is bound to and which scopes it holds.
curl -s "https://api.attio.com/v2/self" --header "Authorization: Bearer $ATTIO_TOKEN"
2. List Objects
Return every object in the workspace, including custom ones.
curl -s "https://api.attio.com/v2/objects" --header "Authorization: Bearer $ATTIO_TOKEN"
Fetch a single object by slug or UUID — replace <object> with e.g. companies, people, deals, or an object UUID:
curl -s "https://api.attio.com/v2/objects/<object>" --header "Authorization: Bearer $ATTIO_TOKEN"
3. Query (List) Records
List records for an object. Uses POST /v2/objects/<object>/records/query because the body carries filter/sort/pagination.
Write /tmp/attio_query.json:
{
"filter": {},
"sorts": [
{ "attribute": "created_at", "direction": "desc" }
],
"limit": 100,
"offset": 0
}
curl -s -X POST "https://api.attio.com/v2/objects/companies/records/query" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_query.json
Filter by a single attribute (e.g. find a person by name):
{
"filter": { "name": "Ada Lovelace" },
"limit": 10
}
4. Get a Single Record
Replace <object> and <record-id> with real values (both are UUIDs or — for <object> — slugs):
curl -s "https://api.attio.com/v2/objects/<object>/records/<record-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
5. Create a Record
Write /tmp/attio_create_record.json. Top-level keys inside values are attribute slugs (for standard attributes) or attribute UUIDs (for custom attributes):
{
"data": {
"values": {
"name": "Acme Inc.",
"domains": ["acme.com"],
"description": "Imported by vm0 agent"
}
}
}
curl -s -X POST "https://api.attio.com/v2/objects/companies/records" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_create_record.json
Throws on conflicts of unique attributes — use the Assert endpoint (next) if you prefer upsert semantics.
6. Assert (Upsert) a Record
Create or update-in-place by a matching attribute. Pass matching_attribute as a query parameter.
Write /tmp/attio_assert_record.json:
{
"data": {
"values": {
"domains": ["acme.com"],
"name": "Acme Inc."
}
}
}
curl -s -X PUT "https://api.attio.com/v2/objects/companies/records?matching_attribute=domains" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_assert_record.json
7. Update a Record
PATCH performs a partial merge (multi-select values are appended); PUT fully overwrites the values you supply. Replace <record-id>.
Write /tmp/attio_update_record.json:
{
"data": {
"values": {
"description": "Updated description"
}
}
}
curl -s -X PATCH "https://api.attio.com/v2/objects/companies/records/<record-id>" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_update_record.json
8. Delete a Record
curl -s -X DELETE "https://api.attio.com/v2/objects/companies/records/<record-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
9. Search Records Across Objects
Search spans every object the token can read.
Write /tmp/attio_search.json:
{
"query": "ada",
"limit": 25
}
curl -s -X POST "https://api.attio.com/v2/objects/records/search" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_search.json
10. List Entries on a Record
Every list a record belongs to (e.g. which sales lists a company is on) — replace <object> (slug or UUID) and <record-id>:
curl -s "https://api.attio.com/v2/objects/<object>/records/<record-id>/entries" --header "Authorization: Bearer $ATTIO_TOKEN"
Lists & Entries
11. List All Lists
curl -s "https://api.attio.com/v2/lists" --header "Authorization: Bearer $ATTIO_TOKEN"
12. Get a List
Replace <list> with the list slug or UUID:
curl -s "https://api.attio.com/v2/lists/<list>" --header "Authorization: Bearer $ATTIO_TOKEN"
13. Query Entries in a List
Write /tmp/attio_list_query.json:
{
"filter": {},
"sorts": [{ "attribute": "created_at", "direction": "desc" }],
"limit": 100,
"offset": 0
}
curl -s -X POST "https://api.attio.com/v2/lists/<list>/entries/query" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_list_query.json
14. Add a Record to a List (Create Entry)
Write /tmp/attio_create_entry.json — replace the parent record id:
{
"data": {
"parent_record_id": "<record-id>",
"parent_object": "companies",
"entry_values": {}
}
}
curl -s -X POST "https://api.attio.com/v2/lists/<list>/entries" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_create_entry.json
15. Update or Delete an Entry
Replace <entry-id>:
curl -s -X PATCH "https://api.attio.com/v2/entries/<entry-id>" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_update_entry.json
curl -s -X DELETE "https://api.attio.com/v2/entries/<entry-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
Notes
16. List Notes
Filter by parent_object + parent_record_id via query string:
curl -s "https://api.attio.com/v2/notes?parent_object=companies&parent_record_id=<record-id>&limit=25" --header "Authorization: Bearer $ATTIO_TOKEN"
17. Create a Note
Write /tmp/attio_create_note.json — format is plaintext or markdown:
{
"data": {
"parent_object": "companies",
"parent_record_id": "<record-id>",
"title": "Discovery call recap",
"format": "markdown",
"content": "## Key takeaways\n- Budget approved\n- Decision by EOQ"
}
}
curl -s -X POST "https://api.attio.com/v2/notes" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_create_note.json
18. Get or Delete a Note
Replace <note-id>:
curl -s "https://api.attio.com/v2/notes/<note-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
curl -s -X DELETE "https://api.attio.com/v2/notes/<note-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
Tasks
19. List Tasks
curl -s "https://api.attio.com/v2/tasks?limit=25" --header "Authorization: Bearer $ATTIO_TOKEN"
20. Create a Task
Write /tmp/attio_create_task.json. linked_records accepts either raw record IDs (via target_record_id) or the shorthand "<record-id>" form — the nested form below is the explicit one. assignees[].referenced_actor_id is a workspace member UUID from GET /v2/workspace-members.
{
"data": {
"content": "Send follow-up deck",
"format": "plaintext",
"deadline_at": "2026-05-01T15:00:00.000000000Z",
"is_completed": false,
"linked_records": [
{
"target_object": "companies",
"target_record_id": "<record-id>"
}
],
"assignees": [
{
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "<workspace-member-id>"
}
]
}
}
curl -s -X POST "https://api.attio.com/v2/tasks" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_create_task.json
21. Update, Complete, or Delete a Task
Replace <task-id>. Toggle is_completed to mark done.
Write /tmp/attio_update_task.json:
{
"data": {
"is_completed": true
}
}
curl -s -X PATCH "https://api.attio.com/v2/tasks/<task-id>" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_update_task.json
curl -s -X DELETE "https://api.attio.com/v2/tasks/<task-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
Comments & Threads
22. Create a Comment
Comments live on threads attached to a record or a note. Write /tmp/attio_create_comment.json:
{
"data": {
"format": "plaintext",
"content": "Nice work — looping in the team.",
"author": {
"type": "workspace-member",
"id": "<workspace-member-id>"
},
"thread_id": "<thread-id>"
}
}
curl -s -X POST "https://api.attio.com/v2/comments" --header "Authorization: Bearer $ATTIO_TOKEN" --header "Content-Type: application/json" -d @/tmp/attio_create_comment.json
Fetch a thread and its comments — replace <thread-id>:
curl -s "https://api.attio.com/v2/threads/<thread-id>" --header "Authorization: Bearer $ATTIO_TOKEN"
Workspace Members
Resolve <workspace-member-id> for assignees/authors:
curl -s "https://api.attio.com/v2/workspace_members" --header "Authorization: Bearer $ATTIO_TOKEN"
Guidelines
- Use object slugs (
companies,people,deals,users,workspaces) for the five standard objects; use object UUIDs for custom objects — both work in the<object>path segment. - Attribute slugs (e.g.
name,domains) work for built-in attributes; custom attributes must be referenced by UUID. POST /v2/objects/<object>/recordsthrows on unique-attribute conflict — usePUT /v2/objects/<object>/records?matching_attribute=<slug>for upsert semantics.PATCHon a record does a shallow merge (multi-selects append);PUToverwrites. Pick the one that matches intent.- Default pagination is
limit: 500, offset: 0for record queries; cap large scans with explicitlimit. - Send all request bodies as JSON files with
-d @/tmp/filename.jsonto avoid shell-escaping issues.