Objects
The workspace's objects: definitions, records, queries and relationships.
List Objects#
/v1/objectsobjects_listEvery Object type in the workspace, each with its fields. Two kinds:
custom— record systems the workspace made (Deals, Candidates, Properties…). Their records are read and written under/v1/objects/{key}/records.system— Attria's built-in Objects (keysagents,websites,image_boards,video_boards,files). They can be related to, and carry extra custom fields, but their records belong to their own feature.
A field's key is what record values use; type says what a value may be; options lists the choices of a select, multi_select or stage field (write either the option's id or its label).
Query parameters
kindstringoptionalOnly this kind.
"custom""system"limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/objects" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/objects", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/objects",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
data = res.json()Create an Object#
/v1/objectsobjects_createMake a new record system — the same thing the Objects screen's New object makes, with a Table view. The Object's key is the plural name in snake_case (Sales leads → sales_leads); if that key is taken the answer is 409 naming the existing Object, so a retried create never makes a twin.
Each field needs a label and a type (text, long_text, number, currency, date (YYYY-MM-DD), datetime (ISO 8601), boolean, email, phone, url, select, multi_select, stage, image, file). Choice types need options. title_field names the field that titles a record (default: the first).
{
"singular_name": "Deal",
"plural_name": "Deals",
"fields": [
{ "label": "Name", "type": "text", "required": true },
{ "label": "Value", "type": "currency" },
{ "label": "Stage", "type": "stage", "options": ["New", "Proposal", "Won", "Lost"] },
{ "label": "Contact email", "type": "email" }
]
}Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
singular_namestringrequiredOne record:
Deal.max length 80plural_namestringrequiredMany:
Deals. Also makes the key.max length 80descriptionstringoptionalDefault""max length 2000iconstringoptionalA Lucide icon name, e.g.
Briefcase.Default"Box"max length 64colorstringoptionalDefault"#64748b"max length 32title_fieldstringoptionalKey of the field that titles a record. Default: the first field.
fieldsarray of objectrequiredfields[].labelstringrequiredWhat people see:
Deal value.max length 120fields[].keystringoptionalStable key used in record values. Default: the label in snake_case (
deal_value).fields[].typestringoptionalThe field's type.
"text""long_text""number""currency""date""datetime""boolean""email""phone""url""select""multi_select""stage""image""file"Default"text"fields[].optionsarray of string | objectoptionalFor
select,multi_selectandstage: the choices, as labels or{ id, label, color }.fields[].requiredbooleanoptionalRecords must have a value.
Defaultfalse
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/objects" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"singular_name": "Deal",
"plural_name": "Deals",
"fields": [
{
"label": "Name",
"type": "text",
"required": true
},
{
"label": "Value",
"type": "currency"
},
{
"label": "Stage",
"type": "stage",
"options": [
"New",
"Proposal",
"Won",
"Lost"
]
},
{
"label": "Contact email",
"type": "email"
}
]
}'const res = await fetch("https://app.attria.io/api/v1/objects", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
singular_name: "Deal",
plural_name: "Deals",
fields: [
{
label: "Name",
type: "text",
required: true,
},
{
label: "Value",
type: "currency",
},
{
label: "Stage",
type: "stage",
options: ["New", "Proposal", "Won", "Lost"],
},
{
label: "Contact email",
type: "email",
},
],
}),
});
const data = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/objects",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"singular_name": "Deal",
"plural_name": "Deals",
"fields": [
{
"label": "Name",
"type": "text",
"required": True,
},
{
"label": "Value",
"type": "currency",
},
{
"label": "Stage",
"type": "stage",
"options": ["New", "Proposal", "Won", "Lost"],
},
{
"label": "Contact email",
"type": "email",
},
],
},
timeout=30,
)
data = res.json()Get an Object#
/v1/objects/{object}objects_getOne Object type and its fields, by key (deals) or id. Read this before writing records: it says which values keys exist, their types, which are required, and the options a choice field accepts.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/objects/deals" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/objects/deals", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/objects/deals",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
data = res.json()Change an Object#
/v1/objects/{object}objects_updateRename an Object, or add, change and remove its fields. Only what you send changes.
fields— each item whosekeyalready exists updates that field (label,type,options,required); a newkey(or a label with no key) adds a field.remove_fields— keys of custom fields to delete. Their values stay on existing records but are no longer shown or validated.
Built-in fields (origin: system) cannot be changed. The Object's key never changes, so code that uses it keeps working.
{
"fields": [
{ "key": "stage", "label": "Stage", "type": "stage", "options": ["New", "Proposal", "Negotiation", "Won", "Lost"] },
{ "label": "Close date", "type": "date" }
],
"remove_fields": ["legacy_notes"]
}Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
singular_namestringoptionalmax length 80plural_namestringoptionalmax length 80descriptionstringoptionalmax length 2000iconstringoptionalmax length 64colorstringoptionalmax length 32title_fieldstringoptionalfieldsarray of objectoptionalFields to change (by existing
key) or add.fields[].keystringoptionalAn existing field's key to change it; a new key (or none, with a label) to add one.
fields[].labelstringoptionalmax length 120fields[].typestringoptional"text""long_text""number""currency""date""datetime""boolean""email""phone""url""select""multi_select""stage""image""file"fields[].optionsarray of string | objectoptionalReplaces the field's choices.
fields[].requiredbooleanoptionalremove_fieldsarray of stringoptional
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X PATCH "https://app.attria.io/api/v1/objects/deals" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"fields": [
{
"key": "stage",
"label": "Stage",
"type": "stage",
"options": [
"New",
"Proposal",
"Negotiation",
"Won",
"Lost"
]
},
{
"label": "Close date",
"type": "date"
}
],
"remove_fields": [
"legacy_notes"
]
}'const res = await fetch("https://app.attria.io/api/v1/objects/deals", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
fields: [
{
key: "stage",
label: "Stage",
type: "stage",
options: ["New", "Proposal", "Negotiation", "Won", "Lost"],
},
{
label: "Close date",
type: "date",
},
],
remove_fields: ["legacy_notes"],
}),
});
const data = await res.json();import os, uuid, requests
res = requests.patch(
"https://app.attria.io/api/v1/objects/deals",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"fields": [
{
"key": "stage",
"label": "Stage",
"type": "stage",
"options": ["New", "Proposal", "Negotiation", "Won", "Lost"],
},
{
"label": "Close date",
"type": "date",
},
],
"remove_fields": ["legacy_notes"],
},
timeout=30,
)
data = res.json()List and query records#
/v1/objects/{object}/recordsobjects_records_listAn Object's records, newest change first. Narrow with:
filter— a JSON object of fieldkey→ value; every pair must match exactly. Values are normalised as on write (a choice matches by optionidorlabel;"true"matches a boolean). For amulti_selecta list matches records that contain all of it.nullmatches an empty field.search— text found anywhere in the record's values (case-insensitive).sort—updated_at,created_ator any field key; prefix-for descending. Default-updated_at.
Page with limit and the next_cursor of the previous page.
GET /v1/objects/deals/records?filter={"stage":"Won"}&sort=-value&limit=20Path parameters
objectstringrequiredThe Object's key (
deals) or id.
Query parameters
filterstringoptionalJSON object:
{"stage":"Won","owner_email":"ana@acme.com"}(URL-encode it).searchstringoptionalText to find in any value.
sortstringoptionalupdated_at,created_ator a field key;-prefix for descending.Default"-updated_at"limitintegeroptionalHow many items to return, 1–100. Default 25.
Default25min 1max 100cursorstringoptionalThe
next_cursorfrom the previous page. Omit for the first page.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/objects/deals/records?filter=%7B%22stage%22%3A%22Won%22%7D&sort=-value&limit=20" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/objects/deals/records?filter=%7B%22stage%22%3A%22Won%22%7D&sort=-value&limit=20", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/objects/deals/records?filter=%7B%22stage%22%3A%22Won%22%7D&sort=-value&limit=20",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
data = res.json()Create a record#
/v1/objects/{object}/recordsobjects_records_createAdd one record. values is keyed by field key and checked against the Object's fields exactly as the app checks them: unknown keys, missing required fields and values of the wrong type are refused with 422 and a message naming the field. Choice fields accept an option's id or label and store the id.
Send an Idempotency-Key: a retry with the same key returns the record the first request made instead of a duplicate.
{ "values": { "name": "Acme renewal", "value": 12000, "stage": "Proposal", "contact_email": "ana@acme.com" } }Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
valuesobjectrequiredField key → value.
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/objects/deals/records" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"values": {
"name": "Acme renewal",
"value": 12000,
"stage": "Proposal",
"contact_email": "ana@acme.com"
}
}'const res = await fetch("https://app.attria.io/api/v1/objects/deals/records", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
values: {
name: "Acme renewal",
value: 12000,
stage: "Proposal",
contact_email: "ana@acme.com",
},
}),
});
const data = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/objects/deals/records",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"values": {
"name": "Acme renewal",
"value": 12000,
"stage": "Proposal",
"contact_email": "ana@acme.com",
},
},
timeout=30,
)
data = res.json()Get a record#
/v1/objects/{object}/records/{id}objects_records_getOne record of an Object, with all its values.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.idstringrequired
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
data = res.json()Update a record#
/v1/objects/{object}/records/{id}objects_records_updateChange some of a record's values. Only the keys you send change; every other value is kept. The change is merged in the database under a row lock, so it never overwrites an edit an Agent or a person made a moment earlier. Send null to clear a field. Values are validated like a create (422 names the field).
{ "values": { "stage": "Won", "value": 14500 } }Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.idstringrequired
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
valuesobjectrequiredField key → new value.
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X PATCH "https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"values": {
"stage": "Won",
"value": 14500
}
}'const res = await fetch("https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
values: {
stage: "Won",
value: 14500,
},
}),
});
const data = await res.json();import os, uuid, requests
res = requests.patch(
"https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"values": {
"stage": "Won",
"value": 14500,
},
},
timeout=30,
)
data = res.json()Delete a record#
/v1/objects/{object}/records/{id}objects_records_deleteDelete one record, and every relationship that points to or from it. This cannot be undone.
Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.idstringrequired
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X DELETE "https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"const res = await fetch("https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
},
});
const data = await res.json();import os, uuid, requests
res = requests.delete(
"https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=30,
)
data = res.json()List a record's relationships#
/v1/objects/{object}/records/{id}/relationshipsobjects_relationships_listEverything this record is linked to, in both directions. Each relationship has a relation_key (account, owner, mentioned_in…), a direction (outgoing when this record is the source) and the other end: its Object key and its id.
Works for any Object, built-in ones too: /v1/objects/agents/records/{agent_id}/relationships lists what an Agent is linked to.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.idstringrequired
Query parameters
relation_keystringoptionalOnly this kind of link.
Can fail with 400 · 401 · 404 · 429 · 500 — the shape is in Errors & limits.
curl "https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a/relationships" \
-H "Authorization: Bearer $ATTRIA_API_KEY"const res = await fetch("https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a/relationships", {
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
},
});
const data = await res.json();import os, requests
res = requests.get(
"https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a/relationships",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
},
timeout=30,
)
data = res.json()Link a record to another#
/v1/objects/{object}/records/{id}/relationshipsobjects_relationships_createLink this record (the source) to another record of any Object (the target) under a relation_key — company, owner, related_to… — in snake_case. Both ends must be in this workspace. The target can be a built-in Object: { "object": "agents", "id": "<agent id>" }.
The same source, target and relation_key is one link: sending it again returns the existing one.
{ "relation_key": "company", "target": { "object": "companies", "id": "0b6f…" }, "label": "Buyer" }Send an Idempotency-Key header: a retry with the same key replays the first result instead of doing the work twice.
Path parameters
objectstringrequiredThe Object's key (
deals) or id.idstringrequired
Headers
Idempotency-KeystringoptionalAny unique string (a UUID) per logical write. Retrying with the same key replays the original result.
Bodyapplication/json
relation_keystringrequiredWhat the link means, snake_case:
company.targetobjectrequiredtarget.objectstringrequiredThe Object's key (
deals) or id.target.idstring · uuidrequiredThe target record's id.
labelstringoptionalDefault""max length 200metadataobjectoptionalDefault{}
Can fail with 400 · 401 · 402 · 404 · 409 · 429 · 500 — the shape is in Errors & limits.
curl -X POST "https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a/relationships" \
-H "Authorization: Bearer $ATTRIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"relation_key": "company",
"target": {
"object": "companies",
"id": "5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a"
},
"label": "Buyer"
}'const res = await fetch("https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a/relationships", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ATTRIA_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
relation_key: "company",
target: {
object: "companies",
id: "5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
},
label: "Buyer",
}),
});
const data = await res.json();import os, uuid, requests
res = requests.post(
"https://app.attria.io/api/v1/objects/deals/records/5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a/relationships",
headers={
"Authorization": f"Bearer {os.environ['ATTRIA_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"relation_key": "company",
"target": {
"object": "companies",
"id": "5b7e2c8e-1f0a-4c3d-9e8b-7a6f5d4c3b2a",
},
"label": "Buyer",
},
timeout=30,
)
data = res.json()