Record reference
Relationships and one-way links between records
Record reference attributes are one of the most useful types of attribute. They allow pointing to other records of the same object, or records of other objects. When combined with features such as drill-down filters, it becomes possible to deeply relate the various parts of your data model.
By default, all of the standard objects have at least one record reference:
- Company has
team
(people),associated_deals
(deals) andassociated_workspaces
(workspaces) - Person has
company
(company),associated_deals
(deals) andassociated_users
(users) - Deal has
associated_people
(people) andassociated_company
(company) - User has
person
(person) andworkspace
(workspace) - Workspace has
users
(users) andcompany
(company)
A note about relationship attributes
All of the example attributes above are also relationship attributes.
Relationship attributes describe a relationship between two objects, which appear as a pair of record reference attributes, one on each object. Updating an attribute value on either of the attributes automatically updates the other attribute, which means you don't need to explicitly update both. For example, adding a person to a company by updating the company team
property, will also update the company
property on that person.
Currently, you will be able to see relationship attributes alongside other non-relationship record reference attributes in the API. The attribute type is still marked as record-reference
, but there is an additional property, relationship
, that you can use to distinguish these.
If the attribute is also a relationship attribute, the relationship
property will be an object containing an id
property. In the example below, our company's team
attribute has a relationship with the person's company
attribute:
{
"id": { ... },
"title": "Team",
"api_slug": "team",
"type": "record-reference",
"relationship": {
"id": {
"workspace_id": "14beef7a-99f7-4534-a87e-70b564330a4c",
"object_id": "4e71c40b-7d35-463c-afcb-e339cfd6dbd1", // Person object
"attribute_id": "41252299-f8c7-4b5e-99c9-4ff8321d2f96" // "Company" attribute on Person object
}
},
...
}
If the record reference attribute is not a relationship attribute, the relationship
property will be set to null
.
In the web application, when setting up a relationship, the user specifies whether the relationship is many-to-many, many-to-one, one-to-many or one-to-one. It is possible to discern this relationship by looking at the is_multiselect
property on each attribute: if true
, this attribute is a "many", if false
it is a "one".
Unfortunately, at present it is not possible to create relationship attributes using the API; they can only be created in the web application and then used in the API.
Configuration
Record references are usually constrained to referencing a specific object. For example, you can't specify a deal for the company team
attribute. This is accomplished with the configuration property allowed_object_ids
which is an array of object IDs (slugs are supported when writing this property):
POST /v2/objects/:object/attributes HTTP/1.1
Authorization: Bearer <<oauth2>>
Content-Type: application/json
{
"title": "Owner",
"api_slug": "owner",
"type": "record-reference",
"config": {
"record_reference": {
"allowed_objects": ["person"]
}
}
}
In responses from the API, this appears as allowed_object_ids
, like so:
{
"id": { ... },
"title": "Team",
"api_slug": "team",
"type": "record-reference",
"config": {
"record_reference": {
"allowed_object_ids": [
"4e71c40b-7d35-463c-afcb-e339cfd6dbd1"
]
}
},
...
}
Reading values
Record reference values have two properties, target_object
(the api_slug
representing what kind of object it is) and target_record_id
(the ID of the Record).
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {...},
"attribute_type": "record-reference",
"target_object": "people",
"target_record_id": "891dcbfc-9141-415d-9b2a-2238a6cc012d"
}
Writing values
There are multiple ways to write a record reference. Since it's such a common operation, Attio provides special write functionality if the record reference only allows a single object and that object is one of our standard objects:
- If the allowed object is a company, you can use the
domains
attribute. - If the allowed object is a person, you can use the
email_addresses
attribute. - If the allowed object is a user, you can use the
user_id
attribute. - If the allowed object is a workspace, you can use the
workspace_id
attribute.
{
"associated_company": [
{
"domains": [{"domain": "company.com"}],
"target_object": "companies"
}
]
}
{
"team": [
{
"email_addresses": [{"email_address": "[email protected]"}],
"target_object": "people"
}
]
}
{
"user": [
{
"user_id": [{"value": "my-user-id"}],
"target_object": "users"
}
]
}
{
"workspace": [
{
"workspace_id": [{"value": "my-workspace-id"}],
"target_object": "workspaces"
}
]
}
Furthermore, we allow writing to these attributes using string values.
- If the allowed object is a company, string values will be interpreted as a domain.
- If the allowed object is a person, string values will be interpreted as email addresses.
- If the allowed object is a user, string values will be interpreted as
user_id
text values. - If the allowed object is a workspace, string values will be interpreted as
workspace_id
text values.
{
"associated_company": "company.com"
}
{
"associated_company": ["company.com", "company.net"]
}
{
"team": ["[email protected]"]
}
{
"team": ["[email protected]", "[email protected]"]
}
{
"user": "my-user-id"
}
{
"user": ["my-user-id", "another-user-id"]
}
{
"workspace": "my-workspace-id"
}
{
"workspace": ["my-workspace-id", "another-workspace-id"]
}
If the attribute is multiselect ("many"), you can also pass these as a series of values like so:
It's also possible to write record references using record IDs.
{
"associated_company": [
{
"target_record_id": "99a03ff3-0435-47da-95cc-76b2caeb4dab",
"target_object": "companies"
}
]
}
Note that the write will fail if the target record does not exist, i.e. you can't create the target record automatically. For example, if you tried to assert a person and referenced a company that did not exist, the request would fail. This means that you need to do your writes in reverse-order, e.g. starting with the company that the person is linked to, then creating the person.
Filtering
Record reference values can be filtered by target_object
and target_record_id
using exact equality matches.
{
"filter": {
"company": {
"target_object": "companies",
"target_record_id": "99a03ff3-0435-47da-95cc-76b2caeb4dab"
}
}
}
{
"filter": {
"users": {
"target_object": "97052eb9-e65e-443f-a297-f2d9a4a7f795",
"target_record_id": "5e3fb280-007b-495a-a530-9354bde01de1"
}
}
}
As well as exact equality matches, record reference values also support the $in
operator.
{
"filter": {
"company": {
"target_object": "companies",
"target_record_id": {
"$in": [
"3aeb39cd-fed8-524e-94b8-1300549354ac",
"8c5cd602-1297-45d2-a99c-14ae091cbac3"
]
}
}
}
}
Record references are special, because they are also filterable using "paths", also known as "drill-downs". You can filter records based on attributes of the target records. paths
is an array containing tuples of (object type, attribute slug or ID)
, while constraints
are applied to the attribute identified by the final path element.
For example, we could construct a filter like "find me Companies which have an employee named John":
{
"filter": {
"path": [
["companies", "team"],
["people", "name"]
],
"constraints": {
"first_name": { "$eq": "John" }
}
}
}
Here, we're using the team
attribute on a Company, which is a multi-select record reference to the person record. We then look across at those related people
, and their name
attribute values. Finally, since name
is a (personal) name attribute, we can query against the first_name
property.
Paths can be more complex, and even somewhat recursive. For example, if you had a manager
attribute on the Person object, and it pointed to another Person, you could find people by who their manager's manager's manager was:
{
"filter": {
"path": [
["people", "manager"],
["people", "manager"],
],
"constraints": {
"target_object": "people",
"target_record_id": "managers-manager-id"
}
}
}
Updated about 2 months ago