# Actors
Source: https://docs.attio.com/docs/actors
Keeping track of who did what
In various places in the Attio API, you will see the concept of an "actor". An actor is something that performs actions inside Attio. A workspace member is the prime example of an actor, but you will also encounter apps and automations as actors.
## Types of Actors
An Actor is made up of two things: a type and an ID. The current list of actors types and their corresponding ID format is as follows:
* `workspace-member` - A human user who is signed into a workspace. Comes with a unique UUID. You can get further information about the workspace member, e.g. name, avatar and email address, using the [list workspace members endpoints](/rest-api/endpoint-reference/workspace-members/list-workspace-members).
* `api-token` - An integration using the developer API. Comes with a unique UUID for the token.
* `system` - An internal part of the Attio system. For example, communications intelligence updates are registered with system actors. Always comes with a `null` ID.
Actors are recorded in various parts of the system. Every attribute value write, for example, includes the actor who wrote it, e.g:
```json The actor who wrote a particular value theme={"system"}
{
"created_by_actor": {
"type": "workspace-member",
"id": "175bec0c-f06a-4c45-9962-7a7a6be28b8a"
}
}
```
Actors are often encountered via the "actor reference" attribute type. Every standard object includes a `created_by` attribute of this type. For more information, please see the [actor reference](/rest-api/attribute-types/attribute-types-actor-reference) documentation.
# Archiving vs deleting
Source: https://docs.attio.com/docs/archiving-vs-deleting
Attio uses two separate but related concepts: **archiving** and **deleting**.
**Deleting** should be considered permanent. Any data you delete will eventually be removed from our servers entirely, without the ability for restoration. As such, deletes should be handled extremely carefully.
When using our API, deleting occurs using the conventional REST semantics of the `DELETE` HTTP verb.
**Archiving** is softer, allowing data to remain in the system, albeit in a more hidden state. The exact behaviour of archived data will depend on the object you are archiving, but the behaviour will be documented for the given endpoint.
When using our API, archiving occurs by setting the `is_archived` property to `true`, and restored by setting it to `false`.
# Default values
Source: https://docs.attio.com/docs/default-values
Attribute definitions on objects and lists support default values which are used to pre-populate values on records and entries.
There are two kinds of default value: static and dynamic.
## Static defaults
Static values are raw values that are used directly without transformation. For example, you might want to always fill the “Deal Status” property on new entries in your “Sales” list to the value “Lead”.
Static values are supported on all attribute types. The `template` property on default values must match the type of the attribute. These types are documented in our endpoint reference docs.
## Dynamic defaults
Dynamic values are used to generate values on the fly, depending on context. For example, you might want to fill in an “Owner” property to the current user, or set a due date to one week in the future.
Dynamic values take the form of a string. The currently supported list of dynamic value templates, broken down by attribute type, is as follows:
* `actor-reference` attributes, with the default value `current-user`
* `timestamp` or `date` attributes, with an [ISO 8601 Duration](https://tc39.es/proposal-temporal/docs/duration.html) e.g. `“P1M”` for one month in the future
Other attribute types do not currently support dynamic default values.
# Authorize
Source: https://docs.attio.com/docs/oauth/authorize
GET https://app.attio.com/authorize
Query Parameters
Your app's client ID. You can find this in your app's settings pages at
[build.attio.com](https://build.attio.com).
The response type. This should always be `"code"`.
The URL to redirect to after the user authorizes access to your app. This URL must exactly match
one of the registered redirect URLs in your app's settings pages at
[build.attio.com](https://build.attio.com).
A random string to prevent CSRF attacks. Set this when starting the OAuth flow and verify it
matches when the user is redirected back to your app.
Response
After the user approves the connection, they are redirected to the `redirect_uri` with a `code`
query parameter. If provided, the original `state` is also included.
# Introspect
Source: https://docs.attio.com/docs/oauth/introspect
POST https://app.attio.com/oauth/introspect
Check whether an access token is valid, and if so, what scopes and identity it grants. A bearer token should be passed in the `Authorization` header of the request.
Response
Inactive Token
Whether the token is currently active and usable. Returns `false` for invalid or expired tokens.
Active Token
Whether the token is currently active and usable. Returns `true` for valid tokens.
A space-separated list of scopes associated with this token.
The app ID of the OAuth app that requested this token.
The type of token. Always `"Bearer"` for tokens acquired via the OAuth 2.0 flow.
The time at which this token will expire, if set, as a number of seconds since January 1 1970 UTC.
The time at which this token was issued, as a number of seconds since January 1 1970 UTC.
Since Bearer tokens grant workspace-level permissions, this property contains the workspace ID.
The intended audience for this token. For Bearer tokens this is the same as the `client_id`.
The issuer of the token. Always `"attio.com"`.
The ID of the workspace member who authorized this token initially.
The ID of the workspace the token is scoped to.
The name of the workspace the token is scoped to.
The slug of the workspace the token is scoped to.
The logo URL of the workspace the token is scoped to.
# Token
Source: https://docs.attio.com/docs/oauth/token
POST https://app.attio.com/oauth/token
Request
Your app's client ID. You can find this in your app's settings pages at
[build.attio.com](https://build.attio.com).
Your app's client secret. You can find this in your app's settings pages at
[build.attio.com](https://build.attio.com).
The grant type. This should always be `"authorization_code"`.
The code which you received after being redirected from the `/authorize` endpoint.
Response
An access token for the workspace which can be used to make authenticated requests to the Attio
REST API.
The type of token. Always `"Bearer"`.
# Objects and lists
Source: https://docs.attio.com/docs/objects-and-lists
At the core of Attio is our powerful and flexible data model, which allows you to define custom objects and lists with a wide array of attribute types.
Objects can be used to model your domain entities, while lists can be used to aggregate them together and model business processes. However, both objects and lists can have their own attributes. For example, you might be using the deal object to keep track of the various deals you have in progress, but you could also create a list of deals with some additional attributes only relevant to a deal in that list.
## Objects
Objects are the data types used to store facts about your customer. By themselves, they contain little information other than a name, but records and attributes are scoped to a single object. Attio comes with two objects enabled by default, people and companies, and three optional objects: deal, user and workspace.
In relational database terms, objects are roughly tables. In object-orientated terms, they are analogous to classes. Note that an instance of an object is called a record (see below).
For more information about Attio objects, please [see our help center guide](https://attio.com/help/reference/workspace/objects).
### Records
Records are an instantiation of an object, e.g. a specific person or a specific company. In relational database terms, a record is roughly a row in a table. In object-orientated terms, they are analogous to an object (an instantiated class).
### Attributes
Attributes sit on objects and lists and describe what data we can store. Some attributes, such as the name on a person, are system defined. Others, you define yourself, either in Attio’s UI or over the API. Attributes are one of many types such as text, number, select, or currency. In relational database terms, an attribute is roughly a column in a table.
## Lists & entries
Lists are composed of multiple rows, known as "entries", each of which corresponds to a single record. Entries are created by adding a record to a list. Lists can be used to model a particular process. For example, you might have a "Sales" list that contains the companies that you have relationships with.
# Overview
Source: https://docs.attio.com/docs/overview
Start building Attio Apps
Attio is a revolutionary CRM platform which is highly customisable, incredibly powerful and
data-driven. In these guides, you can find everything you need to build powerful integrations,
automations and data pipelines on top of Attio.
Our docs cover guides, examples, references and code to help you build apps and share them with
Attio's customers or for your own workspace.
The Attio Developer Platform consists of four parts:
This allows developers to embed Typescript React applications within the Attio interface and run
server functions with access to our REST API.
This allows developers to build apps that read and write information to and from Attio workspaces.
You can also subscribe to events in realtime using webhooks.
Manage Attio via AI. Connect Claude, ChatGPT or your AI tool of choice to search, update and
manage your Attio workspace.
Run SQL queries against your workspace data.
# Slugs and IDs
Source: https://docs.attio.com/docs/slugs-and-ids
Attio uses [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) (universally unique identifiers) to identify most of the objects in our system. We can also identify many API objects by their slug property. Slugs have the advantage that they are human-readable. For standard objects and attributes, slugs are also consistent across workspaces.
## IDs and Uniqueness
Entities returned from Attio’s API contain an `id` property which is composed of one or more sub-IDs. The sub-IDs are each a UUID that identifies either the entity or one of its parents.
```jsonc Example ID from a record response theme={"system"}
{
"data": {
"id": {
"workspace_id": "5821c091-cf04-4aab-a72a-f1646dbd6841",
"object_id": "7c430b6d-fa5b-48c6-bfa7-9520c088c7bc",
"record_id": "d2c2f990-3af0-4be5-808a-5605549e787f"
}
// ...
}
}
```
Uniqueness of an ID is only guaranteed when the ID is taken as a whole, using all sub-IDs. For example, it is unsafe to assume in the example above that the record is the only record with `record_id=”d2c2f990-3af0-4be5-808a-5605549e787f”`.
The safe assumption is that the record is the only record with `record_id=”d2c2f990-3af0-4be5-808a-5605549e787f”` *and* `object_id=”7c430b6d-fa5b-48c6-bfa7-9520c088c7bc"` *and* `workspace_id="5821c091-cf04-4aab-a72a-f1646dbd6841"`.
In practice, such collisions will be rare, but they are important to bear in mind. This is especially true if you are building large integrations which operate on the data of many workspaces.
If you are only operating on data from your own workspace, you can essentially disregard the `workspace_id` key.
## Slugs
For ergonomics and readability, Attio’s API utilises slugs across a variety of entities such as list, objects and attributes. Path parameters, query parameters, request bodies and responses will utilise a unique slug instead of an ID where appropriate.
For example, a request to the [create record endpoint](/rest-api/endpoint-reference/records/create-a-record) accepts values keyed by either the attribute slug or attribute ID and returns values keyed by slug.
```jsonc Slug form theme={"system"}
{
"data": {
"values": {
"email_addresses": [
{
"email_address": "[email protected]"
}
]
// other values...
}
}
}
```
```jsonc ID form theme={"system"}
{
"data": {
"values": {
"8a8ad54f-9314-4bdb-b867-e62d02a7d333": [
{
"email_address": "[email protected]"
}
]
// other values...
}
}
}
```
```jsonc Response theme={"system"}
{
"data": {
"values": {
"email_addresses": [
{
"active_from": "2023-06-01T15:49:08.524000000Z",
"active_until": null,
"created_by_actor": {
"type": "api-token",
"id": "d475d597-2900-4c93-841c-9f83154f21dc"
},
"original_email_address": "[email protected]",
"email_address": "[email protected]",
"email_domain": "bell-labs.com",
"email_root_domain": "bell-labs.com",
"email_local_specifier": "r.hamming",
"attribute_type": "email-address"
}
]
// other values...
}
// other properties on data...
}
}
```
Slugs are set explicitly over the API, or created automatically when entities are created through the UI. Slugs are not updated when entities are renamed through the UI, so they can be reliably used once seen. They are always unique across entities. For example, there cannot be two attributes with the same slug on the same list.
Slugs of system attributes and objects are consistent across time and across workspaces. Non-system slugs are mutable, so care should be taken when modifying them in case they break any integrations relying upon them. If you would like to provide resilience against such changes, please use IDs when looking up objects instead.
Slugs can be found on the following entities:
* Object (`api_slug`)
* Attribute (`api_slug`)
* List (`api_slug`)
* Status (`title`)
* Select Option (`title`)
***
# Overview
Source: https://docs.attio.com/docs/standard-objects/standard-objects
Companies and people are available in all workspaces
Every Attio workspace starts with a company and person object, and admins can optionally enable the deal, user and workspace standard objects if it meets their needs.
These 5 objects come with a pre-defined set of attributes. As a developer, you can rely on being able to read and write from/to these predefined (or "system") attributes. Admins can also add custom attributes to any of these objects, or create new custom objects with custom attributes on those.
Developers can interact with both standard and custom objects in the same ways, using e.g. the [assert a Record](/rest-api/endpoint-reference/records/assert-a-record) API, but the following pages go into more detail about exactly which attributes you might find and how to read/write them for these.
## Attributes on every object
| Attribute | Type | Writable? | Notes |
| --------------------------------------------- | --------------- | --------- | ------------------------------------------------------------------------------------------------------- |
| `created_at` | Timestamp | No | When the record was created, defaults to current timestamp (see [Default values](/docs/default-values)) |
| `created_by` (which actor created the record) | Actor reference | No | Which actor created the record, defaults to current user in web application, or the integration in API |
| `record_id` | Text (UUID) | No | The unique identifier for the record, available as an attribute value to allow easy querying |
# Companies
Source: https://docs.attio.com/docs/standard-objects/standard-objects-companies
An object to represent businesses: customers, partners, peers...
```yaml Company configuration theme={"system"}
api_slug: companies
singular_noun: Company
plural_noun: Companies
```
The company object is available in every Attio workspace. Companies represent businesses, such as customers, partners, suppliers, etc.
When creating a person, a related company record will automatically be generated or matched based on the domain of the person's email address, but they can also be created manually via the web application or the API.
Company records are enriched, which means that Attio will automatically populate additional attributes, and those enriched values cannot be overridden by API users. Additionally, some enriched attributes may be hidden from the API depending on the workspace billing plan.
## Writable attributes
The following attributes can always be read and written using the API:
| Attribute | Slug | Type | Traits |
| --------------------- | ----------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Domains | `domains` | [Domain](/rest-api/attribute-types/attribute-types-domain) | Unique, multiselect |
| Name | `name` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Description | `description` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Team | `team` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Person -> company`, multiselect |
| Categories | `categories` | [Select](/rest-api/attribute-types/attribute-types-select) | Multiselect |
| Primary location | `primary_location` | [Location](/rest-api/attribute-types/attribute-types-location) | |
| AngelList | `angellist` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Facebook | `facebook` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Instagram | `instagram` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| LinkedIn | `linkedin` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Twitter | `twitter` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Associated deals | `associated_deals` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Deal -> associated_companies` (only visible if [Deals](/docs/standard-objects/standard-objects-deals) activated |
| Associated workspaces | `associated_workspaces` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Workspace -> company` (only visible if [Workspaces](/docs/standard-objects/standard-objects-workspaces) activated) |
It's important to note that while standard attributes like `domains` have unique properties by default, you cannot create new custom attributes with a unique constraint for Company objects.
# Deals
Source: https://docs.attio.com/docs/standard-objects/standard-objects-deals
An object to represent deals involving people & companies
```yaml Deal configuration theme={"system"}
api_slug: deals
singular_noun: Deal
plural_noun: Deal
```
The Deal object is available in every Attio workspace, but disabled by default. It can only be activated by a workspace admin, in the [objects settings](https://app.attio.com/_/settings/data/objects) page.
## Writeable attributes
| Attribute | Slug | Type | Traits |
| ------------------ | -------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| Name | `name` | [Text](/rest-api/attribute-types/attribute-types-text) | Required |
| Deal stage | `stage` | [Status](/rest-api/attribute-types/attribute-types-status) | Required - by default one of `"Lead"`, `"In Progress"`, `"Won 🎉"`, `"Lost"` (workspaces can configure these status options) |
| Deal owner | `owner` | [Actor reference](/rest-api/attribute-types/attribute-types-actor-reference) | Required |
| Deal value | `value` | [Currency](/rest-api/attribute-types/attribute-types-currency) | Defaults to USD, can be changed |
| Associated people | `associated_people` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Person -> associated_deals` |
| Associated company | `associated_company` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Company -> associated_deals` |
Note that deals do not have a unique attribute by default. This means that they cannot be automatically asserted without adding an additional unique attribute.
# People
Source: https://docs.attio.com/docs/standard-objects/standard-objects-people
An object to represent human beings
```yaml People configuration theme={"system"}
api_slug: people
singular_noun: Person
plural_noun: People
```
The person object is available in every Attio workspace. When creating a person, a related company record will automatically be generated or matched based on the domain of the person's email address, but they can also be created manually via the web application or the API.
Person records are enriched, which means that Attio will automatically populate additional attributes, and those values cannot be overridden by API users. Additionally, some enriched attributes may be hidden from the API depending on the workspace billing plan.
## Writeable attributes
| Attribute | Slug | Type | Traits |
| ---------------- | ------------------ | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Email addresses | `email_addresses` | [Email address](/rest-api/attribute-types/attribute-types-email-address) | Unique, multiselect |
| Name | `name` | [(Personal) name](/rest-api/attribute-types/attribute-types-personal-name) | |
| Company | `company` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Company -> team` |
| Description | `description` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Job title | `job_title` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Phone numbers | `phone_numbers` | [Phone number](/rest-api/attribute-types/attribute-types-phone-number) | Multiselect |
| Primary location | `primary_location` | [Location](/rest-api/attribute-types/attribute-types-location) | |
| AngelList | `angellist` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Facebook | `facebook` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Instagram | `instagram` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| LinkedIn | `linkedin` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Twitter | `twitter` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Associated deals | `associated_deals` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Deal -> associated_people` (only available if [Deals](/docs/standard-objects/standard-objects-deals) activated) |
| Associated users | `associated_users` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `User -> person` (only available if [Users](/docs/standard-objects/standard-objects-users) activated) |
# Users
Source: https://docs.attio.com/docs/standard-objects/standard-objects-users
An object to represent users of your product
```yaml User configuration theme={"system"}
api_slug: users
singular_noun: User
plural_noun: Users
```
The User object is available in every Attio workspace, but disabled by default. It can only be activated by a workspace admin, in the [Objects settings](https://app.attio.com/_/settings/data/objects) page.
Users represent a user of your product. They are related to a [Person](/docs/standard-objects/standard-objects-people), have an email address, and an ID attribute that is defined by your system. Users are grouped together in Workspaces.
## Writeable attributes
| Attribute | Slug | Type | Traits |
| --------------------- | ----------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
| Person | `person` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Person -> associated_users` |
| Primary email address | `primary_email_address` | [Text](/rest-api/attribute-types/attribute-types-text) | Required, unique - this is usually their login email in your system |
| ID | `user_id` | [Text](/rest-api/attribute-types/attribute-types-text) | Required, unique - this is usually their ID in your system |
| Workspaces | `workspace` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Workspace -> users` |
Users can be asserted by either their `primary_email_address` or `user_id` unique attributes, or you can add your own.
# Workspaces
Source: https://docs.attio.com/docs/standard-objects/standard-objects-workspaces
An object to group users of your product
```yaml Workspace configuration theme={"system"}
api_slug: workspaces
singular_noun: Workspace
plural_noun: Workspaces
```
The Workspace object is available in every Attio workspace, but disabled by default. It can only be activated by a workspace admin, in the [objects settings](https://app.attio.com/_/settings/data/objects) page.
Workspaces represent a grouping of users, or an account, in your product. Workspaces can belong to a [company](/docs/standard-objects/standard-objects-companies) and have multiple [users](/docs/standard-objects/standard-objects-users).
## Writeable attributes
| Attribute | Slug | Type | Traits |
| ---------- | -------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
| ID | `workspace_id` | [Text](/rest-api/attribute-types/attribute-types-text) | Unique, required |
| Name | `name` | [Text](/rest-api/attribute-types/attribute-types-text) | |
| Users | `users` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `User -> workspaces`, multiselect |
| Company | `company` | [Record reference](/rest-api/attribute-types/attribute-types-record-reference) | Relationship, inverse of `Company -> associated_workspaces` |
| Avatar URL | `avatar_url` | [Text](/rest-api/attribute-types/attribute-types-text) | Similar to [Company](/docs/standard-objects/standard-objects-companies) `logo_url` |
Workspaces have a single unique attribute, `workspace_id`, which can be used for assertions, or you can add your own.
# Users and workspaces
Source: https://docs.attio.com/docs/users-and-workspaces
*Not to be confused with our standard objects [users](/docs/standard-objects/standard-objects-users) and [workspaces](/docs/standard-objects/standard-objects-workspaces)*
A **user** is a login account with Attio. For example, a person who has signed up to Attio with the email `person@company.com`.
A **workspace** is a company account with objects, records, lists and such. Every user has at least one workspace created when signing up for the first time. Some users will have multiple workspaces when they are members of large organisations with multiple workspaces, or for testing and development purposes.
Since users can join multiple workspaces, we use the **workspace member** concept to represent a single user in a single workspace (this also encapsulates their permissions, such as whether they are an admin).
All access to the Attio API is scoped to a single workspace, so you will never interact with users directly. Instead, user-level data such as email addresses are accessed through [workspace member APIs](/rest-api/endpoint-reference/workspace-members/list-workspace-members).
# Building workflow blocks
Source: https://docs.attio.com/guides/building-workflow-blocks
How to add custom triggers and steps to Attio Workflows using the App SDK
This guide walks through building a step block and a trigger block from scratch. For an overview of how blocks work, see [Workflow blocks](/sdk/workflow-blocks/overview). For individual API details, see the [reference pages](/sdk/workflows/define-workflow-block).
## How workflow blocks work
A workflow block is a reusable action that workspace members can drag into the Attio Workflows builder. Once your app is installed, your blocks appear in the same block picker as Attio's built-in blocks. A workspace member configures the block once (e.g. enters an API key or a list ID), then connects it to other blocks to form an automation.
Every block is made of three files that serve distinct roles:
| File | Runs in | Purpose |
| --------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------- |
| `block.ts` | Shared (browser + server) | Declares the block's identity, display name, and the fields the workspace member fills in |
| `execute.ts` / `activate.ts` etc. | Server only | The logic that runs when Attio calls your block |
| `configurator.tsx` | Browser only | The form the workspace member sees in the workflow editor |
These files are separate by design. `block.ts` is loaded in the browser to render the editor UI *and* on the server to run your logic, so it cannot import anything browser-specific or server-specific. The handler files (`.ts`) are server-only. `configurator.tsx` is browser-only and has no access to server APIs.
When in doubt, start with a step block — triggers involve managing external subscriptions across activation and deactivation, which adds complexity. See [Workflow blocks overview](/sdk/workflow-blocks/overview) for a comparison of the two types.
***
## Building a step block
A step block runs inline when a workflow reaches it. The workspace member configures it once (for example, entering an email address or an API project ID) and then the block executes those values each time a run passes through.
### 1. Define the block
Create `src/blocks/my-step/block.ts`. This file declares the block's stable identity and the configuration fields workspace members will fill in. It is imported by both the browser (to render the editor UI) and the server (to type-check your handlers), so it must not import anything from `attio/client` or `attio/server`.
```ts src/blocks/my-step/block.ts theme={"system"}
import {Workflows} from "attio"
export default Workflows.defineWorkflowBlock({
type: "step",
id: "send-notification",
title: "Send notification",
description: "Send a notification to an external service.",
configSchema: Workflows.ConfigSchema.struct({
recipient_email: Workflows.ConfigSchema.emailAddress(),
message: Workflows.ConfigSchema.string(),
}),
})
```
The `id` is the stable internal identifier for this block; it must be unique across all blocks in your app and must not change once the block is in use, because workflow definitions reference it by ID. The `title` and `description` appear in the block picker.
The `configSchema` describes the form the workspace member fills in. Each field in `configSchema` maps to a typed property on the `config` argument in your handlers. If you add a `recipient_email` field, `config.recipient_email` is typed as an email address value in `execute.ts`; TypeScript will catch mismatches. See [Config schema](/sdk/workflows/config-schema) for all available field types.
### 2. Write the execute handler
Create `src/blocks/my-step/execute.ts`. Attio calls this function each time a running workflow reaches your block. It receives the values the workspace member configured (`config`) and contextual metadata about the current run (`metadata`).
```ts src/blocks/my-step/execute.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockExecute(block, async ({config, metadata}) => {
const response = await fetch("https://api.example.com/notify", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
to: config.recipient_email,
body: config.message,
}),
})
if (!response.ok) {
return {
type: "error",
errorMessage: `Notification failed: ${response.statusText}`,
retryable: true,
}
}
return {type: "outcome", id: "sent", data: null}
})
```
The return value tells Attio what to do next:
| Return | Effect |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `{type: "outcome", id, data}` | Continue the workflow run down the named branch |
| `{type: "error", errorMessage, retryable}` | Surface an error in the Attio UI. Set `retryable: true` for transient failures (e.g. rate limits, temporary outages) that are safe to retry |
| `{type: "exit"}` | Stop the current run silently, without raising an error |
| `{type: "defer"}` | Pause the run until an external system POSTs to `metadata.finishCallbackUrl` |
See [Executing a step](/sdk/workflows/define-workflow-block-execute) for the full API.
`metadata.uniqueExecutionId` is stable across retries of the same execution. Use it as an
idempotency key when calling external APIs so that a retried execution does not create duplicate
side-effects.
### 3. Wire up a configurator
Create `src/blocks/my-step/configurator.tsx`. This is the form workspace members see in the workflow editor when they add or edit your block. It runs only in the browser, so it has no access to server-side APIs.
```tsx src/blocks/my-step/configurator.tsx theme={"system"}
import {Workflows} from "attio/client"
import block from "./block"
export default Workflows.defineConfigurator(block, (workflowBlock) => {
const {EmailAddressInput, TextInput, Outcome} = Workflows.useConfigurator(workflowBlock)
return (
<>
>
)
})
```
`Workflows.useConfigurator(workflowBlock)` returns typed input components bound to the block's config schema. The `name` prop on each input is type-checked against the schema; rename a field in `block.ts` and TypeScript will show a compile error in `configurator.tsx` until you update it here too. The input component names (`EmailAddressInput`, `TextInput`, etc.) match the field types declared in the schema.
The `Outcome` component tells the editor that a branch with that `id` exists. Its `schema` prop declares what data that branch carries, which becomes the typed variables downstream steps can reference. For example, `schema={{task_id: Workflows.OutcomeSchema.string()}}` makes `task_id` available as a variable to any step wired to that branch. The `data` object returned from `execute` must have the same shape as the declared schema. When there is only one outcome, `label` is optional; the editor uses the block's title as the branch label.
### Loading data into the configurator
When your configurator loads data from a third party — say, to populate a dropdown — prefer a combobox [options provider](/sdk/workflows/configurator#combobox-options) passed to the `options` prop:
```tsx theme={"system"}
```
For anything else, use [`useAsyncCache`](/sdk/data-fetching/use-async-cache), which suspends until the data is ready. See [Loading data](/sdk/workflows/configurator#loading-data) for the full guidance.
### Multiple outcomes
Return different `id` values from `execute` for different code paths. Each `id` becomes a named branch in the workflow editor that workspace members wire to downstream steps. Declare each `id` with the `Outcome` component so the editor knows the branch exists and what data it exposes.
Here is a step block that creates a Todoist task and branches based on the result:
```ts src/blocks/create-todoist-task/block.ts theme={"system"}
import {Workflows} from "attio"
export default Workflows.defineWorkflowBlock({
type: "step",
id: "create-todoist-task",
title: "Create Todoist task",
description: "Add a task to a Todoist project.",
configSchema: Workflows.ConfigSchema.struct({
project_id: Workflows.ConfigSchema.string(),
content: Workflows.ConfigSchema.string(),
}),
})
```
```ts src/blocks/create-todoist-task/execute.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockExecute(block, async ({config, metadata}) => {
const response = await fetch("https://api.todoist.com/rest/v2/tasks", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
project_id: config.project_id,
content: config.content,
}),
})
if (response.status === 404) {
return {type: "outcome", id: "project-not-found", data: null}
}
if (!response.ok) {
return {
type: "error",
errorMessage: `Failed to create task: ${response.statusText}`,
retryable: response.status === 429,
}
}
const task = (await response.json()) as {id: string; url: string}
return {
type: "outcome",
id: "created",
data: {task_id: task.id, task_url: task.url},
}
})
```
```tsx src/blocks/create-todoist-task/configurator.tsx theme={"system"}
import {Workflows} from "attio/client"
import block from "./block"
export default Workflows.defineConfigurator(block, (workflowBlock) => {
const {TextInput, Outcome} = Workflows.useConfigurator(workflowBlock)
return (
<>
>
)
})
```
The editor shows `created` and `project-not-found` as separate connectable branches. Workspace members wire each branch to a different downstream step, for example logging a record update on `created` and sending an alert on `project-not-found`. The `task_id` and `task_url` values from the `created` outcome become typed variables any step downstream on that branch can use.
### Deferred execution
If your step needs to pause and wait for an async callback (for example, a background job that posts back when it finishes, or a human approval), return `{type: "defer"}` from `execute` and add a `finish.ts` handler.
Before returning `{type: "defer"}`, pass `metadata.finishCallbackUrl` to the external service as the URL to POST to when the work is complete. Attio immediately checkpoints and suspends the run; no server resources are held while waiting. When the external service POSTs to that URL, Attio calls `finish` with the incoming request. Return an outcome to resume the run, or `{type: "no-op"}` to stay deferred and wait for the next POST.
```ts src/blocks/my-step/execute.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockExecute(block, async ({config, metadata}) => {
await fetch("https://api.example.com/jobs", {
method: "POST",
body: JSON.stringify({
task: config.task_name,
callback_url: metadata.finishCallbackUrl,
}),
})
return {type: "defer"}
})
```
```ts src/blocks/my-step/finish.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockFinish(block, async (req, {config, metadata}) => {
const payload = await req.json()
if (payload.status === "pending") {
return {type: "no-op"} // stay deferred, wait for next POST
}
return {type: "outcome", id: "completed", data: null}
})
```
See [Finishing a deferred step](/sdk/workflows/define-workflow-block-resume-callback) for the full API reference.
***
## Building a trigger block
A trigger block starts a new workflow run when something happens in an external system. Unlike a step block, which has a single `execute` handler, a trigger block manages an ongoing subscription to an external service across the full lifecycle of the workflow:
1. **Activate**: called once when the workspace member enables the workflow. Register your webhook or subscription with the external service here.
2. **Trigger**: called for every incoming event at `triggerCallbackUrl`. Decide whether the event should start a run.
3. **Deactivate**: called once when the workflow is disabled or deleted. Remove the subscription here.
Attio provides a stable `triggerCallbackUrl` per workflow version. Any HTTP POST to that URL is forwarded to your `trigger` handler. You never expose that URL directly to workspace members; you register it with the external service inside `activate`.
### 1. Define the block
```ts src/blocks/my-trigger/block.ts theme={"system"}
import {Workflows} from "attio"
export default Workflows.defineWorkflowBlock({
type: "trigger",
id: "new-form-submission",
title: "New form submission",
description: "Start a workflow when a form is submitted.",
configSchema: Workflows.ConfigSchema.struct({
form_id: Workflows.ConfigSchema.string(),
}),
})
```
### 2. Write the activate handler
Create `src/blocks/my-trigger/activate.ts`. Attio calls this once when the workspace member enables the workflow. Use it to register your webhook with the upstream service, passing `metadata.triggerCallbackUrl` as the destination.
Store `metadata.uniqueActivationId` alongside your webhook registration so you can look it up during deactivation. This ID is stable across retries of the same `activate` call, so it is safe to use as an idempotency key.
```ts src/blocks/my-trigger/activate.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockActivate(block, async ({config, metadata}) => {
const response = await fetch(`https://api.example.com/forms/${config.form_id}/webhooks`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
url: metadata.triggerCallbackUrl,
ref: metadata.uniqueActivationId,
}),
})
if (!response.ok) {
return {type: "error", errorMessage: "Failed to register webhook"}
}
return {type: "complete"}
})
```
If `activate` returns `{type: "error"}`, the workflow is not enabled and `deactivate` is never called for this version. See [Registering a trigger](/sdk/workflows/define-workflow-block-activate).
### 3. Handle incoming events
Create `src/blocks/my-trigger/trigger.ts`. Attio calls this once for every HTTP POST that arrives at `triggerCallbackUrl`. Your handler reads the payload and decides whether the event should start a workflow run.
This handler should be fast and free of heavy side-effects; its only job is to inspect the incoming event and signal whether to start a run.
```ts src/blocks/my-trigger/trigger.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockTrigger(block, async (req, {config, metadata}) => {
const payload = await req.json()
// Ignore events that don't match the configured form
if (payload.form_id !== config.form_id) {
return {type: "no-op"}
}
return {
type: "outcome",
id: "submitted",
data: null,
}
})
```
Return `{type: "outcome"}` to start a new workflow run, or `{type: "no-op"}` to silently ignore the event. See [Receiving a trigger event](/sdk/workflows/define-workflow-block-on-trigger-callback).
### 4. Write the deactivate handler
Create `src/blocks/my-trigger/deactivate.ts`. Attio calls this once when the workflow is disabled or the workspace member edits and re-enables the workflow (which deactivates the old version before activating the new one). Use `metadata.uniqueActivationId` to look up and remove the webhook registration you created in `activate`.
```ts src/blocks/my-trigger/deactivate.ts theme={"system"}
import {Workflows} from "attio/server"
import block from "./block"
export default Workflows.defineWorkflowBlockDeactivate(block, async ({config, metadata}) => {
await fetch(
`https://api.example.com/forms/${config.form_id}/webhooks/${metadata.uniqueActivationId}`,
{
method: "DELETE",
},
)
return {type: "complete"}
})
```
See [Deactivating a trigger](/sdk/workflows/define-workflow-block-deactivate).
### 5. Wire up a configurator
Same pattern as step blocks:
```tsx src/blocks/my-trigger/configurator.tsx theme={"system"}
import {Workflows} from "attio/client"
import block from "./block"
export default Workflows.defineConfigurator(block, (workflowBlock) => {
const {TextInput, Outcome} = Workflows.useConfigurator(workflowBlock)
return (
<>
>
)
})
```
***
## Accessing third-party services
Workflow block handlers run in Attio's server sandbox, the same environment as [server functions](/sdk/server/server-functions). You have access to `fetch()` and can use [connections](/sdk/server/connections/connections) to authenticate on behalf of the user.
To require a user connection, set `requireUserConnection: true` in `Workflows.defineWorkflowBlock`, then call `getUserConnection()` in your handlers.
```ts theme={"system"}
import {getUserConnection} from "attio/server"
const token = getUserConnection().value
```
See [Authenticating to external services](/sdk/guides/authenticating-to-external-services) for the full setup.
## Calling the Attio REST API
Use `ATTIO_API_TOKEN` to call the Attio REST API from any handler:
```ts theme={"system"}
import {ATTIO_API_TOKEN} from "attio/server"
await fetch("https://api.attio.com/v2/records", {
headers: {Authorization: `Bearer ${ATTIO_API_TOKEN}`},
})
```
See the [REST API overview](/rest-api/overview) for available endpoints.
## Reference
* [File structure](/sdk/workflows/file-structure): complete folder layout for trigger and step blocks
* [Block definition](/sdk/workflows/define-workflow-block): block identity and config schema
* [Config schema](/sdk/workflows/config-schema): all available schema field types
* [Outcome schema](/sdk/workflows/outcome-schema): typing the `data` field in return values
* [Executing a step](/sdk/workflows/define-workflow-block-execute): step execute handler
* [Registering a trigger](/sdk/workflows/define-workflow-block-activate): trigger activate handler
* [Receiving a trigger event](/sdk/workflows/define-workflow-block-on-trigger-callback): trigger event handler
* [Deactivating a trigger](/sdk/workflows/define-workflow-block-deactivate): trigger deactivate handler
* [Finishing a deferred step](/sdk/workflows/define-workflow-block-resume-callback): deferred step handler
# Attio MCP
Source: https://docs.attio.com/mcp/overview
Learn how to connect AI agents to your Attio workspace.
Connect your AI tools to Attio using the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction), an open standard that lets AI assistants interact with your Attio CRM workspace.
## What is Attio MCP?
Attio MCP is Attio's hosted MCP server that gives AI tools secure access to your Attio workspace. It's designed to work seamlessly with popular AI assistants like Claude, ChatGPT, Cursor, and more.
### Why use Attio MCP?
* **Easy setup** — Connect through simple OAuth with one-click installation for supported AI tools
* **Natural language CRM management** — Manage your entire Attio workspace through conversational AI instead of clicking through multiple screens
* **Full workspace access** — AI tools can search, read, create, and update records across your CRM—people, companies, deals, tasks, and notes
* **Optimized for AI** — Built specifically for AI agents with efficient data formatting and human-readable responses
* **Secure by design** — Authenticates as your Attio user with OAuth, and read operations are auto-approved while write operations request confirmation
### What can you do with Attio MCP?
* **Contact & company lookup** — Find contacts at specific companies, look up email addresses, and discover decision makers
* **Activity tracking** — Log notes from calls and meetings, view interaction history, and track engagement
* **Task management** — Create follow-up tasks, set deadlines, and manage your to-do list
* **Pipeline management** — Search and filter records by stage, funding round, role, location, and more
* **Meeting intelligence** — Search call recordings, access transcripts, and find meeting notes
## Getting Started
### Prerequisites
Before connecting Attio MCP, ensure you have:
1. An active Attio workspace
2. An MCP-compatible AI client (Claude Desktop, Claude.ai, ChatGPT, Cursor, etc.)
### Connecting to Attio MCP
Attio MCP uses OAuth authentication, so you simply log in with your Attio account—no API keys required.
#### Add as a Custom Connector
In your AI tool of choice, add the following URL as a custom MCP connector:
```
https://mcp.attio.com/mcp
```
**For Claude Desktop or Claude.ai:**
1. Open Settings -> Connectors -> Browse connectors
2. Search for Attio
3. Install Attio
**For ChatGPT:**
1. Open Apps
2. Search for Attio
3. Connect Attio
**For other MCP clients:**
Add `https://mcp.attio.com/mcp` as a remote MCP server and complete the OAuth authentication when prompted.
Once connected, you'll be authenticated as your Attio user with access to your workspace data.
***
## Supported Tools
Now that you have installed Attio MCP, let's explore how AI assistants can use these tools to search, create, and manage content in your Attio workspace.
These tools work seamlessly together through prompts, and their real power comes from combining them. With a single prompt, you can search your workspace, create new records, log notes, and manage tasks. Understanding these building blocks helps you craft efficient prompts that tackle complex workflows.
### Records & Objects
| Tool | Description | Example Prompts |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `search-records` | Full-text search for records (people, companies, deals, etc.) by name, email, domain, or other indexed attributes | • "Find all contacts at Stripe" • "What's the email for Namit Chadha?" • "Show me everything we know about TheSwarm" |
| `list-records` | List records in a given object with optional filtering and sorting | • "List all open deals" • "Show me companies created this month" |
| `get-records-by-ids` | Retrieve full details for specific records by their IDs | • "Get the complete profile for this contact" • "Show me all details for these three companies" |
| `create-record` | Create a new record (person, company, deal, or custom object) | • "Add a new contact: John Smith, CTO at Acme Corp" • "Create a company record for Linear" |
| `upsert-record` | Create or update a record using a matching attribute (e.g., email or domain) | • "Update the company record for stripe.com with their new funding info" • "Add or update Sarah's contact with her new title" |
| `update-record` | Update an existing record by `record_id` | • "Update this company record with the new ARR" • "Change Sarah's title to VP Product" |
| `list-attribute-definitions` | List all attributes available on an object type, including their types and valid options | • "What fields are available on company records?" • "Show me the status options for deals" |
### Lists
| Tool | Description | Example Prompts |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `list-lists` | List all lists in the workspace, with optional filtering by name or slug | • "What lists do we have?" • "Find lists related to recruiting" |
| `list-list-attribute-definitions` | List the available entry attributes for a given list, including their types and options | • "What fields can I update on the sales pipeline list?" • "Show me the status options for this list" |
| `list-records-in-list` | List entries in a given list with optional filtering and sorting, including their parent records | • "Show me all entries in the sales pipeline" • "List recruiting candidates in the onsite stage" |
| `add-record-to-list` | Add a record to a list as a new list entry | • "Add Acme to the sales pipeline" • "Add this candidate to the interview pipeline" |
| `update-list` | Update a list's name or API slug | • "Rename this list to Enterprise pipeline" • "Change this list's API slug to enterprise\_pipeline" |
| `update-list-entry-by-id` | Update an existing list entry when you already know the `entry_id` | • "Move this sales pipeline entry to Closed Won" • "Update the owner on list entry `9f0f9d7e-3e4b-4a4d-b48d-0b3d03a6e4d2`" |
| `update-list-entry-by-record-id` | Find and update a list entry using its parent record ID. This tool errors if the record has zero or multiple entries in the target list. | • "Update Acme's entry in the sales pipeline to Proposal Sent" • "Set the owner on the recruiting pipeline entry for this candidate" |
### Comments
| Tool | Description | Example Prompts |
| ---------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `create-comment` | Create a new comment on a record, list entry, or as a reply to an existing comment thread | • "Add a comment to the Acme company record: waiting on legal review" • "Reply to this thread and ask for an update" |
| `list-comments` | List comments on a record or list entry, with paginated top-level comments and inline replies | • "Show me comments on the Linear account" • "List comments on this pipeline entry" |
| `list-comment-replies` | Fetch additional replies for a top-level comment thread | • "Show all replies to this comment thread" • "Load more replies on this comment" |
| `delete-comment` | Delete a comment that you created. Deleting a parent comment also deletes all of its replies | • "Delete my last comment on the Acme account" • "Remove this comment thread I started" |
### Notes
| Tool | Description | Example Prompts |
| -------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `create-note` | Create a new note attached to a record | • "Log a note on the Linear account: had a great demo call, they want to see pricing" • "Add meeting notes to the Acme Corp record" |
| `search-notes-by-metadata` | Search notes by parent record, author, meeting, or creation time | • "Show me all notes from this week" • "Find notes I created for the Notion deal" |
| `semantic-search-notes` | Find notes by topic using AI-powered semantic search | • "Find notes where we discussed pricing objections" • "Search for notes mentioning competitor comparisons" |
| `get-note-body` | Retrieve the full content of a specific note | • "Show me the full text of that meeting note" • "What did the note about the Q3 review say?" |
| `update-note` | Append or prepend content to a note, or update its title | • "Add a follow-up action item to the end of the Linear meeting note" • "Prepend a summary to this note" • "Rename the note to 'Q3 Review - Final'" |
### Tasks
| Tool | Description | Example Prompts |
| ------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `list-tasks` | List tasks in the workspace with optional filters | • "Show my incomplete tasks due this week" • "List tasks linked to the Stripe account" |
| `create-task` | Create a new task with optional deadline, assignee, and linked record | • "Add a task to follow up with Ramp in 3 days" • "Create a task to send the proposal to Linear by Friday" |
| `update-task` | Update a task's deadline, status, assignee, or linked record | • "Mark the Stripe follow-up task as complete" • "Move the demo prep task deadline to next Monday" |
### Meetings & Calls
| Tool | Description | Example Prompts |
| ------------------------------------ | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `search-meetings` | Search past and upcoming meetings by participants, related records, or time range | • "What meetings do I have with Notion this week?" • "Find all calls with Linear from last month" |
| `search-call-recordings-by-metadata` | Search call recordings by participants, related records, title, or time range | • "Find call recordings with the Stripe team" • "Show me all recorded calls from Q4" |
| `semantic-search-call-recordings` | Find call recordings by topic using AI-powered semantic search | • "Find calls where we discussed enterprise pricing" • "Search recordings for mentions of API integration" |
| `get-call-recording` | Retrieve full details and transcript of a specific call recording | • "Get the transcript from yesterday's call with Linear" • "Show me the full recording details" |
### Emails
| Tool | Description | Example Prompts |
| --------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `search-emails-by-metadata` | Search emails by participants, domain, or time range | • "Find emails from stripe.com in the last week" • "Show me all emails with [john@acme.com](mailto:john@acme.com)" |
| `semantic-search-emails` | Find emails by topic using AI-powered semantic search | • "Find emails discussing contract renewal" • "Search for emails about the Q3 proposal" |
| `get-email-content` | Retrieve the full body of a specific email | • "Show me the full email about the partnership" • "What did that pricing email say?" |
### Workspace
| Tool | Description | Example Prompts |
| ------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `list-workspace-members` | List members in the workspace with their details and team memberships | • "Who's on the sales team?" • "Find the workspace member named Sarah" |
| `list-workspace-teams` | List all teams in the workspace | • "What teams exist in our workspace?" • "Show me all the team structures" |
| `whoami` | Get information about the current user's identity and workspace membership | • "What's my user ID?" • "Show my workspace access level" |
### Reporting
| Tool | Description | Example Prompts |
| ------------------ | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `run-basic-report` | Run aggregate reports on records or list entries, including totals, averages, and grouped breakdowns | • "Count open deals by stage" • "Show average deal size by owner" |
### SQL
`query-particle-sql` is not available on every billing plan. For more information, please see the [SQL overview](/sql/overview).
| Tool | Description | Example Prompts |
| -------------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `query-particle-sql` | Execute a read-only SQL query against your workspace data | • "Give me a weighted pipeline report by stage × segment × owner" • "What's the source mix for deals over \$100k?" |
***
## Sample Prompts
### Contact & Company Lookup
| Prompt | What it does |
| ---------------------------------------------------- | --------------------------------------------------- |
| "Find all contacts at Stripe" | Searches for people records associated with Stripe |
| "What's the email for Namit Chadha?" | Looks up contact information for a specific person |
| "Show me everything we know about TheSwarm" | Retrieves comprehensive company/contact information |
| "Who's the decision maker at Notion?" | Finds key contacts with decision-making roles |
| "Find contacts in the fintech space based in London" | Filters contacts by industry and location |
### Activity & Notes
| Prompt | What it does |
| ----------------------------------------------------------------------------------- | ---------------------------------------------------- |
| "Log a note on the Linear account: had a great demo call, they want to see pricing" | Creates a note attached to the Linear company record |
| "What was the last interaction with Linear?" | Searches for recent notes, emails, and meetings |
| "Show me all notes from my calls this week" | Retrieves notes created within the current week |
### Tasks
| Prompt | What it does |
| --------------------------------------------- | ------------------------------------------------ |
| "Add a task to follow up with Ramp in 3 days" | Creates a task with a deadline and linked record |
| "Show my incomplete tasks due this week" | Lists matching tasks using task filters |
### Bulk Operations
| Prompt | What it does |
| ----------------------------------------------------- | -------------------------------------------- |
| "Find all Series B companies in our CRM" | Searches companies filtered by funding stage |
| "Show me contacts who are CTOs or VPs of Engineering" | Filters people by job title |
### Lists
| Prompt | What it does |
| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| "What lists do we have?" | Lists available workspace lists |
| "Show me all entries in the sales pipeline" | Lists records and entry values for a specific list |
| "Add Acme to the sales pipeline" | Creates a new list entry for the Acme record |
| "Update Acme's entry in the sales pipeline to Proposal Sent" | Finds the matching list entry for the Acme record and updates its values |
| "Update the owner on list entry `9f0f9d7e-3e4b-4a4d-b48d-0b3d03a6e4d2` to Sarah" | Updates a specific list entry directly when you already know its entry ID |
### Comments
| Prompt | What it does |
| ------------------------------------------------------------------- | ------------------------------------------ |
| "Show me comments on the Linear account" | Lists comment threads on the Linear record |
| "Add a comment to the Acme company record: waiting on legal review" | Creates a new comment on the Acme record |
### Reporting
| Prompt | What it does |
| --------------------------------- | ------------------------------------------------- |
| "Count open deals by stage" | Runs a grouped aggregate report over deal records |
| "Show average deal size by owner" | Runs an average report grouped by record owner |
***
## Rate Limits
Attio MCP applies per-workspace rate limits to tool calls. Tools are grouped into tiers, and each tier has its own limit. Tools in the same tier share a single bucket per workspace, so calls to any tool in a tier count against the same limit.
| Tier | Limit | Tools |
| --------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Read | 100 requests / second | `get-call-recording`, `get-email-content`, `get-note-body`, `get-records-by-ids`, `list-attribute-definitions`, `list-comment-replies`, `list-comments`, `list-list-attribute-definitions`, `list-lists`, `list-records`, `list-records-in-list`, `list-tasks`, `list-workspace-members`, `list-workspace-teams`, `whoami` |
| Write | 25 requests / second | `add-record-to-list`, `create-comment`, `create-note`, `create-record`, `create-task`, `delete-comment`, `update-list`, `update-list-entry-by-id`, `update-list-entry-by-record-id`, `update-note`, `update-record`, `update-task`, `upsert-record` |
| Search | 300 requests / minute | `search-call-recordings-by-metadata`, `search-emails-by-metadata`, `search-meetings`, `search-notes-by-metadata`, `search-records` |
| Semantic search | 2 requests / second | `semantic-search-call-recordings`, `semantic-search-emails`, `semantic-search-notes` |
| Reporting | 2 requests / second | `run-basic-report` |
| SQL | 2 requests / second | `query-particle-sql` |
The Read and Write tiers match the equivalent [REST API rate limits](/rest-api/guides/rate-limiting).
### What to do if you're rate-limited
If you encounter rate limit errors:
1. Prompt your AI tool to reduce parallel operations
2. Space out sequential requests
3. Try again after a brief waiting period
In most cases, normal conversational use stays well within rate limits.
***
## Security Best Practices
### Authentication
Attio MCP uses OAuth authentication, which means:
* You log in with your existing Attio credentials
* No API keys to manage or rotate
* Access is tied to your Attio user permissions
* Sessions can be revoked from your Attio account settings
### Approval Flows
Attio MCP uses MCP safety annotations:
* **Read operations**: Auto-approved for seamless searching and viewing
* **Write operations**: Request user confirmation before creating or updating data
### Data Access
* Attio MCP only operates within your workspace
* You have the same permissions as your Attio user account
* No data is shared with external systems unless you configure additional integrations
* All operations are logged and auditable
***
### Getting Help
If you encounter issues not covered here:
* Check the [Attio Help Center](https://attio.com/help)
* Contact [Attio Support](https://attio.com/help/reference/support/contact-attio-support)
***
## What's Next
* [Attio REST API Documentation](https://docs.attio.com/rest-api/overview) — Full API reference for custom integrations
* [Attio Developer Platform](https://attio.com/platform/developers) — Build custom apps and integrations
* [Attio Help Center](https://attio.com/help) — Product guides and support
# Overview
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types
Attributes are used to model data on objects and lists. Standard objects come with a series of "system" attributes predefined, but users and developers can create and modify their own attributes as well.
## Attribute properties
In the API, attributes are presented as a set of properties. Some attribute types have specific properties (e.g. an actor reference attribute has the `referenced_actor_type` property), but all attributes have the following properties:
* `id`, which is a [composite ID](/docs/slugs-and-ids) composed of `(workspace_id, object_id, attribute_id)`
* `title`, the human-readable name of the attribute (e.g. `"Name"`)
* `description`, an optional human-readable description (e.g. `"The name as registered in our database"`)
* `api_slug`, a shorthand way for developers to refer to this attribute (e.g. `"name"`)
* `type`, one of the enumerated types below (e.g. `"text"`)
* `is_archived`, whether the [attribute has been archived](/docs/archiving-vs-deleting)
* `is_required`, whether a value is required when creating the record or list entry
* `is_unique`, whether the value for this attribute is unique among all other records or list entries of that type
* `is_multiselect`, whether you can write more than one value to this attribute
* `is_default_value_enabled` and `default_value`, see the [default values guide](/docs/default-values)
* `created_at`, a timestamp of when this attribute was first created
* `config`, an object containing specific configuration for `currency` or `record_reference` types
For more information about attributes, you can find our [attribute APIs here](/rest-api/endpoint-reference/attributes).
## Attribute values
Attribute values represent the value of the given attribute on a given record or list entry. They also have a set of common properties, that you'll see referenced throughout this guide:
* `active_from`, a timestamp showing when this value was created
* `active_until`, either a timestamp (meaning the value was readable until that point in time) or `null`, meaning the value is still currently active. Most endpoints will only return active values, meaning this value is usually `null`. In special cases, such as the [list record attribute values API](https://attio.readme.io/reference/get_v2-objects-object-records-record-id-attributes-attribute-values), you can also query historic data.
* `created_by_actor`, a reference to the `id` and `type` of the [Actor](/docs/actors) who created this value
* `attribute_type`, matches the `type` property on the Attribute itself (e.g. `"text"`)
## Attribute types
Attio currently has 17 different attribute types, and they can behave differently when creating, viewing, filtering and sorting. This guide will walk through each type, with examples of where you might find them and how to work with them:
* [Actor reference](/rest-api/attribute-types/attribute-types-actor-reference)
* [Checkbox](/rest-api/attribute-types/attribute-types-checkbox)
* [Currency](/rest-api/attribute-types/attribute-types-currency)
* [Date](/rest-api/attribute-types/attribute-types-date)
* [Domain](/rest-api/attribute-types/attribute-types-domain)
* [Email address](/rest-api/attribute-types/attribute-types-email-address)
* [Interaction](/rest-api/attribute-types/attribute-types-interaction)
* [Location](/rest-api/attribute-types/attribute-types-location)
* [(Personal) name](/rest-api/attribute-types/attribute-types-personal-name)
* [Number](/rest-api/attribute-types/attribute-types-number)
* [Phone number](/rest-api/attribute-types/attribute-types-phone-number)
* [Rating](/rest-api/attribute-types/attribute-types-rating)
* [Record reference](/rest-api/attribute-types/attribute-types-record-reference)
* [Select](/rest-api/attribute-types/attribute-types-select)
* [Status](/rest-api/attribute-types/attribute-types-status)
* [Text](/rest-api/attribute-types/attribute-types-text)
* [Timestamp](/rest-api/attribute-types/attribute-types-timestamp)
# Actor reference
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-actor-reference
References to workspace members and others
Actor references are used to link to [actors](/docs/actors) in Attio. You're most likely to encounter this attribute via the `created_by` attribute which is available on every object, the `owner` attribute on a deal object, or the `strongest_connection_user` on a company or person.
Actor reference attributes can be single-select or multi-select.
Please note, in the mobile and web clients, attributes of this type are marked as "User" attributes.
### Reading values
Actor reference values have two properties, `referenced_actor_type` and `referenced_actor_id`. The `referenced_actor_type` can be one of `"api-token"`, `"workspace-member"` or `"system"`. The `referenced_actor_id` is a UUID, or the value `null`, that uniquely identifies the actor.
```jsonc Example: workspace member theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "actor-reference",
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "fbe75eb0-d704-4d12-9e41-aa187e60ed73"
}
```
### Writing values
Currently, the only type of [actor](/docs/actors) that can be explicitly set in our API is `"workspace-member"`. We may expand this list in future.
When writing references to workspace members, we allow you to identify actors by email addresses. You may do this either by passing an email address string directly, or by using an object with the key `workspace_member_email_address`.
Actor reference attributes may be multi-select or single-select. When writing to multi-select attributes, you must always wrap values in an array. Single-select attributes accept unwrapped data.
```json Using email string theme={"system"}
{
"owner": ["[email protected]"]
}
```
```json Single-select string theme={"system"}
{
"owner": "[email protected]"
}
```
```json Using workspace_member_email_address theme={"system"}
{
"owner": [
{
"workspace_member_email_address": "[email protected]"
}
]
}
```
You may also specify the actor type and ID explicitly using an object with the keys `referenced_actor_type` and `referenced_actor_id`.
```json Using workspace member ID theme={"system"}
{
"owner": [
{
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "50cf242c-7fa3-4cad-87d0-75b1af71c57b"
}
]
}
```
### Filtering
Actor reference values can only be filtered using both the `referenced_actor_type` and `referenced_actor_id` properties. If using explicit operators, only the `$eq` operator is supported. For example:
```json Value written by workspace member theme={"system"}
{
"filter": {
"created_by": {
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "ec44a06c-b690-4e4f-95b6-757fb4e2f55f"
}
}
}
```
```json Value written by API token theme={"system"}
{
"filter": {
"created_by": {
"referenced_actor_type": "api-token"
}
}
}
```
```json Expanded format theme={"system"}
{
"filter": {
"created_by": {
"referenced_actor_type": {
"$eq": "api-token"
}
}
}
}
```
# Checkbox
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-checkbox
Modelling boolean values
Checkbox attributes are used to represent boolean values (`true` and `false`). In the UI, they are presented to users as a checkbox, hence the name.
There are no predefined checkbox attributes on any of the standard objects. As a result, checkbox attributes will only be present when added by the user.
Checkbox attributes may only be single-select.
### Reading values
If the checkbox is checked, you'll get the `true` property back, otherwise it will be `false`. This attribute does not support null values.
```jsonc Example: a checked attribute theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "checkbox",
"value": true
}
```
### Writing values
To write checkbox attribute values, you can use either the boolean values `true`/`false` or their string equivalents `"true"`/`"false"`.
We support setting these values directly as raw booleans/strings, or by using an object with a single key, `value`.
We only support single-select checkbox attributes, so you may always write checkbox values without wrapping the values in an array (array values containing a single element are also supported).
```json Using boolean theme={"system"}
{
"a_custom_checkbox_attribute": true
}
```
```json Using string theme={"system"}
{
"a_custom_checkbox_attribute": "true"
}
```
```json Using array theme={"system"}
{
"a_custom_checkbox_attribute": [true]
}
```
```json Using object theme={"system"}
{
"a_custom_checkbox_attribute": [
{
"value": true
}
]
}
```
### Filtering
Checkbox attribute values can be filtered by true/false. If using explicit operators, only the `$eq` operator is supported. For example:
```json Records where checkbox is checked theme={"system"}
{
"filter": {
"a_custom_checkbox_attribute": true
}
}
```
```json Expanded format theme={"system"}
{
"filter": {
"a_custom_checkbox_attribute": {
"value": {
"$eq": false
}
}
}
}
```
# Currency
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-currency
More than just numbers
Currency attributes represent quantities of money. They are similar to number attributes, allowing storing numbers with up to four decimal places of precision, but are presented differently in the UI with a currency symbol usually alongside.
Two examples of currency attributes are the `funding_raised_usd` attribute on the company object, and the `value` attribute on the deal object.
There is a `currency_code` property returned from the API on each attribute value, but please note that this is shared among all attribute values of the attribute; it is not possible to override currency for a particular record or entry.
Currency attributes can only be single-select.
### Configuration
When creating currency attributes, you can specify two configuration properties:
* `default_currency_code` - The [ISO4217](https://www.iso.org/iso-4217-currency-codes.html) currency code e.g. `USD` or `EUR`. All values for the attribute inherit this value.
* `display_type` How the currency should be displayed across the app. See [MDN for more details](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#currencydisplay).
```http Specifying configuration when creating a currency attribute theme={"system"}
POST /v2/objects/:object/attributes HTTP/1.1
Authorization: Bearer <>
Content-Type: application/json
{
"title": "Amount owed",
"api_slug": "amount_owed",
"type": "currency",
"config": {
"currency": {
"default_currency_code": "USD",
"display_type": "symbol"
}
}
}
```
### Reading values
Currency attributes have two properties, `currency_code` (string) and `currency_value` (number). This attribute does not support null values.
```jsonc Example: $499.00 theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "currency",
"currency_value": "499.00",
"currency_code": "USD"
}
```
### Writing values
Currency values can be written using floating point numbers. We accept values with up to 4 decimal places of precision. You do not need to pass floating point numbers explicitly; we will automatically convert integers to their floating point equivalents.
Where possible, Attio will convert strings into numbers. For example, the string `"4.99"` will be parsed as the float `4.99`.
As currency attributes may only be single-select, you may always write values without wrapping in an array if preferred.
We also support writing currency values using an object with a single key, `currency_value`.
```json Using float theme={"system"}
{
"amount_owed": 4.99
}
```
```json Using string theme={"system"}
{
"amount_owed": "4.99"
}
```
```json Using object theme={"system"}
{
"amount_owed": [
{
"currency_value": "399.00"
}
]
}
```
It is not possible to specify the `currency_code` since this is inherited from the attribute.
### Filtering
Currency attribute values can be filtered by their value, using either JSON strings or numbers. You can filter for an exact value using the implicit syntax, or use the `$eq`,`$gt`,`$gte`,`$lt`,`$lte` operators with the explicit syntax.
```json Finding records with an exact currency value theme={"system"}
{
"filter": {
"amount_owed": "399.00"
}
}
```
```json Currency at least 500 theme={"system"}
{
"filter": {
"amount_owed": {
"currency_value": {
"$gte": 500
}
}
}
}
```
# Date
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-date
A timezone-less calendar date
Date attributes are used to represent a single calendar year, month and day, independent of timezone. Attio exclusively works with the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, i.e. `YYYY-MM-DD` e.g. `2023-11-24`.
There is only one default example of a date attribute, `foundation_date` on the company object.
Date attributes can only be single-select.
### Reading values
Date attributes have a single property, `value` (a string).
```jsonc Example: 24th November, 2023 theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "date",
"value": "2023-11-24"
}
```
### Writing values
Date values can be written by passing an ISO 8601 date string.
If hours, months, seconds or timezones are provided, they will be trimmed. For example:
* `'2023'` → `'2023-01-01'`
* `'2023-01'` → `'2023-01-01'`
* `'2023-01-02'` → `'2023-01-02'`
* `'2023-01-02T13:00'` → `'2023-01-02'`
* `'2023-01-02T14:00:00'` → `'2023-01-02'`
* `'2023-01-02T15:00:00.000000000'` → `'2023-01-02'`
* `'2023-01-02T15:00:00.000000000+02:00'` → `'2023-01-02'`
If a timezone is provided that would result in a different calendar date in UTC, the date will be coerced to UTC and then the timezone component will be trimmed. For example, the value `'2023-01-02T23:00:00-10:00'` will be returned as `'2023-01-03'`.
As date values are always single-select, you may write values either by passing the date string directly, or by wrapping a single value in an array.
You may also write date values using an object with a single `value` key.
```json Using string theme={"system"}
{
"foundation_date": "2004-07-29"
}
```
```json Using object theme={"system"}
{
"foundation_date": [
{
"value": "2004-07-29"
}
]
}
```
### Filtering
Date attribute values can be filtered by their value. You can filter for an exact date using the implicit syntax, or use the `$eq`,`$gt`,`$gte`,`$lt`,`$lte` operators with the explicit syntax.
```json Companies founded on 2023-11-24 theme={"system"}
{
"filter": {
"foundation_date": "2023-11-24"
}
}
```
```json Companies formed after the year 2000 theme={"system"}
{
"filter": {
"foundation_date": {
"value": {
"$gte": "2000-01-01"
}
}
}
}
```
# Domain
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-domain
An internet domain
Domain attributes represent an internet domain, for example, "apple.com".
Attio represents domains as structured objects rather than raw strings, allowing filtering and display of specific domain properties such as the root domain.
Please note that domain attributes store domains, not URLs. Any inputted values will have paths and query parameters trimmed. If you would like to store full URLs, please use a text attribute.
It isn't currently possible to create your own domain attributes, so you'll find only the multi-select `domains` attribute on a company object.
### Reading values
There are two properties on a domain attribute, `domain` and `root_domain`.
The `domain` property contains the entire domain, after normalization.
The `root_domain` property is the top-most part of the domain besides the public suffix. For example, the root domain of `"app.attio.com"` would be `"attio.com"`.
```jsonc Example: app.attio.com theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "domain",
"domain": "app.attio.com",
"root_domain": "attio.com"
}
```
### Writing values
To write domain values, simply pass the string of the domain.
The `root_domain` property will automatically be inferred from input values so there is no need to write it yourself.
You may also write domain values using an object with a single key, `domain`.
As the `domains` attribute is multi-select, you must always pass values wrapped in an array.
```json Using string (single value) theme={"system"}
{
"domains": ["app.attio.com"]
}
```
```json Using string (multiple values) theme={"system"}
{
"domains": ["app.attio.com", "attio.com"]
}
```
```json Using object theme={"system"}
{
"domains": [
{
"domain": "app.attio.com"
}
]
}
```
### Filtering
Domain attribute values can be filtered by either the `root_domain` or `domain` property, and support several operators:
* `$eq` for an exact match
* `$not_empty` for any value present
* `$contains` , `$starts_with` and `$ends_with`
In implicit mode, the `domain` property is checked for equality, otherwise you can use the explicit syntax to combine the properties and operators above.
```json Companies where domain=app.attio.com theme={"system"}
{
"filter": {
"domain": "app.attio.com"
}
}
```
```json ...where subdomain=attio.com theme={"system"}
{
"filter": {
"domains": {
"root_domain": {
"$eq": "attio.com"
}
}
}
}
```
```jsonc ...where domain includes attio theme={"system"}
{
"filter": {
"domains": {
"domain": {
"$contains": "attio"
}
}
}
}
```
# Email address
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-email-address
An email address
Email address attributes are a string referencing an internet email address. For example, an email address might be `"[email protected]"`. Like [domain](/rest-api/attribute-types/attribute-types-domain) attributes, we do some parsing of the email domain part, as well as validating the general shape of an email address overall.
It isn't currently possible to create your own email address attributes. You'll find only the multiselect `email_addresses` attribute on a person object, or the single attribute `email_address` attribute on the user standard object.
### Reading values
There are five properties available on this attribute:
* `email_address` - the normalized form of the email address, this is the one you are most likely to use
* `original_email_address` - the email as it was originally input to the system, without normalization
* `email_domain` - the full domain part of the email address
* `email_root_domain` - the root domain part of the email address
* `email_local_specifier` - the local part of the email address
```jsonc Example: person@company.com theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "email-address",
"email_address": "[email protected]",
"original_email_address": "[email protected]",
"email_domain": "company.com",
"email_root_domain": "company.com",
"email_local_specifier": "person"
}
```
### Writing values
Email address values can be written by passing in a string of the email you would like to write.
We also support passing values with a single key, `email_address`.
We strictly validate that email addresses have valid domain and local part formats.
Attio will automatically infer the root domain, local specify and other properties available on read so there is no need to write them yourself.
If writing to a multi-select attribute, you must wrap your input values in an array.
```json Writing to email_addresses theme={"system"}
{
"email_addresses": ["[email protected]", "[email protected]"]
}
```
```json Writing to email_address theme={"system"}
{
"email_address": "[email protected]"
}
```
```json Using object theme={"system"}
{
"email_addresses": [
{
"email_address": "[email protected]"
}
]
}
```
### Filtering
Email attribute values can be filtered by the `email_address`, `email_domain`, `email_root_domain` and `email_local_specifier` properties, and support several operators:
* `$eq` for an exact match
* `$contains` , `$starts_with` and `$ends_with`
In implicit mode, the `email_address` property is checked for equality, otherwise you can use the explicit syntax to combine the properties and operators above.
```json People where email_addresses=person@company.com theme={"system"}
{
"filter": {
"email_addresses": "[email protected]"
}
}
```
```json ...where domain=attio.com theme={"system"}
{
"filter": {
"email_addresses": {
"email_domain": {
"$eq": "attio.com"
}
}
}
}
```
```json ...where local part includes person theme={"system"}
{
"filter": {
"email_addresses": {
"email_local_specifier": {
"$contains": "person"
}
}
}
}
```
# Interaction
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-interaction
Calendar events and emails
Interactions are quite a generic concept, used to model when a given actor interacted with a record in a particular way. Presently, Attio has just two types of interaction:
* Email interactions (`first_email_interaction` and `last_email_interaction`)
* Calendar interactions (`first_calendar_interaction`, `last_calendar_interaction` and `next_calendar_interaction`)
These attributes are available on both the Company and Person objects, although they are enriched and not available on every billing plan. For more information about these attributes, [please see our Enriched data help page](https://attio.com/help/reference/data-and-syncing/enriched-data#communication-intelligence).
### Reading values
Interaction attribute values have an `interaction_type` property, which can be either `"email"` or `"calendar-event"`, and an `interacted_at` timestamp property in ISO8601 format.
There is also an `owner_actor` property, which is an object relating the [actor](/docs/actors) who created this interaction (this is different from the `created_by` attribute value property which could be e.g. a system actor).
```jsonc Example: last email received by Tom on 2023-11-25 theme={"system"}
{
"active_from": "2023-11-25T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
"type": "system",
"id": null
},
"attribute_type": "interaction",
"interaction_type": "email",
"interacted_at": "2023-11-25T15:21:06.447000000Z",
"owner_actor": {
"type": "workspace-member",
"id": "50cf242c-7fa3-4cad-87d0-75b1af71c57b" // Tom
}
}
```
### Writing values
It is not currently possible to write Interaction values, they are only created by the Attio system.
### Filtering
There are three properties of interactions that can be used in filtering:
* `owner_member_id` filters by the workspace member ID that is the `owner_actor`, this supports `$eq` and `$not_empty` operators
* `interacted_at` (timestamp) supports `$eq`, `$gte`, `$gt`, `$lte` and `$lt` operators
* `interaction_type` can also be filtered by `$eq` and `$not_empty`
```json Companies ... which have had an email interaction theme={"system"}
{
"filter": {
"last_email_interaction": {
"owner_member_id": {
"$not_empty": true
}
}
}
}
```
```json ... with whom Tom had a recent meeting theme={"system"}
{
"filter": {
"last_calendar_interaction": {
"owner_member_id": "50cf242c-7fa3-4cad-87d0-75b1af71c57b",
"interacted_at": {
"$gte": "2023-11-01"
}
}
}
}
```
```json ... via email theme={"system"}
{
"filter": {
"first_email_interaction": {
"interaction_type": "email"
}
}
}
```
# Location
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-location
A physical location in the world
Location attributes model a physical location in the world. We store all location properties (address lines, postcode, country code, etc) on a single attribute value, rather than separate attributes. This means that when working with locations, updates must be atomic—every property must be specified, even if it is null.
You'll find an example of this attribute as `primary_location` on both person and company objects.
Locations have the following properties:
* `line_1` - the first line of the address, e.g. `"1 Infinite Loop"`
* `line_2` - the second line of the address e.g. `"Block 1"`
* `line_3`, `line_4`, same as above
* `locality` - the town, neighbourhood or area, e.g. `"Cupertino"`
* `region` - the state, county, province or region, e.g. `"CA"`
* `postcode` - the postal or zip code, e.g. `"95014"`
* `country_code` - the ISO 3166-1 alpha-2 country code, e.g. `US`
* `latitude` - latitudinal coordinates, e.g. `"37.331741"`
* `longitude` - longitudinal coordinates, e.g. `"-122.030333"`
There are some properties which are not presently shown in the Attio app but are captured by the API—for example, address lines or postcodes.
### Reading values
Depending on your use case, there are various properties of a location that might be relevant for you. The API will return the full object as structured data, without attempting to format it:
```jsonc Example: Apple Headquarters theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "location",
"line_1": "1 Infinite Loop",
"line_2": null,
"line_3": null,
"line_4": null,
"locality": "Cupertino",
"region": "CA",
"postcode": "95014",
"country_code": "US",
"latitude": "37.331741",
"longitude": "-122.030333"
}
```
### Writing values
When writing location values, Attio will intelligently parse strings into structured location data.
```json json theme={"system"}
{
"primary_location": "1 Infinite Loop, Cupertino, CA, 95014, US"
}
```
You may also specify each location property explicitly using an object value. When using the object syntax, we require that updates to a location attribute must specify a value for every attribute, even if it is `null`.
```json Using object theme={"system"}
{
"primary_location": {
"line_1": "1 Infinite Loop",
"line_2": null,
"line_3": null,
"line_4": null,
"locality": "Cupertino",
"region": "CA",
"postcode": "95014",
"country_code": "US",
"latitude": "37.331741",
"longitude": "-122.030333"
}
}
```
### Filtering
The properties `line_1`, `line_2`, `line_3`, `line_4`, `locality`, `region` and `postcode` can all be filtered by `$eq`, `$contains`, `$starts_with` and `$ends_with` operators.
The property `country_code` can be filtered by `$eq` and `$starts_with` operators.
```json People in Cupertino, California theme={"system"}
{
"filter": {
"primary_location": {
"locality": "Cupertino",
"region": "CA",
"country_code": "US"
}
}
}
```
```jsonc ...where locality contains "cuper" theme={"system"}
{
"filter": {
"primary_location": {
"locality": {
"$contains": "cuper" /* Case insensitive */
}
}
}
}
```
It is not currently possible to filter by latitude/longitude.
# Number
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-number
Quantities, sums and metrics
Number attributes store floating point numbers with up to four decimal places of precision.
An example of a number attribute is the `twitter_follower_count` attribute on both the company and person objects.
Only single-select number attributes are supported.
### Reading values
Number attribute values have a `value` property:
```jsonc Example: 14 Twitter followers theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "number",
"value": 14
}
```
### Writing values
To write number values, simply pass your desired number, either as an integer or a float. Alternatively, you can use an object with a single `value` property.
```json Using number theme={"system"}
{
"a_custom_number_attribute": 3.1415
}
```
```json Using object theme={"system"}
{
"a_custom_number_attribute": [
{
"value": 3.1415
}
]
}
```
Please note that `twitter_follower_count` is a system attribute and cannot be written to from the API.
### Filtering
Numbers can be filtered by the operators `$eq`, `$gte`, `$gt`,`$lte`,`$lt`. The implicit syntax does an exact equality check:
```json Finding records with 14 Twitter followers theme={"system"}
{
"filter": {
"twitter_follower_count": 14
}
}
```
```json ... with more than 1000 followers theme={"system"}
{
"filter": {
"twitter_follower_count": {
"value": {
"$gte": 1000
}
}
}
}
```
# (Personal) name
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-personal-name
A person's name
Name attributes represent a person's name. They have three properties: `first_name`, `last_name` and `full_name`.
Only the person object has a `name` attribute. Name attributes cannot be created by users.
### Reading values
All three properties are present in responses:
```jsonc Example: John Smith theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "personal-name",
"first_name": "John",
"last_name": "Smith",
"full_name": "John Smith"
}
```
### Writing values
Attio provides two syntaxes for writing name values, a string syntax and an object syntax. If possible, we recommend using the object syntax as it provides full control over the name values you create.
When writing values using the object syntax, all three properties must be set.
```json Using object theme={"system"}
{
"name": {
"first_name": "John",
"last_name": "Smith",
"full_name": "John Smith"
}
}
```
When writing using a string, the string must match format 'Last name(s), First name(s)'. Text without a comma is interpreted as solely comprising the first name. Further commas will be ignored and assumed to be part of the first name.
```json Using string theme={"system"}
{
"name": "Smith, John"
}
```
### Filtering
You can filter by any of the properties, using these operators:
* `$eq`
* `$not_empty`
* `$contains`, `$starts_with`, `$ends_with` (case insensitive)
Using the implicit filter syntax, you can search for exact matches of `full_name`, otherwise any combination of property/operator.
```json People exactly named John Smith theme={"system"}
{
"filter": {
"name": "John Smith"
}
}
```
```jsonc ...where first_name starts with "jo" theme={"system"}
{
"filter": {
"name": {
"first_name": {
"$starts_with": "jo"
}
}
}
}
```
```json ... where last_name is specified theme={"system"}
{
"filter": {
"name": {
"last_name": {
"$not_empty": true
}
}
}
}
```
# Phone number
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-phone-number
International telephone numbers
Phone number attributes represent telephone numbers. They are represented in [E164 format](https://en.wikipedia.org/wiki/E.164) and always prefixed with a country code.
The person object comes with a phone number attribute (`phone_numbers`), which is is a multi-select attribute, but users can also create their own phone number attributes on other objects or lists as well.
There are three properties on a phone number: `original_phone_number` (as inputted by the user), `country_code` (an ISO 3166-1 alpha-2 country code) and `normalized_phone_number` (as set by Attio). All properties are represented as strings.
### Reading values
```jsonc Example: a US telephone number theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "phone-number",
"original_phone_number": "+15558675309",
"normalized_phone_number": "+15558675309",
"country_code": "US"
}
```
### Writing values
You may either write phone number values as strings or objects. For both formats, you must provide sufficient information to identify the country code of the number.
For string values, the phone number must be prefixed with the area code, starting with "+".
Object values must include both a `original_phone_number` and `country_code` property. You may pass `null` to `country_code` if an area code prefix is provided as part of `original_phone_number`.
All values are validated against the E164 format.
Since the `phone_numbers` attribute is multi-select, you must always pass values as part of an array.
```json Using string theme={"system"}
{
"phone_numbers": ["+447777777777"]
}
```
```json Setting phone number with country theme={"system"}
{
"phone_numbers": [
{
"original_phone_number": "+447777777777",
"country_code": "GB"
}
]
}
```
```json Setting just the number theme={"system"}
{
"phone_numbers": [
{
"original_phone_number": "+44 7777777777",
"country_code": null
}
]
}
```
### Filtering
Phone numbers can be filtered by equality or substrings (`$eq`,`$contains`,`$starts_with`,`$ends_with`). Note that the explicit property is called `phone_number`, internally Attio will use the normalized phone number for searching.
The `country_code` property can also be filtered by `$eq` and `$not_empty`.
```json People by exact phone number theme={"system"}
{
"filter": {
"phone_numbers": "+15558675309"
}
}
```
```json ... with US phone numbers theme={"system"}
{
"filter": {
"phone_numbers": {
"country_code": {
"$eq": "US"
}
}
}
}
```
```json ... with part of a phone number theme={"system"}
{
"filter": {
"phone_numbers": {
"phone_number": {
"$contains": "67530"
}
}
}
}
```
# Rating
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-rating
Star ratings from 0 to 5
Rating attributes are a numeric value from 0 to 5. In the UI, they are presented as a proportion of 5 stars.
There are no default rating attributes in Attio, but they can be created by users and in the API.
Only single-select rating attributes are permitted.
### Reading values
Rating attribute values have a `value` property:
```jsonc Example: 3 out of 5 stars theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "rating",
"value": 3
}
```
### Writing values
To write rating values, you can either pass an integer directly, or an object with a single key, `value`.
As all rating attributes are single-select, you may pass the value directly or as an array containing a single element.
```json Using number theme={"system"}
{
"performance": 4
}
```
```json Using object theme={"system"}
{
"performance": [
{
"value": 4
}
]
}
```
### Filtering
Ratings can be filtered by the operators `$eq`, `$gte`, `$gt`,`$lte`,`$lt`. The implicit syntax does an exact equality check:
```json Finding records rated 4 stars theme={"system"}
{
"filter": {
"performance": 4
}
}
```
```json ... with more than 3 stars theme={"system"}
{
"filter": {
"performance": {
"value": {
"$gte": 3
}
}
}
}
```
# Record reference
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-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) and `associated_workspaces` (workspaces)
* Person has `company` (company), `associated_deals`(deals) and `associated_users` (users)
* Deal has `associated_people` (people) and `associated_company` (company)
* User has `person` (person) and `workspace` (workspace)
* Workspace has `users` (users) and `company` (company)
### A note about relationship attributes
All of the example attributes above are also relationship attributes.
[Relationship attributes](https://attio.com/help/reference/workspace/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:
```jsonc Example "team" attribute on a company theme={"system"}
{
"id": {
"workspace_id": "14beef7a-99f7-4534-a87e-70b564330a4c",
"object_id": "7c430b6d-fa5b-48c6-bfa7-9520c088c7bc",
"attribute_id": "8468fb8b-52f4-4a7f-ae42-3a4bed9782e4"
},
"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):
```http Create "owner" attribute which only supports People theme={"system"}
POST /v2/objects/:object/attributes HTTP/1.1
Authorization: Bearer <>
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:
```jsonc Attribute definition response for "team" attribute theme={"system"}
{
"id": {
"workspace_id": "14beef7a-99f7-4534-a87e-70b564330a4c",
"object_id": "7c430b6d-fa5b-48c6-bfa7-9520c088c7bc",
"attribute_id": "8468fb8b-52f4-4a7f-ae42-3a4bed9782e4"
},
"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).
```jsonc A reference to person@company.com theme={"system"}
{
"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.
```json Referencing a company theme={"system"}
{
"associated_company": [
{
"domains": [{"domain": "company.com"}],
"target_object": "companies"
}
]
}
```
```json Referencing a person theme={"system"}
{
"team": [
{
"email_addresses": [{"email_address": "[email protected]"}],
"target_object": "people"
}
]
}
```
```json Referencing a user theme={"system"}
{
"user": [
{
"user_id": [{"value": "my-user-id"}],
"target_object": "users"
}
]
}
```
```json Referencing a workspace theme={"system"}
{
"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.
```json Domain theme={"system"}
{
"associated_company": "company.com"
}
```
```json Domains theme={"system"}
{
"associated_company": ["company.com", "company.net"]
}
```
```json Email theme={"system"}
{
"team": ["[email protected]"]
}
```
```json Email theme={"system"}
{
"team": ["[email protected]", "[email protected]"]
}
```
```json User theme={"system"}
{
"user": "my-user-id"
}
```
```json Users theme={"system"}
{
"user": ["my-user-id", "another-user-id"]
}
```
```json Workspace theme={"system"}
{
"workspace": "my-workspace-id"
}
```
```json Workspaces theme={"system"}
{
"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.
```json Using record IDs theme={"system"}
{
"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.
```json Find people with a given company theme={"system"}
{
"filter": {
"company": {
"target_object": "companies",
"target_record_id": "99a03ff3-0435-47da-95cc-76b2caeb4dab"
}
}
}
```
```json Find workspaces with a given user theme={"system"}
{
"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.
```json Find people where their company's record_id is one of several possible values theme={"system"}
{
"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":
```json Companies with an employee named John theme={"system"}
{
"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](/rest-api/attribute-types/attribute-types-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:
```json People by their manager's manager's manager theme={"system"}
{
"filter": {
"path": [
["people", "manager"],
["people", "manager"]
],
"constraints": {
"target_object": "people",
"target_record_id": "managers-manager-id"
}
}
}
```
# Select
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-select
An option from a predefined list
Select attributes are a constrained input type, where the user must pick from a predefined list.
Company has several select attributes (they are mostly [enriched attributes](https://attio.com/help/reference/data-and-syncing/enriched-data)): `categories`, `estimated_arr_usd` and `employee_range`. `strongest_connection_strength` is also available on both person and company.
Attio provides a [separate API for managing the select options available](/rest-api/endpoint-reference/attributes/list-select-options).
Select attributes may be either single-select or multi-select. In the API, these two variants are represented using the same underlying type, select. However, in web and mobile clients, users will see these attributes as two separate types: select and multi-select.
Please note that select attributes cannot be configured to be unique.
### Reading values
Select attribute values have an `option` property, which is an object describing which select option was used:
```jsonc Example: 3 out of 5 stars theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "select",
"option": {
"id": {
"workspace_id": "4f9a01be-3792-4ab9-926f-ca7f9005700c",
"object_id": "5f1feef5-fe73-4c0e-9d97-5b0a96a7d32b",
"attribute_id": "a4977b52-d367-4e28-a671-b5c4fa401fc5",
"option_id": "14938464-cae9-4e50-8856-0fb584844f24"
},
"title": "Aerospace & Defense",
"is_archived": false
}
}
```
### Writing values
You can find a list of available options using the [list select options](/rest-api/endpoint-reference/attributes/list-select-options) API.
To write select values, pass the title of the select option as a string. If it's a multi-value select attribute, you'll need to pass an array.
You can also pass an object with an `option` property, which references either the `option_id` or the `title` of the select option.
If you attempt to write a value where the ID or title cannot be found, you will receive an error rather than create a new select option.
```json Using string theme={"system"}
{
"categories": ["3D Printing"]
}
```
```json Multiple values theme={"system"}
{
"categories": ["3D Printing", "Architecture"]
}
```
```json Using object (title) theme={"system"}
{
"categories": [
{
"option": "3D Printing"
}
]
}
```
```json Using object (option_id) theme={"system"}
{
"categories": [
{
"option": "14938464-cae9-4e50-8856-0fb584844f24"
}
]
}
```
### Filtering
Select attributes can be filtered by equality, using either the implicit syntax or the explicit one:
```json Finding companies in the "Aerospace & Defense" category theme={"system"}
{
"filter": {
"categories": "Aerospace & Defense"
}
}
```
```json ... with an option ID instead theme={"system"}
{
"filter": {
"categories": {
"option": {
"$eq": "Aerospace & Defense"
}
}
}
}
```
It's also possible to use `$or` to find records which match one of several categories:
```json Find Companies in one of many categories theme={"system"}
{
"filter": {
"$or": [{"categories": "Aerospace & Defense"}, {"categories": "Biotechnology"}]
}
}
```
Select attributes can also be filtered based on when they were modified, using the `active_from` property. This allows automations based on when the attribute was changed. This filter supports the `$lt`, `$lte`, `$gt`, `$gte` operators:
```json Finding companies where the category was changed this week theme={"system"}
{
"filter": {
"categories": {
"active_from": {
"$gte": "2023-11-20"
}
}
}
}
```
```json ... where the category was not changed this year theme={"system"}
{
"filter": {
"categories": {
"active_from": {
"$lt": "2023-01-01"
}
}
}
}
```
# Status
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-status
Similar to select attributes, originally designed for use in Lists
Just like [select](/rest-api/attribute-types/attribute-types-select) attributes, status attributes are a constrained input type, where the user must pick from a predefined list. They are used in the Attio UI to define the different columns on a kanban board, but they can also be used with objects directly.
There's only one predefined status attribute, available on the deal object as `stage`.
The possible values of a status attribute are known as "statuses", and there are [separate APIs for managing them](/rest-api/endpoint-reference/attributes/list-statuses).
All status attributes are single-select.
### Reading values
Status values have a `status` property, which is an object describing which status was used:
```jsonc Example: 3 out of 5 stars theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "select",
"status": {
"id": {
"workspace_id": "4f9a01be-3792-4ab9-926f-ca7f9005700c",
"object_id": "5f1feef5-fe73-4c0e-9d97-5b0a96a7d32b",
"attribute_id": "a4977b52-d367-4e28-a671-b5c4fa401fc5",
"status_id": "11f07f01-c10f-4e05-a522-33e050bc52ee"
},
"title": "In Progress",
"is_archived": false,
"target_time_in_status": null,
"celebration_enabled": false
}
}
```
### Writing values
You can find a list of available statuses using the [list statuses](/rest-api/endpoint-reference/attributes/list-statuses) API.
To write status values, pass the title of the status as a string.
You can also pass an object with a `status` property which references either the `status_id` or the `title` of the status.
If you attempt to write a value where the ID or title cannot be found, you will receive an error rather than create a new status.
```json Using string theme={"system"}
{
"stage": "Lead"
}
```
```json Using object (title) theme={"system"}
{
"stage": [
{
"status": "Lead"
}
]
}
```
```json Using object (status_id) theme={"system"}
{
"stage": [
{
"status": "11f07f01-c10f-4e05-a522-33e050bc52ee"
}
]
}
```
### Filtering
Status attributes can be filtered by equality, using either the implicit syntax or the explicit one, with either the title or status ID:
```json Finding deals in the "In Progress" stage theme={"system"}
{
"filter": {
"stage": "In Progress"
}
}
```
```json ... with a status ID instead theme={"system"}
{
"filter": {
"stage": {
"status": {
"$eq": "11f07f01-c10f-4e05-a522-33e050bc52ee"
}
}
}
}
```
You can also filter for multiple possible matching values using the `$or` syntax:
```json Finding deals in either "In Progress" or "Lead" stage theme={"system"}
{
"filter": {
"$or": [{"stage": "In Progress"}, {"stage": "Lead"}]
}
}
```
Status attributes can also be filtered based on when they were modified, using the `active_from` property. This allows automations based on when the attribute was changed. This filter supports the `$lt`, `$lte`, `$gt`, `$gte` operators:
```json Finding deals where the stage was changed this week theme={"system"}
{
"filter": {
"stage": {
"active_from": {
"$gte": "2023-11-20"
}
}
}
}
```
```json ... where the stage was not changed this year theme={"system"}
{
"filter": {
"stage": {
"active_from": {
"$lt": "2023-01-01"
}
}
}
}
```
# Text
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-text
Human-readable, unconstrained text inputs
Text attributes are the most common type of attribute, and usually represent unstructured or human-readable data. They have a max size of 10mb.
Examples of text attributes include the `description`, `facebook` or `instagram` attributes on company and person, or the `workspace_id` and `user_id` attributes on workspace/user. Please note that on person, the `name` attribute is a [(Personal) name](/rest-api/attribute-types/attribute-types-personal-name) attribute, but on company it's a text attribute.
Text attributes are always single-select.
### Reading values
Text attribute values have a single `value` property:
```jsonc Example: 14 Twitter followers theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "text",
"value": "A long time ago in a galaxy far, far away..."
}
```
### Writing values
To write text attribute values, simply pass the string that you would like to set.
You may also pass an object with a single `value` property.
```json Using string theme={"system"}
{
"description": "Headquartered in New York City, Waystar Royco was founded by Logan Roy and operates in 50 countries across 4 continents"
}
```
```json Using object theme={"system"}
{
"description": [
{
"value": "Headquartered in New York City, Waystar Royco was founded by Logan Roy and operates in 50 countries across 4 continents"
}
]
}
```
### Filtering
Text can be filtered by the operators `$eq`, `$in`, `$contains`, `$starts_with` and `$ends_with`. The implicit syntax does an exact equality (`$eq`) check:
```json Finding companies with an exact description theme={"system"}
{
"filter": {
"description": "An exact match"
}
}
```
```json ... which starts with a prefix theme={"system"}
{
"filter": {
"description": {
"value": {
"$starts_with": "Headquartered in New York City"
}
}
}
}
```
```json ... which contains a keyword theme={"system"}
{
"filter": {
"description": {
"value": {
"$contains": "New York City"
}
}
}
}
```
```json ...where record_id is one of supplied values theme={"system"}
{
"filter": {
"record_id": {
"$in": ["000e8881-37cc-41d2-bc22-39fe35e76e6b", "592dc9d8-548b-4148-813f-1259055ca83c"]
}
}
}
```
# Timestamp
Source: https://docs.attio.com/rest-api/attribute-types/attribute-types-timestamp
A calendar date including time information, stored in UTC
Timestamp attributes represent a single, universal moment in time using the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format. Time information is stored with nanosecond precision, and UTC is the assumed timezone if one is not provided. Timestamp values will always be returned in UTC.
Every Attio object has a `created_at` timestamp attribute, but users can also create their own custom timestamp attributes.
All timestamp attributes are single-select.
### Reading values
Timestamp attributes have a single property, `value` (string).
```jsonc Example: 24th November, 2023 theme={"system"}
{
"active_from": "2023-04-03T15:21:06.447000000Z",
"active_until": null,
"created_by_actor": {
/*...*/
},
"attribute_type": "date",
"value": "2023-11-24T15:17:48.000000000Z"
}
```
### Writing values
Input values will be coerced into the full format. UTC is assumed if not specified. For example, the following input values would all be coerced to `"2023-01-02T13:00:00.000000000Z"`:
* `"2023"`
* `"2023-01"`
* `"2023-01-02"`
* `"2023-01-02T13:00"`
* `"2023-01-02T13:00:00"`
* `"2023-01-02T13:00:00.000000000"`
* `"2023-01-02T15:00:00.000000000+02:00`
To write timestamp attribute values, you should specify the `value` property:
You may also pass an object with a single property, `value`.
```Text Using string theme={"system"}
{
"my_timestamp_attribute": "2019-01-17T15:17:48.000000000Z"
}
```
```json Using object theme={"system"}
{
"my_timestamp_attribute": [
{
"value": "2019-01-17T15:17:48.000000000Z"
}
]
}
```
### Filtering
Timestamp attribute values can be filtered by their value. Unlike when writing timestamp values, both date and time components must be specified.
You can filter for an exact timestamp using the implicit syntax, or use the `$eq`,`$gt`,`$gte`,`$lt`,`$lte` operators with the explicit syntax.
```json Companies created on 2023-11-24 at 15:34:07.111222333Z theme={"system"}
{
"filter": {
"created_at": "2023-11-24T15:34:07.111222333Z"
}
}
```
```json ... after the year 2000 theme={"system"}
{
"filter": {
"created_at": {
"value": {
"$gte": "2000-01-01T00:00:00Z"
}
}
}
}
```
Since timestamps are stored with nanosecond precision, it is often undesirable to look for an exact timestamp if you're trying to filter records with a lower precision, e.g. in a given hour or day. Here, you should use two operators to set the upper and lower bounds of the query, remembering to use the inclusive `$gte` for the earlier bound and the exclusive `$lt` for the upper bound:
```json Companies created between 8am and 9am theme={"system"}
{
"filter": {
"created_at": {
"$gte": "2023-11-24T08:00:00Z",
"$lt": "2023-11-24T09:00:00Z"
}
}
}
```
```json ... on 24th November theme={"system"}
{
"filter": {
"created_at": {
"$gte": "2023-11-24T00:00:00Z",
"$lt": "2023-11-25T00:00:00Z"
}
}
}
```
# Create a select option
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/create-a-select-option
https://api.attio.com/openapi/api post /v2/{target}/{identifier}/attributes/{attribute}/options
Adds a select option to a select attribute on an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read-write`. When `target` is `lists`, the required scopes are `list_configuration:read-write`.
# Create a status
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/create-a-status
https://api.attio.com/openapi/api post /v2/{target}/{identifier}/attributes/{attribute}/statuses
Add a new status to a status attribute on either an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read-write`. When `target` is `lists`, the required scopes are `list_configuration:read-write`.
# Create an attribute
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/create-an-attribute
https://api.attio.com/openapi/api post /v2/{target}/{identifier}/attributes
Creates a new attribute on either an object or a list.
For record-reference attributes, you can optionally create a bidirectional relationship by providing a `relationship` object. This will create two entangled attributes: one on the specified object and a reverse attribute on the related object.
To create an attribute on an object, you must also have the `object_configuration:read-write` scope.
To create an attribute on a list, you must also have the `list_configuration:read-write` scope.
# Get an attribute
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/get-an-attribute
https://api.attio.com/openapi/api get /v2/{target}/{identifier}/attributes/{attribute}
Gets information about a single attribute on either an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read`. When `target` is `lists`, the required scopes are `list_configuration:read`.
# List attributes
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/list-attributes
https://api.attio.com/openapi/api get /v2/{target}/{identifier}/attributes
Lists all attributes defined on a specific object or list. Attributes are returned in the order that they are sorted by in the UI.
When `target` is `objects`, the required scopes are `object_configuration:read`. When `target` is `lists`, the required scopes are `list_configuration:read`.
# List select options
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/list-select-options
https://api.attio.com/openapi/api get /v2/{target}/{identifier}/attributes/{attribute}/options
Lists all select options for a particular attribute on either an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read`. When `target` is `lists`, the required scopes are `list_configuration:read`.
# List statuses
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/list-statuses
https://api.attio.com/openapi/api get /v2/{target}/{identifier}/attributes/{attribute}/statuses
Lists all statuses for a particular status attribute on either an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read`. When `target` is `lists`, the required scopes are `list_configuration:read`.
# Update a select option
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/update-a-select-option
https://api.attio.com/openapi/api patch /v2/{target}/{identifier}/attributes/{attribute}/options/{option}
Updates a select option on an attribute on either an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read-write`. When `target` is `lists`, the required scopes are `list_configuration:read-write`.
# Update a status
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/update-a-status
https://api.attio.com/openapi/api patch /v2/{target}/{identifier}/attributes/{attribute}/statuses/{status}
Update a status on an status attribute on either an object or a list.
When `target` is `objects`, the required scopes are `object_configuration:read-write`. When `target` is `lists`, the required scopes are `list_configuration:read-write`.
# Update an attribute
Source: https://docs.attio.com/rest-api/endpoint-reference/attributes/update-an-attribute
https://api.attio.com/openapi/api patch /v2/{target}/{identifier}/attributes/{attribute}
Updates a single attribute on a given object or list.
When `target` is `objects`, the required scopes are `object_configuration:read-write`. When `target` is `lists`, the required scopes are `list_configuration:read-write`.
# Get call recording
Source: https://docs.attio.com/rest-api/endpoint-reference/call-recordings/get-call-recording
https://api.attio.com/openapi/api get /v2/meetings/{meeting_id}/call_recordings/{call_recording_id}
Get a single call recording by ID.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `meeting:read`, `call_recording:read`.
# List call recordings
Source: https://docs.attio.com/rest-api/endpoint-reference/call-recordings/list-call-recordings
https://api.attio.com/openapi/api get /v2/meetings/{meeting_id}/call_recordings
List all call recordings for a meeting.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `meeting:read`, `call_recording:read`.
# Create a comment
Source: https://docs.attio.com/rest-api/endpoint-reference/comments/create-a-comment
https://api.attio.com/openapi/api post /v2/comments
Creates a new comment related to an existing thread, record or entry.
To create comments on records, you will need the `object_configuration:read` and `record_permission:read` scopes.
To create comments on list entries, you will need the `list_configuration:read` and `list_entry:read` scopes.
Required scopes: `comment:read-write`.
# Delete a comment
Source: https://docs.attio.com/rest-api/endpoint-reference/comments/delete-a-comment
https://api.attio.com/openapi/api delete /v2/comments/{comment_id}
Deletes a comment by ID. If deleting a comment at the head of a thread, all messages in the thread are also deleted.
Required scopes: `comment:read-write`.
# Get a comment
Source: https://docs.attio.com/rest-api/endpoint-reference/comments/get-a-comment
https://api.attio.com/openapi/api get /v2/comments/{comment_id}
Get a single comment by ID.
To view comments on records, you will need the `object_configuration:read` and `record_permission:read` scopes.
To view comments on list entries, you will need the `list_configuration:read` and `list_entry:read` scopes.
Required scopes: `comment:read`.
# Create a company record
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/create-a-company-record
https://api.attio.com/openapi/standard-objects post /v2/objects/companies/records
Creates a new company record. This endpoint will throw on conflicts of unique attributes, like `domains`. If you would prefer to update company records on conflicts, please use the Assert company record endpoint instead. Please note, the `logo_url` attribute cannot currently be set via the API.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Delete a company record
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/delete-a-company-record
https://api.attio.com/openapi/standard-objects delete /v2/objects/companies/records/{record_id}
Deletes a single company record by ID.
Required scopes: `object_configuration:read`, `record_permission:read-write`.
# Get a company record
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/get-a-company-record
https://api.attio.com/openapi/standard-objects get /v2/objects/companies/records/{record_id}
Gets a single company record by its `record_id`.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List company record attribute values
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/list-company-record-attribute-values
https://api.attio.com/openapi/standard-objects get /v2/objects/companies/records/{record_id}/attributes/{attribute}/values
Gets all values for a given attribute on a company record. Historic values can be queried using the `show_historic` query param. Historic values cannot be queried on COMINT (Communication Intelligence) or enriched attributes and the endpoint will return a 400 error if this is attempted. Historic values are sorted from oldest to newest (by `active_from`). Some attributes are subject to billing status and will return an empty array of values if theworkspace being queried does not have the required billing flag enabled.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List company record entries
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/list-company-record-entries
https://api.attio.com/openapi/standard-objects get /v2/objects/companies/records/{record_id}/entries
List all entries, across all lists, for which this company record is the parent.
Required scopes: `record_permission:read`, `object_configuration:read`, `list_entry:read`.
# List company records
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/list-company-records
https://api.attio.com/openapi/standard-objects post /v2/objects/companies/records/query
Lists company records, with the option to filter and sort results.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Update a company record
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/update-a-company-record
https://api.attio.com/openapi/standard-objects patch /v2/objects/companies/records/{record_id}
Use this endpoint to update company records by `record_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the [Upsert company endpoint](/rest-api/endpoint-reference/standard-objects/companies/upsert-a-company-record) to overwrite or remove multiselect attribute values. Please note, the `logo_url` attribute cannot currently be updated via the API.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Upsert a company record
Source: https://docs.attio.com/rest-api/endpoint-reference/companies/upsert-a-company-record
https://api.attio.com/openapi/standard-objects put /v2/objects/companies/records
Use this endpoint to create or update company records, using a unique attribute to search for existing companies (for example, the `domains` attribute). If a company is found with the same value for the matching attribute, that company will be updated. If no company with the same value for the matching attribute is found, a new company will be created instead. If you would like to avoid matching, please use the Create company endpoint.
If the matching attribute is a multiselect attribute, new values will be added and existing values will not be deleted. For any other multiselect attribute, all values will be either created or deleted as necessary to match the list of supplied values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Create a deal record
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/create-a-deal-record
https://api.attio.com/openapi/standard-objects post /v2/objects/deals/records
Creates a new deal record. This endpoint will throw on conflicts of unique attributes, if defined. If you would prefer to update deal records on conflicts, please use the Assert deal record endpoint instead.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Delete a deal record
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/delete-a-deal-record
https://api.attio.com/openapi/standard-objects delete /v2/objects/deals/records/{record_id}
Deletes a single deal record by ID.
Required scopes: `object_configuration:read`, `record_permission:read-write`.
# Get a deal record
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/get-a-deal-record
https://api.attio.com/openapi/standard-objects get /v2/objects/deals/records/{record_id}
Gets a single deal record by its `record_id`.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List deal record attribute values
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/list-deal-record-attribute-values
https://api.attio.com/openapi/standard-objects get /v2/objects/deals/records/{record_id}/attributes/{attribute}/values
Gets all values for a given attribute on a deal record. Historic values can be queried using the `show_historic` query param. Historic values are sorted from oldest to newest (by `active_from`).
Required scopes: `record_permission:read`, `object_configuration:read`.
# List deal record entries
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/list-deal-record-entries
https://api.attio.com/openapi/standard-objects get /v2/objects/deals/records/{record_id}/entries
List all entries, across all lists, for which this deal record is the parent.
Required scopes: `record_permission:read`, `object_configuration:read`, `list_entry:read`.
# List deal records
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/list-deal-records
https://api.attio.com/openapi/standard-objects post /v2/objects/deals/records/query
Lists deal records, with the option to filter and sort results.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Update a deal record
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/update-a-deal-record
https://api.attio.com/openapi/standard-objects patch /v2/objects/deals/records/{record_id}
Use this endpoint to update deal records by `record_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the [Upsert deal endpoint](/rest-api/endpoint-reference/standard-objects/deals/upsert-a-deal-record) to overwrite or remove multiselect attribute values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Upsert a deal record
Source: https://docs.attio.com/rest-api/endpoint-reference/deals/upsert-a-deal-record
https://api.attio.com/openapi/standard-objects put /v2/objects/deals/records
Use this endpoint to create or update deal records, using a unique attribute to search for existing deals. By default, deals do not have a unique attribute, so you should add your own attribute with a unique constraint to use this API. If a deal is found with the same value for the matching attribute, that deal will be updated, otherwise a new deal will be created instead. If the matching attribute is a multiselect attribute, new values will be added and existing values will not be deleted. For any other multiselect attribute, all values will be either created or deleted as necessary to match the list of supplied values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Create an entry (add record to list)
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/create-an-entry-add-record-to-list
https://api.attio.com/openapi/api post /v2/lists/{list}/entries
Adds a record to a list as a new list entry. This endpoint will throw on conflicts of unique attributes. Multiple list entries are allowed for the same parent record
Required scopes: `list_entry:read-write`, `list_configuration:read`.
# Delete a list entry
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/delete-a-list-entry
https://api.attio.com/openapi/api delete /v2/lists/{list}/entries/{entry_id}
Deletes a single list entry by its `entry_id`.
Required scopes: `list_entry:read-write`, `list_configuration:read`.
# Get a list entry
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/get-a-list-entry
https://api.attio.com/openapi/api get /v2/lists/{list}/entries/{entry_id}
Gets a single list entry by its `entry_id`.
Required scopes: `list_entry:read`, `list_configuration:read`.
# List attribute values for a list entry
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/list-attribute-values-for-a-list-entry
https://api.attio.com/openapi/api get /v2/lists/{list}/entries/{entry_id}/attributes/{attribute}/values
Gets all values for a given attribute on a list entry. This endpoint has the ability to return all historic values using the `show_historic` query param. Historic values are sorted from oldest to newest (by `active_from`).
Required scopes: `list_entry:read`, `list_configuration:read`.
# List entries
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/list-entries
https://api.attio.com/openapi/api post /v2/lists/{list}/entries/query
Lists entries in a given list, with the option to filter and sort results.
Required scopes: `list_entry:read`, `list_configuration:read`.
# Update a list entry (append multiselect values)
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/update-a-list-entry-append-multiselect-values
https://api.attio.com/openapi/api patch /v2/lists/{list}/entries/{entry_id}
Use this endpoint to update list entries by `entry_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the `PUT` endpoint to overwrite or remove multiselect attribute values.
Required scopes: `list_entry:read-write`, `list_configuration:read`.
# Update a list entry (overwrite multiselect values)
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/update-a-list-entry-overwrite-multiselect-values
https://api.attio.com/openapi/api put /v2/lists/{list}/entries/{entry_id}
Use this endpoint to update list entries by `entry_id`. If the update payload includes multiselect attributes, the values supplied will overwrite/remove the list of values that already exist (if any). Use the `PATCH` endpoint to add multiselect attribute values without removing those value that already exist.
Required scopes: `list_entry:read-write`, `list_configuration:read`.
# Upsert a list entry by parent
Source: https://docs.attio.com/rest-api/endpoint-reference/entries/upsert-a-list-entry-by-parent
https://api.attio.com/openapi/api put /v2/lists/{list}/entries
Use this endpoint to create or update a list entry for a given parent record. If an entry with the specified parent record is found, that entry will be updated. If no such entry is found, a new entry will be created instead. If there are multiple entries with the same parent record, this endpoint with return the "MULTIPLE_MATCH_RESULTS" error. When writing to multi-select attributes, all values will be either created or deleted as necessary to match the list of values supplied in the request body.
Required scopes: `list_entry:read-write`, `list_configuration:read`.
# Create a folder
Source: https://docs.attio.com/rest-api/endpoint-reference/files/create-a-folder
https://api.attio.com/openapi/api post /v2/files
Creates a native folder entry or a connected file/folder entry on an object record.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `file:read-write`, `object_configuration:read`, `record_permission:read`.
# Delete a file
Source: https://docs.attio.com/rest-api/endpoint-reference/files/delete-a-file
https://api.attio.com/openapi/api delete /v2/files/{file_id}
Delete a single file by ID. Deleting a folder will delete all of its descendants.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `file:read-write`, `object_configuration:read`, `record_permission:read`.
# Download a file
Source: https://docs.attio.com/rest-api/endpoint-reference/files/download-a-file
https://api.attio.com/openapi/api get /v2/files/{file_id}/download
Downloads a file by redirecting to a signed URL.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `object_configuration:read`, `record_permission:read`, `file:read`.
# Get a file
Source: https://docs.attio.com/rest-api/endpoint-reference/files/get-a-file
https://api.attio.com/openapi/api get /v2/files/{file_id}
Get a single file entry by ID.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `file:read`, `object_configuration:read`, `record_permission:read`.
# List files
Source: https://docs.attio.com/rest-api/endpoint-reference/files/list-files
https://api.attio.com/openapi/api get /v2/files
Lists internal files, externally connected files and folders for a specific record. Use the `object` and `record_id` query parameters to specify the record. Optional query parameters may be provided to filter results by storage provider or parent folder.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `object_configuration:read`, `record_permission:read`, `file:read`.
# Upload a file
Source: https://docs.attio.com/rest-api/endpoint-reference/files/upload-a-file
https://api.attio.com/openapi/api post /v2/files/upload
Uploads a file to native Attio storage for a record. Send multipart/form-data with a single binary field named `file` together with the body fields `object`, `record_id`, and optional `parent_folder_id`. Maximum file size is 50 MB.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `file:read-write`, `object_configuration:read`, `record_permission:read`.
# Create a list
Source: https://docs.attio.com/rest-api/endpoint-reference/lists/create-a-list
https://api.attio.com/openapi/api post /v2/lists
Creates a new list.
Once you have your list, add attributes to it using the [Create attribute](/rest-api/endpoint-reference/attributes/create-an-attribute) API, and add records to it using the [Add records to list](/rest-api/endpoint-reference/entries/create-an-entry-add-record-to-list) API.
New lists must specify which records can be added with the `parent_object` parameter which accepts either an object slug or an object ID. Permissions for the list are controlled with the `workspace_access` and `workspace_member_access` parameters.
Please note that new lists must have either `workspace_access` set to `"full-access"` or one or more element of `workspace_member_access` with a `"full-access"` level. It is also possible to receive a `403` billing error if your workspace is not on a plan that supports either advanced workspace or workspace member-level access for lists.
Required scopes: `list_configuration:read-write`.
# Get a list
Source: https://docs.attio.com/rest-api/endpoint-reference/lists/get-a-list
https://api.attio.com/openapi/api get /v2/lists/{list}
Gets a single list in your workspace that your access token has access to.
Required scopes: `list_configuration:read`.
# List all lists
Source: https://docs.attio.com/rest-api/endpoint-reference/lists/list-all-lists
https://api.attio.com/openapi/api get /v2/lists
List all lists that your access token has access to. lists are returned in the order that they are sorted in the sidebar.
Required scopes: `list_configuration:read`.
# List views for list
Source: https://docs.attio.com/rest-api/endpoint-reference/lists/list-views-for-list
https://api.attio.com/openapi/api get /v2/lists/{list}/views
Lists saved views for a list. Results are ordered by view ID (`id.view_id` ascending).
Required scopes: `list_configuration:read`.
# Update a list
Source: https://docs.attio.com/rest-api/endpoint-reference/lists/update-a-list
https://api.attio.com/openapi/api patch /v2/lists/{list}
Updates an existing list. Permissions for the list are controlled with the `workspace_access` and `workspace_member_access` parameters. Please note that lists must have either `workspace_access` set to `"full-access"` or one or more element of `workspace_member_access` with a `"full-access"` level. It is also possible to receive a `403` billing error if your workspace is not on a plan that supports either advanced workspace or workspace member level access for lists. Changing the parent object of a list is not possible through the API as it can have unintended side-effects that should be considered carefully. If you wish to carry out a parent object change you should do so through the UI.
Required scopes: `list_configuration:read-write`.
# Get a meeting
Source: https://docs.attio.com/rest-api/endpoint-reference/meetings/get-a-meeting
https://api.attio.com/openapi/api get /v2/meetings/{meeting_id}
Get a single meeting by ID.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `meeting:read`, `record_permission:read`.
# List meetings
Source: https://docs.attio.com/rest-api/endpoint-reference/meetings/list-meetings
https://api.attio.com/openapi/api get /v2/meetings
Lists all meetings in the workspace using a deterministic sort order. When both the `participants` and `linked_record_id` filters are supplied, they are combined with OR: meetings that match either filter are returned.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `meeting:read`, `record_permission:read`.
# Identify
Source: https://docs.attio.com/rest-api/endpoint-reference/meta/identify
https://api.attio.com/openapi/api get /v2/self
Identify the current access token, the workspace it is linked to, and any permissions it has.
# Create a note
Source: https://docs.attio.com/rest-api/endpoint-reference/notes/create-a-note
https://api.attio.com/openapi/api post /v2/notes
Creates a new note for a given record.
Required scopes: `note:read-write`, `object_configuration:read`, `record_permission:read`.
# Delete a note
Source: https://docs.attio.com/rest-api/endpoint-reference/notes/delete-a-note
https://api.attio.com/openapi/api delete /v2/notes/{note_id}
Delete a single note by ID.
Required scopes: `note:read-write`.
# Get a note
Source: https://docs.attio.com/rest-api/endpoint-reference/notes/get-a-note
https://api.attio.com/openapi/api get /v2/notes/{note_id}
Get a single note by ID.
Required scopes: `note:read`, `object_configuration:read`, `record_permission:read`.
# List notes
Source: https://docs.attio.com/rest-api/endpoint-reference/notes/list-notes
https://api.attio.com/openapi/api get /v2/notes
List notes for all records or for a specific record.
Required scopes: `note:read`, `object_configuration:read`, `record_permission:read`.
# Create an object
Source: https://docs.attio.com/rest-api/endpoint-reference/objects/create-an-object
https://api.attio.com/openapi/api post /v2/objects
Creates a new custom object in your workspace.
Required scopes: `object_configuration:read-write`.
# Get an object
Source: https://docs.attio.com/rest-api/endpoint-reference/objects/get-an-object
https://api.attio.com/openapi/api get /v2/objects/{object}
Gets a single object by its `object_id` or slug.
Required scopes: `object_configuration:read`.
# List objects
Source: https://docs.attio.com/rest-api/endpoint-reference/objects/list-objects
https://api.attio.com/openapi/api get /v2/objects
Lists all system-defined and user-defined objects in your workspace.
Required scopes: `object_configuration:read`.
# List views for object
Source: https://docs.attio.com/rest-api/endpoint-reference/objects/list-views-for-object
https://api.attio.com/openapi/api get /v2/objects/{object}/views
Lists saved views for an object. Results are ordered by view ID (`id.view_id` ascending).
Required scopes: `object_configuration:read`.
# Update an object
Source: https://docs.attio.com/rest-api/endpoint-reference/objects/update-an-object
https://api.attio.com/openapi/api patch /v2/objects/{object}
Updates a single object. The object to be updated is identified by its `object_id`.
Required scopes: `object_configuration:read-write`.
# OpenAPI
Source: https://docs.attio.com/rest-api/endpoint-reference/openapi
Access the OpenAPI specification for the Attio REST API
Attio exposes a public OpenAPI specification for the Attio REST API. The specification is available
[here](https://api.attio.com/openapi/api).
# Create a person Record
Source: https://docs.attio.com/rest-api/endpoint-reference/people/create-a-person-record
https://api.attio.com/openapi/standard-objects post /v2/objects/people/records
Creates a new person Record. This endpoint will throw on conflicts of unique attributes, like `email_addresses`. If you would prefer to update person records on conflicts, please use the Assert person record endpoint instead. Please note, the `avatar_url` attribute cannot currently be set via the API.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Delete a person Record
Source: https://docs.attio.com/rest-api/endpoint-reference/people/delete-a-person-record
https://api.attio.com/openapi/standard-objects delete /v2/objects/people/records/{record_id}
Deletes a single person record by ID.
Required scopes: `object_configuration:read`, `record_permission:read-write`.
# Get a person Record
Source: https://docs.attio.com/rest-api/endpoint-reference/people/get-a-person-record
https://api.attio.com/openapi/standard-objects get /v2/objects/people/records/{record_id}
Gets a single person record by its `record_id`.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List person record attribute values
Source: https://docs.attio.com/rest-api/endpoint-reference/people/list-person-record-attribute-values
https://api.attio.com/openapi/standard-objects get /v2/objects/people/records/{record_id}/attributes/{attribute}/values
Gets all values for a given attribute on a person record. Historic values can be queried using the `show_historic` query param. Historic values cannot be queried on COMINT (Communication Intelligence) or enriched attributes and the endpoint will return a 400 error if this is attempted. Historic values are sorted from oldest to newest (by `active_from`). Some attributes are subject to billing status and will return an empty array of values if theworkspace being queried does not have the required billing flag enabled.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List person record entries
Source: https://docs.attio.com/rest-api/endpoint-reference/people/list-person-record-entries
https://api.attio.com/openapi/standard-objects get /v2/objects/people/records/{record_id}/entries
List all entries, across all lists, for which this person record is the parent.
Required scopes: `record_permission:read`, `object_configuration:read`, `list_entry:read`.
# List person records
Source: https://docs.attio.com/rest-api/endpoint-reference/people/list-person-records
https://api.attio.com/openapi/standard-objects post /v2/objects/people/records/query
Lists person records, with the option to filter and sort results.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Update a person Record
Source: https://docs.attio.com/rest-api/endpoint-reference/people/update-a-person-record
https://api.attio.com/openapi/standard-objects patch /v2/objects/people/records/{record_id}
Use this endpoint to update person records by `record_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the [Upsert person endpoint](/reference/put_v2-objects-people-records) to overwrite or remove multiselect attribute values. Please note, the `avatar_url` attribute cannot currently be updated via the API.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Upsert a person record
Source: https://docs.attio.com/rest-api/endpoint-reference/people/upsert-a-person-record
https://api.attio.com/openapi/standard-objects put /v2/objects/people/records
Use this endpoint to create or update person records, using a unique attribute to search for existing People (for example the `email_addresses` attribute). If a person is found with the same value for the matching attribute, that person will be updated. If no person with the same value for the matching attribute is found, a new person will be created instead. If you would like to avoid matching, please use the Create person endpoint.
If the matching attribute is a multiselect attribute, new values will be added and existing values will not be deleted. For any other multiselect attribute, all values will be either created or deleted as necessary to match the list of supplied values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Create a record
Source: https://docs.attio.com/rest-api/endpoint-reference/records/create-a-record
https://api.attio.com/openapi/api post /v2/objects/{object}/records
Creates a new person, company or other record. This endpoint will throw on conflicts of unique attributes. If you would prefer to update records on conflicts, please use the [Upsert record endpoint](/rest-api/endpoint-reference/records/upsert-a-record) instead.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Delete a record
Source: https://docs.attio.com/rest-api/endpoint-reference/records/delete-a-record
https://api.attio.com/openapi/api delete /v2/objects/{object}/records/{record_id}
Deletes a single record (e.g. a company or person) by ID.
Required scopes: `object_configuration:read`, `record_permission:read-write`.
# Get a record
Source: https://docs.attio.com/rest-api/endpoint-reference/records/get-a-record
https://api.attio.com/openapi/api get /v2/objects/{object}/records/{record_id}
Gets a single person, company or other record by its `record_id`.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List record attribute values
Source: https://docs.attio.com/rest-api/endpoint-reference/records/list-record-attribute-values
https://api.attio.com/openapi/api get /v2/objects/{object}/records/{record_id}/attributes/{attribute}/values
Gets all values for a given attribute on a record. Historic values can be queried using the `show_historic` query param. Historic values cannot be queried on COMINT (Communication Intelligence) or enriched attributes and the endpoint will return a 400 error if this is attempted. Historic values are sorted from oldest to newest (by `active_from`). Some attributes are subject to billing status and will return an empty array of values if theworkspace being queried does not have the required billing flag enabled.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List record entries
Source: https://docs.attio.com/rest-api/endpoint-reference/records/list-record-entries
https://api.attio.com/openapi/api get /v2/objects/{object}/records/{record_id}/entries
List all entries, across all lists, for which this record is the parent.
Required scopes: `record_permission:read`, `object_configuration:read`, `list_entry:read`.
# List records
Source: https://docs.attio.com/rest-api/endpoint-reference/records/list-records
https://api.attio.com/openapi/api post /v2/objects/{object}/records/query
Lists people, company or other records, with the option to filter and sort results.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Search records
Source: https://docs.attio.com/rest-api/endpoint-reference/records/search-records
https://api.attio.com/openapi/api post /v2/objects/records/search
The search records endpoint provides a convenient way to fuzzy search for records across one or more objects.
The matching strategy employed in this endpoint follows the in-product strategy and will match names, domains, emails, phone numbers and social handles on people and companies, and labels on all other objects.
Please note, results returned from this endpoint are eventually consistent. For results which are guaranteed to be up to date, please use the record query endpoint instead.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Update a record (append multiselect values)
Source: https://docs.attio.com/rest-api/endpoint-reference/records/update-a-record-append-multiselect-values
https://api.attio.com/openapi/api patch /v2/objects/{object}/records/{record_id}
Use this endpoint to update people, companies, and other records by `record_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the `PUT` endpoint to overwrite or remove multiselect attribute values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Update a record (overwrite multiselect values)
Source: https://docs.attio.com/rest-api/endpoint-reference/records/update-a-record-overwrite-multiselect-values
https://api.attio.com/openapi/api put /v2/objects/{object}/records/{record_id}
Use this endpoint to update people, companies, and other records by `record_id`. If the update payload includes multiselect attributes, the values supplied will overwrite/remove the list of values that already exist (if any). Use the `PATCH` endpoint to append multiselect values without removing those that already exist.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Upsert a record
Source: https://docs.attio.com/rest-api/endpoint-reference/records/upsert-a-record
https://api.attio.com/openapi/api put /v2/objects/{object}/records
Use this endpoint to create or update people, companies and other records. A matching attribute is used to search for existing records. If a record is found with the same value for the matching attribute, that record will be updated. If no record with the same value for the matching attribute is found, a new record will be created instead. If you would like to avoid matching, please use the [Create record endpoint](/rest-api/endpoint-reference/records/create-a-record).
If the matching attribute is a multiselect attribute, new values will be added and existing values will not be deleted. For any other multiselect attribute, all values will be either created or deleted as necessary to match the list of supplied values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Query SQL
Source: https://docs.attio.com/rest-api/endpoint-reference/sql/query-sql
https://api.attio.com/openapi/api post /v2/sql
Query records and lists with SQL.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Create a task
Source: https://docs.attio.com/rest-api/endpoint-reference/tasks/create-a-task
https://api.attio.com/openapi/api post /v2/tasks
Creates a new task.
At present, tasks can only be created from plaintext without record reference formatting.
Required scopes: `task:read-write`, `object_configuration:read`, `record_permission:read`, `user_management:read`.
# Delete a task
Source: https://docs.attio.com/rest-api/endpoint-reference/tasks/delete-a-task
https://api.attio.com/openapi/api delete /v2/tasks/{task_id}
Delete a task by ID.
Required scopes: `task:read-write`.
# Get a task
Source: https://docs.attio.com/rest-api/endpoint-reference/tasks/get-a-task
https://api.attio.com/openapi/api get /v2/tasks/{task_id}
Get a single task by ID.
Required scopes: `task:read`, `object_configuration:read`, `record_permission:read`, `user_management:read`.
# List tasks
Source: https://docs.attio.com/rest-api/endpoint-reference/tasks/list-tasks
https://api.attio.com/openapi/api get /v2/tasks
List all tasks. Results are sorted by creation date, from oldest to newest.
Required scopes: `task:read`, `object_configuration:read`, `record_permission:read`, `user_management:read`.
# Update a task
Source: https://docs.attio.com/rest-api/endpoint-reference/tasks/update-a-task
https://api.attio.com/openapi/api patch /v2/tasks/{task_id}
Updates an existing task by `task_id`. At present, only the `deadline_at`, `is_completed`, `linked_records`, and `assignees` fields can be updated.
Required scopes: `task:read-write`, `object_configuration:read`, `record_permission:read`, `user_management:read`.
# Get a thread
Source: https://docs.attio.com/rest-api/endpoint-reference/threads/get-a-thread
https://api.attio.com/openapi/api get /v2/threads/{thread_id}
Get all comments in a thread.
To view threads on records, you will need the `object_configuration:read` and `record_permission:read` scopes.
To view threads on list entries, you will need the `list_configuration:read` and `list_entry:read` scopes.
Required scopes: `comment:read`.
# List threads
Source: https://docs.attio.com/rest-api/endpoint-reference/threads/list-threads
https://api.attio.com/openapi/api get /v2/threads
List threads of comments on a record or list entry.
To view threads on records, you will need the `object_configuration:read` and `record_permission:read` scopes.
To view threads on list entries, you will need the `list_configuration:read` and `list_entry:read` scopes.
Required scopes: `comment:read`.
# Get call transcript
Source: https://docs.attio.com/rest-api/endpoint-reference/transcripts/get-call-transcript
https://api.attio.com/openapi/api get /v2/meetings/{meeting_id}/call_recordings/{call_recording_id}/transcript
Get the transcript for a call recording.
This endpoint is in beta. We will aim to avoid breaking changes, but small updates may be made as we roll out to more users.
Required scopes: `meeting:read`, `call_recording:read`.
# Create a user record
Source: https://docs.attio.com/rest-api/endpoint-reference/users/create-a-user-record
https://api.attio.com/openapi/standard-objects post /v2/objects/users/records
Creates a new user record. This endpoint will throw on conflicts of unique attributes, like `primary_email_address`. If you would prefer to update user records on conflicts, please use the Assert user record endpoint instead. Please note, the `avatar_url` attribute cannot currently be set via the API.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Delete a user record
Source: https://docs.attio.com/rest-api/endpoint-reference/users/delete-a-user-record
https://api.attio.com/openapi/standard-objects delete /v2/objects/users/records/{record_id}
Deletes a single user record by ID.
Required scopes: `object_configuration:read`, `record_permission:read-write`.
# Get a user record
Source: https://docs.attio.com/rest-api/endpoint-reference/users/get-a-user-record
https://api.attio.com/openapi/standard-objects get /v2/objects/users/records/{record_id}
Gets a single user record by its `record_id`.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List user record attribute values
Source: https://docs.attio.com/rest-api/endpoint-reference/users/list-user-record-attribute-values
https://api.attio.com/openapi/standard-objects get /v2/objects/users/records/{record_id}/attributes/{attribute}/values
Gets all values for a given attribute on a user record. Historic values can be queried using the `show_historic` query param. Historic values are sorted from oldest to newest (by `active_from`).
Required scopes: `record_permission:read`, `object_configuration:read`.
# List user record entries
Source: https://docs.attio.com/rest-api/endpoint-reference/users/list-user-record-entries
https://api.attio.com/openapi/standard-objects get /v2/objects/users/records/{record_id}/entries
List all entries, across all lists, for which this user record is the parent.
Required scopes: `record_permission:read`, `object_configuration:read`, `list_entry:read`.
# List user records
Source: https://docs.attio.com/rest-api/endpoint-reference/users/list-user-records
https://api.attio.com/openapi/standard-objects post /v2/objects/users/records/query
Lists user records, with the option to filter and sort results.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Update a user Record
Source: https://docs.attio.com/rest-api/endpoint-reference/users/update-a-user-record
https://api.attio.com/openapi/standard-objects patch /v2/objects/users/records/{record_id}
Use this endpoint to update user records by `record_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the [Upsert user endpoint](/reference/put_v2-objects-users-records) to overwrite or remove multiselect attribute values. Please note, the `avatar_url` attribute cannot currently be updated via the API.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Upsert a user record
Source: https://docs.attio.com/rest-api/endpoint-reference/users/upsert-a-user-record
https://api.attio.com/openapi/standard-objects put /v2/objects/users/records
Use this endpoint to create or update user records, using a unique attribute to search for existing users (for example the `primary_email_address` attribute). If a user is found with the same value for the matching attribute, that user will be updated, otherwise a new user will be created instead. If the matching attribute is a multiselect attribute, new values will be added and existing values will not be deleted. For any other multiselect attribute, all values will be either created or deleted as necessary to match the list of supplied values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Create a webhook
Source: https://docs.attio.com/rest-api/endpoint-reference/webhooks/create-a-webhook
https://api.attio.com/openapi/api post /v2/webhooks
Create a webhook and associated subscriptions.
Required scopes: `webhook:read-write`.
# Delete a webhook
Source: https://docs.attio.com/rest-api/endpoint-reference/webhooks/delete-a-webhook
https://api.attio.com/openapi/api delete /v2/webhooks/{webhook_id}
Delete a webhook by ID.
Required scopes: `webhook:read-write`.
# Get a webhook
Source: https://docs.attio.com/rest-api/endpoint-reference/webhooks/get-a-webhook
https://api.attio.com/openapi/api get /v2/webhooks/{webhook_id}
Get a single webhook.
Required scopes: `webhook:read`.
# List webhooks
Source: https://docs.attio.com/rest-api/endpoint-reference/webhooks/list-webhooks
https://api.attio.com/openapi/api get /v2/webhooks
Get all of the webhooks in your workspace.
Required scopes: `webhook:read`.
# Update a webhook
Source: https://docs.attio.com/rest-api/endpoint-reference/webhooks/update-a-webhook
https://api.attio.com/openapi/api patch /v2/webhooks/{webhook_id}
Update a webhook and associated subscriptions.
Required scopes: `webhook:read-write`.
# Get a workspace member
Source: https://docs.attio.com/rest-api/endpoint-reference/workspace-members/get-a-workspace-member
https://api.attio.com/openapi/api get /v2/workspace_members/{workspace_member_id}
Gets a single workspace member by ID.
Required scopes: `user_management:read`.
# List workspace members
Source: https://docs.attio.com/rest-api/endpoint-reference/workspace-members/list-workspace-members
https://api.attio.com/openapi/api get /v2/workspace_members
Lists all workspace members in the workspace.
Required scopes: `user_management:read`.
# Create a workspace record
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/create-a-workspace-record
https://api.attio.com/openapi/standard-objects post /v2/objects/workspaces/records
Creates a new workspace record. This endpoint will throw on conflicts of unique attributes, like `workspace_id`. If you would prefer to update workspace records on conflicts, please use the Assert workspace record endpoint instead.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Delete a workspace record
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/delete-a-workspace-record
https://api.attio.com/openapi/standard-objects delete /v2/objects/workspaces/records/{record_id}
Deletes a single workspace record by ID.
Required scopes: `object_configuration:read`, `record_permission:read-write`.
# Get a workspace record
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/get-a-workspace-record
https://api.attio.com/openapi/standard-objects get /v2/objects/workspaces/records/{record_id}
Gets a single workspace record by its `record_id`.
Required scopes: `record_permission:read`, `object_configuration:read`.
# List workspace record attribute values
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/list-workspace-record-attribute-values
https://api.attio.com/openapi/standard-objects get /v2/objects/workspaces/records/{record_id}/attributes/{attribute}/values
Gets all values for a given attribute on a workspace record. Historic values can be queried using the `show_historic` query param. Historic values are sorted from oldest to newest (by `active_from`).
Required scopes: `record_permission:read`, `object_configuration:read`.
# List workspace record entries
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/list-workspace-record-entries
https://api.attio.com/openapi/standard-objects get /v2/objects/workspaces/records/{record_id}/entries
List all entries, across all lists, for which this workspace record is the parent.
Required scopes: `record_permission:read`, `object_configuration:read`, `list_entry:read`.
# List workspace records
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/list-workspace-records
https://api.attio.com/openapi/standard-objects post /v2/objects/workspaces/records/query
Lists workspace records, with the option to filter and sort results.
Required scopes: `record_permission:read`, `object_configuration:read`.
# Update a workspace Record
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/update-a-workspace-record
https://api.attio.com/openapi/standard-objects patch /v2/objects/workspaces/records/{record_id}
Use this endpoint to update workspace records by `record_id`. If the update payload includes multiselect attributes, the values supplied will be created and prepended to the list of values that already exist (if any). Use the [Upsert workspace endpoint](/rest-api/endpoint-reference/standard-objects/workspaces/upsert-a-workspace-record) to overwrite or remove multiselect attribute values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Upsert a workspace record
Source: https://docs.attio.com/rest-api/endpoint-reference/workspaces/upsert-a-workspace-record
https://api.attio.com/openapi/standard-objects put /v2/objects/workspaces/records
Use this endpoint to create or update workspace records, using a unique attribute to search for existing workspaces (for example the `workspace_id` attribute). If a workspace is found with the same value for the matching attribute, that workspace will be updated, otherwise a new workspace will be created instead. If the matching attribute is a multiselect attribute, new values will be added and existing values will not be deleted. For any other multiselect attribute, all values will be either created or deleted as necessary to match the list of supplied values.
Required scopes: `record_permission:read-write`, `object_configuration:read`.
# Authenticating requests
Source: https://docs.attio.com/rest-api/guides/authentication
How to generate access tokens and make requests
In order to make requests to the Attio REST API, you need to generate an access token.
There are two ways to generate an access token:
1. By implementing an OAuth 2.0 flow
2. By generating an API key for your workspace
You should prefer the OAuth 2.0 flow if building an app for multiple workspaces. If you are building
an app for a single workspace, you can manually generate an API key to make requests on behalf of
that workspace only.
## Generating access tokens
### OAuth 2.0
Attio implements the standard [OAuth 2.0 specification](https://datatracker.ietf.org/doc/html/rfc6749).
You can find the reference for our OAuth authorize, token exchange and introspect endpoints
[here](/docs/oauth/authorize).
If you would prefer a tutorial on how to implement an OAuth 2.0 flow into an existing app, you can
find one [here](/rest-api/tutorials/connect-an-app-through-oauth).
### API key
If you only need a token for a single workspace, you can generate an API key in the developer
settings page of your apps. You can find docs on to do this [here](https://attio.com/help/apps/other-apps/generating-an-api-key).
## Using tokens
Both OAuth access tokens and single-workspace access token are used in the same way. Pass the value
of the token in the `Authorization` header of your requests like so.
```
Authorization: Bearer
```
We also support [HTTP Basic
Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization#basic_authentication),
where the username is the token and the password is left blank. However, we recommend using Bearer
authentication where possible.
### Scopes
Both OAuth access tokens and single-workspace access tokens use scopes to control the resources that
the token has access to and the actions that can be performed on those resources.
The possible scopes for OAuth and single-workspace access tokens are the same.
The reference documentation for each endpoint includes a "Required scopes" section that lists the
scopes needed to call that endpoint.
When using an OAuth access token, the scopes are specified by configuring the scope settings for
your app in the [developer dashboard](https://build.attio.com).
When using a single-workspace access token, the scopes are specified in the settings UI when
generating the token. Scopes for single-workspace access tokens can also be modified on existing
tokens.
# Filtering and sorting
Source: https://docs.attio.com/rest-api/guides/filtering-and-sorting
Searching for just the records you need
Some of our endpoints allow specifying `filter` or `sorts` objects as part of the request. These properties can help reduce the size of the result set or get them back in a more appropriate order.
## Filtering
Attio supports two distinct formats for filtering. There is the verbose format, which supports a wider variety of operators and conditions, and a shorthand format, which is useful for quick equality checks.
### Shorthand filters
Shorthand filters generally look like this:
```http People called "John Smith" with the email "john@smith.com" theme={"system"}
POST /v2/objects/companies/records/query
Content-Type: application/json
{
"filter": {
"name": "John Smith",
"email_addresses": "john@smith.com"
}
}
```
### Verbose filters
Verbose filters allow joining multiple different conditions together, or querying for different properties of the attribute value. All shorthand syntaxes can be expressed in the verbose syntax. For example, the above filter can also be written as:
```http theme={"system"}
POST /v2/objects/companies/records/query
Content-Type: application/json
{
"filter": {
"$and": [
{
"name": {
"full_name": {
"$eq": "John Smith"
}
}
},
{
"email_addresses": {
"email_address": {
"$eq": "john@smith.com"
}
}
}
]
}
}
```
Note that we're using a logical operator (`$and`) to combine these two conditions, and that we're querying specific properties on the attributes (`full_name` and `normalized_email_address`).
Each attribute type has specific properties available, and so the set of possible filters varies by attribute type, but every attribute type is filterable in some way. We have examples of filtering by each attribute type in our [attribute types documentation](/rest-api/attribute-types/attribute-types).
Next, let's walk through some more advanced filtering, starting with the comparison operators.
## Comparison operators
There are nine comparison operators in total.
`$eq` is by far the most common, this operator checks for equality. It is supported by every attribute type. If you're using shorthand syntax, this is usually the implied operator, but it can also be specified explicitly:
```json Deals with an exact name theme={"system"}
{
"name": {
"$eq": "Contract with Apple"
}
}
```
`$not_empty` is also available on some attribute types, this operator allows filtering results based on whether there is any value defined at all, for example:
```json Companies which have at least one domain theme={"system"}
{
"domains": {
"$not_empty": true
}
}
```
`$in`can be used to check if the record or entry has a value that is part of a set.
```json Records where record_id is one of many values theme={"system"}
{
"filter": {
"record_id": {
"$in": ["000e8881-37cc-41d2-bc22-39fe35e76e6b", "592dc9d8-548b-4148-813f-1259055ca83c"]
}
}
}
```
There are also operators for string properties and numeric/date properties:
### String comparisons
For string-like properties or attributes, there are three further operators. `$contains` can be used for matching parts of a string, case-insensitively:
```json Companies in New York theme={"system"}
{
"primary_location": {
"locality": {
"$contains": "new york"
}
}
}
```
```json Companies with "LTD" in the name theme={"system"}
{
"name": {
"$contains": "LTD"
}
}
```
`$starts_with` and `$ends_with` can match on the beginning or the end of the string, respectively:
```json People with phone numbers starting +44 theme={"system"}
{
"phone_numbers": {
"$starts_with": "+44"
}
}
```
```json People with a job title "X engineer" theme={"system"}
{
"job_title": {
"$ends_with": "of things"
}
}
```
Note that these can be combined on the same property or attribute to achieve a logical AND:
```json Phone number starts with +44 and ends with 798 theme={"system"}
{
"phone_numbers": {
"$starts_with": "+44",
"$ends_with": "798"
}
}
```
### Numeric or date comparisons
There are four operators for comparing sortable properties like numbers or dates:
1. **Less than** (`$lt`), finds records where the value is strictly less than (exclusive) the given value
2. **Less than or equal** (`$lte`), finds records where the value is either less than or the same as the given value
3. **Greater than or equal** (`$gte`), finds records where the value is either greater than or the same as the given value
4. **Greater than** (`$gt`), finds records where the value is strictly more than (exclusive) the given value
```json People with 1000 or more Twitter followers theme={"system"}
{
"twitter_follower_count": {
"$gte": 1000
}
}
```
```json People with between 100 and 200 followers theme={"system"}
{
"twitter_follower_count": {
"$gte": 100,
"$lt": 200
}
}
```
You can also combine multiple comparators to find values between two points:
```json Companies founded in 2019 theme={"system"}
{
"foundation_date": {
"$gte": "2019-01-01",
"$lte": "2019-12-31"
}
}
```
## Supported operators by attribute type
Different attribute types support different filter operators. The table below provides an overview of the operators supported by each attribute type. For detailed examples and guidance, please see the [attribute type documentation](/rest-api/attribute-types/attribute-types).
| Attribute Type | `$eq` | `$in` | `$not_empty` | `$contains` | `$starts_with` | `$ends_with` | `$lt` | `$lte` | `$gt` | `$gte` |
| ---------------------------------------------------------------------------------------- | :---: | :---: | :----------: | :---------: | :------------: | :----------: | :---: | :----: | :---: | :----: |
| [Actor reference](/rest-api/attribute-types/attribute-types-actor-reference#filtering) | ✓ | | | | | | | | | |
| [Checkbox](/rest-api/attribute-types/attribute-types-checkbox#filtering) | ✓ | | | | | | | | | |
| [Currency](/rest-api/attribute-types/attribute-types-currency#filtering) | ✓ | | | | | | ✓ | ✓ | ✓ | ✓ |
| [Date](/rest-api/attribute-types/attribute-types-date#filtering) | ✓ | | | | | | ✓ | ✓ | ✓ | ✓ |
| [Domain](/rest-api/attribute-types/attribute-types-domain#filtering) | ✓ | | ✓ | ✓ | ✓ | ✓ | | | | |
| [Email address](/rest-api/attribute-types/attribute-types-email-address#filtering) | ✓ | | | ✓ | ✓ | ✓ | | | | |
| [Interaction](/rest-api/attribute-types/attribute-types-interaction#filtering) | ✓ | | ✓ | | | | ✓ | ✓ | ✓ | ✓ |
| [Location](/rest-api/attribute-types/attribute-types-location#filtering) | ✓ | | | ✓ | ✓ | ✓ | | | | |
| [Number](/rest-api/attribute-types/attribute-types-number#filtering) | ✓ | | | | | | ✓ | ✓ | ✓ | ✓ |
| [(Personal) name](/rest-api/attribute-types/attribute-types-personal-name#filtering) | ✓ | | ✓ | ✓ | ✓ | ✓ | | | | |
| [Phone number](/rest-api/attribute-types/attribute-types-phone-number#filtering) | ✓ | | ✓ | ✓ | ✓ | ✓ | | | | |
| [Rating](/rest-api/attribute-types/attribute-types-rating#filtering) | ✓ | | | | | | ✓ | ✓ | ✓ | ✓ |
| [Record reference](/rest-api/attribute-types/attribute-types-record-reference#filtering) | ✓ | ✓ | | | | | | | | |
| [Select](/rest-api/attribute-types/attribute-types-select#filtering) | ✓ | | | | | | | | | |
| [Status](/rest-api/attribute-types/attribute-types-status#filtering) | ✓ | | | | | | | | | |
| [Text](/rest-api/attribute-types/attribute-types-text#filtering) | ✓ | ✓ | | ✓ | ✓ | ✓ | | | | |
| [Timestamp](/rest-api/attribute-types/attribute-types-timestamp#filtering) | ✓ | | | | | | ✓ | ✓ | ✓ | ✓ |
Attribute values consist of multiple sub-values. For example, an email address value is made up of
`email_address`, `original_email_address`, `email_domain`, `email_root_domain` and
`email_local_specifier`. Operator support is provided per-field. The above table displays an
aggregated view of operator support across all attribute fields. For per-field support, please see
the individual attribute type documentation page.
## Logical operators
You can combine multiple conditions using the `$and`, `$or` and `$not` operators.
### `$and` operator
`$and` specifies that all conditions must match. If using the shorthand syntax with multiple attributes, this operator is implied. Note that you can also use the shorthand syntax for individual conditions—see the following example.
```json Deals assigned to Lauren in the "In Progress" state theme={"system"}
{
"$and": [
{"stage": "In Progress"},
{
"owner": {
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "[laurens-id]"
}
}
]
}
```
### `$or` operator
`$or` specifies that at least one of the conditions must match. It is not an exclusive-or, if all conditions match it will still pass.
```json List entries on a kanban board in either the "One" or "Two" stage theme={"system"}
{
"$or": [{"stage": "One"}, {"stage": "Two"}]
}
```
### `$not` operator
Attio doesn't offer negative operators, for example there is no inverse of `$eq` like `$neq`. Instead, filters should be wrapped using the `$not` operator, which matches all documents which don't meet the condition.
```json People not called John theme={"system"}
{
"$not": {
"name": {
"first_name": "John"
}
}
}
```
```json Deals not in the "In Progress" state theme={"system"}
{
"$not": {
"stage": "In Progress"
}
}
```
### Combining logical operators
It's possible to combine logical operators to build up trees of filters. For example, you could express multiple `$and` conditions, where some of them are a `$not`:
```json Companies with Apple in their name that aren't using an apple domain theme={"system"}
{
"$and": [
{"name": {"$contains": "Apple"}},
{
"$not": {
"domains": {
"root_domain": "apple.com"
}
}
}
]
}
```
Or you could query for "deals that are owned by Alice or Bob that are worth more than \$500:
```json Deals owned by Alice/Bob worth more than $500 theme={"system"}
{
"$and": [
{
"$or": [
{
"owner": {
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "[alices-id]"
}
},
{
"owner": {
"referenced_actor_type": "workspace-member",
"referenced_actor_id": "[bobs-id]"
}
}
]
},
{
"value": {
"$gt": 500
}
}
]
}
```
## Paths and parents
For [record reference attributes](/rest-api/attribute-types/attribute-types-record-reference), it's possible to use special `path` filtering by drilling down into the target objects. This filtering method is also supported on list entries.
For example, let's assume we have a list of people we want to hire, called "Candidates" (the `api_slug` is `candidates`). We could then write a query to find entries where the person has an `@apple.com` email address:
```json List entries filtered by their parent record email theme={"system"}
{
"path": [
["candidates", "parent_record"],
["people", "email_addresses"]
],
"constraints": {
"email_domain": "apple.com"
}
}
```
You can also filter directly on a record ID to get a specific record in a specific list:
```json List entries filtered by a specific record_id theme={"system"}
{
"path": [
["candidates", "parent_record"],
["people", "record_id"]
],
"constraints": {
"value": "[person-record-id]"
}
}
```
Note that we're using a special attribute called `parent_record` that we can use for filtering any list entry. We can even use these drill-down queries to express a more complicated query, like "entries where the candidate works at the same company as Steve Jobs":
```json Candidates who have worked with Steve Jobs theme={"system"}
{
"path": [
["candidates", "parent_record"],
["people", "company"],
["company", "team"]
],
"constraints": {
"target_object": "people",
"target_record_id": "[steve-jobs-record-id]"
}
}
```
## Sorting
Sorting allows us to get our results back in a particular order, based on attribute values. Each sort must specify a `direction`. If the target attribute is composed of multiple properties (like [(Personal) name](/rest-api/attribute-types/attribute-types-personal-name)), `field` can also be specified.
We can do sorting by `attribute`, which is either a slug or ID. For example, we could sort People by their last name, then their email address:
```json People sorted by last name, then email theme={"system"}
{
"sorts": [
{"direction": "asc", "attribute": "name", "field": "last_name"},
{"direction": "desc", "attribute": "email_addresses"}
]
}
```
We can also sort using paths, which works similarly to filtering by path. Instead of specifying `attribute`, you specify a `path` property which resolves to the attribute of the related record(s):
```json People sorted by their Company name theme={"system"}
{
"sorts": [
{
"direction": "asc",
"path": [
["people", "company"],
["companies", "name"]
]
}
]
}
```
# Paginating API results
Source: https://docs.attio.com/rest-api/guides/pagination
Retrieving particular ranges of data
API endpoints which are expected to return large numbers of results will use pagination to return a limited number of items at a time.
Depending on the endpoint, pagination will be implemented through either limit/offset pagination, or
cursor-based pagination.
## Limit/offset pagination
When using limit/offset pagination, two parameters are used to provide pagination functionality:
* `limit` controls the maximum number of results that an endpoint can return. The default value for limit is documented on each specific endpoint.
* `offset` controls how many values to skip over in the result set. By default, it is always 0.
When querying all results, your initial call should pass an `offset` value of 0. If the number of results you
receive is less than the value of the `limit` parameter (or the default limit if none was passed explicitly), you have
reached the end of the result set and can end pagination. Otherwise, make another API call with a new
`offset` value set to the previous `offset` + `limit` to get the next page, and then repeat until
you are at the end of the list.
For example, if there were 122 records available, your queries would look something like this for a `GET` endpoint using query parameters.
```http theme={"system"}
GET /v2/notes?limit=50 # Results 1 to 50
GET /v2/notes?limit=50&offset=50 # Results 51 to 100
GET /v2/notes?limit=50&offset=100 # Results 101 to 122, less than limit, stop here
```
For a `POST` endpoint using a body, your requests would look like this.
```http theme={"system"}
POST /v2/objects/people/records/query
{
"limit": 50,
"offset": 50
}
```
## Cursor-based pagination
Cursor-based pagination is implemented through the `limit` and `cursor` parameters. You will also
receive a `next_cursor` value in the API response.
* `cursor` should only be set to the opaque cursor value provided in the API response and will be used to get the next page of results.
* `limit` is the same as in limit/offset pagination; it controls the maximum number of results that
an endpoint can return.
Your initial request should omit the `cursor` parameter and specify a `limit` value (or rely on the
endpoint's default). Your response will include `pagination.next_cursor` which you should use in
your next request by passing it as the `cursor` parameter. It is important to keep the limit as
well as any filter parameters consistent between requests to avoid receiving unexpected results.
For example, to call our list meetings endpoint, you would do the following:
```http theme={"system"}
GET /v2/meetings?limit=50
GET /v2/meetings?limit=50&cursor=cursor-1
GET /v2/meetings?limit=50&cursor=cursor-2
```
##### Example response
```jsonc theme={"system"}
{
"data": [
/* meeting results here */
],
"pagination": {
"next_cursor": "opaque-cursor-value-here"
}
}
```
# Handling rate limits
Source: https://docs.attio.com/rest-api/guides/rate-limiting
How to ensure your app handles rate limits gracefully
The Attio API applies rate limits to all API calls to ensure fair usage and maintain the
availability and stability of the platform.
Our rate limit across the whole API is **100 requests per second** for read requests, **25 requests per second** for write requests. We may occasionally reduce the rate limit as part of incident response to protect our other systems, and we may also permanently lower it for specific APIs which handle a lot of data (this will be documented on the endpoint if so).
This means that your software should be prepared to receive this response, and handle it appropriately, even if you don’t currently anticipate making this many requests.
## Rate limit response
If you exceed the limit, you'll get a special HTTP response, like this:
```http HTTP theme={"system"}
HTTP/1.1 429 Too Many Requests
Retry-After: Tue, 23 May 2023 14:42:01 GMT
Content-Type: application/json
{
"status_code": 429,
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded, please try again later"
}
```
Rate limit responses always have the HTTP status code `429`. They also always include a `Retry-After` header, which is a date at which the limit resets (usually the following second), and they may also include additional debugging information in the JSON body.
### Handling rate limit responses
A rate limited response means that we have not processed the request, so it can be safely retried after the limit has reset. Typically this will be in the next clock second. Most programming languages offer a sleep command or equivalent that allows waiting for the reset time to elapse.
Alternatively, you might want to put the request into a background queue to be processed in a different execution thread later.
## Score-based rate limits
Attio's [List records](/rest-api/endpoint-reference/records/list-records) and [List entries](/rest-api/endpoint-reference/entries/list-entries) endpoints also utilize score-based rate limits.
Each request receives a complexity score. The score is a function of the requests' sorts and filters as well as the total record/entry count for the object/list.
Scores may trigger rate limits in two ways:
1. The score for an individual query may exceed the per-query limit. In this case, you should reduce the complexity of your query and try again.
2. The summed scores across multiple queries may exceed the limit for a given window of time. For this limit, we utilize a sliding window algorithm with a 10 second window. Scores are summed across all apps and access tokens using the API. You will receive a clear 429 error and a `Retry-After` header when you hit a limit.
# Configuring webhooks
Source: https://docs.attio.com/rest-api/guides/webhooks
Responding to changes in real-time
Webhooks allow you to subscribe to changes that happen in Attio and then receive real-time HTTP requests to a chosen target URL to notify you of these changes.
This pattern can be useful in a wide range of contexts, but is often implemented by those who want to build real-time data syncs (e.g. an ETL pipeline) or fire automations in a timely manner (e.g. Attio’s own Zapier integration is powered by webhooks).
## Creating webhooks
There are two places you can create webhooks: in our settings pages, and the API.
### Creating using the API
We offer a range of endpoints for creating, updating, deleting and viewing webhooks. You can find these endpoints in the [webhooks API reference](/rest-api/endpoint-reference/webhooks).
Creating webhooks over the API is essential for those building integrations for Attio that will operate for many customers.
The webhook APIs also allow you to utilise our powerful filtering functionality (see “Filtering” section below).
### Creating using settings page
You can create webhooks for an integration in the developer settings page.
Please note that webhooks created with tokens that were created through our OAuth sign up flow will not be shown in the developer settings page.
## Authenticating
When receiving webhooks, you should ensure that the request came from us. We cryptographically sign every webhook request using your webhook secret, and include it as HTTP header called `Attio-Signature` (this is duplicated as `X-Attio-Signature` to support legacy middleware).
The `Attio-Signature` value is calculated using a SHA256 HMAC of the request body using your webhook secret as the secret. The webhook's secret is viewable inside the developer settings page and in the API response when creating the webhook.
To verify that webhooks came from us, you should also construct the signature on your side using the same algorithm, then verify that it matches the one in the request.
We encode the Attio-Signature as a hexadecimal string, and we only sign the request body, which we interpret as a UTF-8 string.
Here's an example in NodeJS for verifying the webhook signature:
```typescript TypeScript theme={"system"}
const webhookBody = request.body
const webhookSignature = request.headers["attio-signature"]
// Generate signature ourselves
const webhookSecret = "..."
const hmac = crypto.createHmac("sha256", webhookSecret)
hmac.update(webhookBody)
const expectedSignature = hmac.digest("hex")
// Compare signature
if (!crypto.timingSafeEqual(webhookSignature, expectedSignature)) {
throw new Error("Invalid signature")
}
```
## HTTPS
Webhooks must target URLs secured with HTTPS. This is because targeting URLs using HTTP reveals the confidential content of webhooks to the public internet. In addition to exposing your data to 'man in the middle' attacks, webhooks delivered via HTTP can be read at any point in the journey to your server, for example, by cloud providers or logging services. Ensuring your target URL is encrypted with HTTPS keeps your data secure.
## Idempotency
Webhooks guarantee at-least-once message delivery. Occasionally, due to network instability, Attio may send duplicate messages. To help deduplicate messages, Attio includes an `Idempotency-Key` header which will be different for each message, but the same between retries and redeliveries.
## Delivery attempts
If you're receiving many duplicate messages, it may mean that you're not acknowledging them properly. Attio will mark a delivery as successful if the response code is within the 200-299 range (for example `200` or `202`).If you answer with any other code, Attio will retry delivery of the message up to 10 times with an exponential back-off, which will happen over approximately 3 days in total; after which, the webhook will be marked as degraded and we'll send you an email.
Attio also enforces a 5 second timeout on requests to your target URL. Failure to respond within 5 seconds will be treated as a delivery failure.
## IP addresses
We recommend using the request signature to validate the request instead of relying on IP allowlisting. This will mean that your integration does not require maintenance if we add new IP addresses.
Attio delivers webhooks from a fixed set of IP addresses. In some environments with restrictive firewalls it might be necessary to allowlist these IPs, and from time to time we might need to add a new IP Address to our list. We'll endeavour to provide you with as much notice as possible before we change these.
```text Attio webhooks egress IP addresses theme={"system"}
34.76.181.69
35.189.212.204
35.190.200.137
104.199.25.43
35.205.134.181
34.77.170.251
104.155.38.31
35.240.20.227
35.205.218.25
34.77.63.171
35.195.180.236
104.199.20.44
34.78.73.25
34.77.104.7
35.205.250.54
34.78.179.95
35.189.210.201
34.77.106.144
104.155.115.39
34.78.11.169
35.241.187.180
35.240.124.129
35.241.222.75
35.195.62.68
```
## Delivery rate limiting
To avoid overwhelming your server with a large burst of requests, Attio smoothes out webhook delivery with a rate limiter. Rate limiting is implemented on a per-target URL basis. We restrict delivery per URL to a maximum of 25 requests per second. Please contact support if you would like this number adjusted for your workspace.
## Testing webhooks
The developer settings page provides the ability to deliver test payloads to your webhook's target URL.
When building an integration that uses webhooks, this lets you quickly test that your integration is functioning, without having to modify real data in your workspace.
To send a test payload:
1. Navigate to an integration the developer settings page and select an integration.
2. Open an existing webhook or create a new one inside your integration.
3. Ensure your webhook is subscribed to the events that you want to test.
4. Open the dropdown next to the event you want to send a test payload for and select "Send test event to target URL".
5. We'll make an HTTP request to your target URL.
We populate test payloads with real data from your workspace. For example, when testing the `note.created` event, we'll set the `note_id` property on the payload to correspond to a real note you have created. In cases where this is not possible, for example if you have no notes in your system, we'll fallback to randomly generated fake data.
Please note that filters are not taken into account when generating test data.
## Filtering
To reduce the amount of webhooks you receive and help avoid writing client-side filtering code, it's possible to define rules to further limit the events Attio will send. Filters work by taking the payload of the generated webhook event, and running it against a set of rules that you define.
For example, you might only care about updates to a particular attribute on a particular list, or about new notes on people but not companies.
Our API will validate that the filter syntax you have provided is valid. Filters are currently only editable and viewable over the API.
### Filter syntax
The filter syntax can be broken down into the following components.
#### Logical joins ($and, $or)
* An `$and` filter passes when all operations match the payload.
* An `$or` filter passes when at least one operation matches the payload.
#### Operations
* `field`: Specifies which property of the webhook payload to apply the filter condition on. It supports nested properties using dot notation, such as `"actor.type"` and `"actor.id"`.
* `operator`: The operator property defines the comparison operation to be used in the filter operation. The currently supported operators are: `"equals"` and `"not_equals"`
* `value`: The value property specifies the value to compare against the chosen payload field using the operator.
### Filter examples
**Subscribe to changes on the "Sales" list or the "Hiring" list**
```json5 JSON theme={"system"}
{
$or: [
{
field: "id.list_id",
operator: "equals",
value: "2a33abd4-dae7-49d0-b6ed-b09da0d8f00b", // <-- Sales List ID
},
{
field: "id.list_id",
operator: "equals",
value: "9d74e5c9-41eb-4d5c-b70b-d346ef15e13e", // <-- Hiring List ID
},
],
}
```
**Subscribe to changes to the value of the “Status” attribute of the Sales list**
```json5 JSON theme={"system"}
{
$and: [
{
field: "id.list_id",
operator: "equals",
value: "2a33abd4-dae7-49d0-b6ed-b09da0d8f00b", // <-- Sales List ID
},
{
field: "id.attribute_id",
operator: "equals",
value: "c65a3828-b5e9-46d9-afe6-c8319ae46412", // <-- Status Attribute ID
},
],
}
```
**Subscribe to changes made by workspace members**
```json JSON theme={"system"}
{
"$and": [
{
"field": "actor.type",
"operator": "equals",
"value": "workspace-member"
}
]
}
```
**Subscribe to all events**
```json JSON theme={"system"}
{"filter": null}
```
## Migrating from V1 webhooks
Webhooks were supported over the V1 API and have now been replaced by updated V2 Webhooks. Using V2 webhooks will allow you to use our new filtering system and receive payloads which are consistent with the rest of the V2 API (e.g. we now refer to “lists” instead of “collections”).
V1 Webhook endpoints and even types will eventually be removed. Therefore, we recommend upgrading to use V2 Webhooks at your soonest convenience.
### Event Types
The following V1 Webhook events should be considered deprecated:
```
entry.created
entry-attribute.updated
entry.deleted
```
These have been replaced by the following V2 event types which fire under exactly the same circumstances.
```
entry.created → list-entry.created
entry-attribute.updated → list-entry.updated
entry.deleted → list-entry.deleted
```
### Payloads
Your code will also need to take into account the changes in the payloads of the above events.
Below are examples of payloads with V1 events and V2.
#### entry.created
```json V1 theme={"system"}
{
"event_type": "entry.created",
"collection_id": "69815e80-949c-44c9-92be-242457a4be28",
"entry_id": "861c1071-54ba-4d3d-b642-f72f7bcc8c7e"
}
```
```json5 V2 theme={"system"}
{
event_type: "list-entry.created",
id: {
workspace_id: "928e88d9-de10-4e1c-9aef-36b07cb4260d", // New
list_id: "69815e80-949c-44c9-92be-242457a4be28", // Previously, collection_id
entry_id: "861c1071-54ba-4d3d-b642-f72f7bcc8c7e", // Previously, entry_id
},
parent_object_id: "7298c9b4-63ac-4b7e-8a74-4468d2e403a9", // New
parent_record_id: "6003a6aa-7122-45f1-b840-efe9231dfd06", // New
}
```
#### entry-attribute.updated
```json V1 theme={"system"}
{
"event_type": "entry-attribute.updated",
"collection_id": "69815e80-949c-44c9-92be-242457a4be28",
"entry_id": "861c1071-54ba-4d3d-b642-f72f7bcc8c7e",
"attribute_id": "18b7bb8c-fc41-4b70-be0b-0dea00b3ca23"
}
```
```json5 V2 theme={"system"}
{
event_type: "list-entry.updated",
id: {
workspace_id: "928e88d9-de10-4e1c-9aef-36b07cb4260d", // New
list_id: "69815e80-949c-44c9-92be-242457a4be28", // Previously, collection_id
entry_id: "861c1071-54ba-4d3d-b642-f72f7bcc8c7e", // Previously, entry_id
attribute_id: "18b7bb8c-fc41-4b70-be0b-0dea00b3ca23", // Previously, attribute_id
},
parent_object_id: "7298c9b4-63ac-4b7e-8a74-4468d2e403a9", // New
parent_record_id: "6003a6aa-7122-45f1-b840-efe9231dfd06", // New
}
```
#### entry.deleted
```json V1 theme={"system"}
{
"event_type": "entry.deleted",
"collection_id": "69815e80-949c-44c9-92be-242457a4be28",
"entry_id": "861c1071-54ba-4d3d-b642-f72f7bcc8c7e"
}
```
```json5 V2 theme={"system"}
{
event_type: "list-entry.deleted",
id: {
workspace_id: "928e88d9-de10-4e1c-9aef-36b07cb4260d", // New
list_id: "69815e80-949c-44c9-92be-242457a4be28", // Previously, collection_id
entry_id: "861c1071-54ba-4d3d-b642-f72f7bcc8c7e", // Previously, entry_id
},
parent_object_id: "7298c9b4-63ac-4b7e-8a74-4468d2e403a9", // New
parent_record_id: "6003a6aa-7122-45f1-b840-efe9231dfd06", // New
}
```
### Step-by-step migration guide
The following guide assumes you are implementing a zero downtime migration. You are, of course, welcome to migrate without such a constraint.
1. **Update your webhook server to handle V1 and V2 Events**. First, update the endpoints that handle the events we send you to deal with both V1 and V2 events.As there will be a brief overlap period where you receive both V1 and V2 events, you may wish to make your handler idempotent.
2. **Add new events**. Create new subscriptions to replace your old ones. For example, if you previously had a subscription on the `"entry.created"` event type, add a new one for the `"list-entry.updated"` event type. V1 subscriptions used a static `"collection_id"` property to limit subscriptions to a particular List (formerly “Collection”). This functionality can be replaced using our new filter functionality.
For example, below is an example of a V1 subscription and its V2 replacement. These two subscriptions will respond to exactly the same changes in the system.
```json V1 theme={"system"}
{
"event_type": "entry.created",
"collection_id": "738eefb5-d481-4aed-9735-ce918f279b74"
}
```
```json V2 theme={"system"}
{
"event_type": "list-entry.created",
"filter": {
"$and": [
{
"field": "id.list_id",
"operator": "equals",
"value": "d0a22439-5668-468a-b82a-f5988d9826f8"
}
]
}
}
```
Any automated subscription creation using V1 APIs should be moved over to use V2 APIs. You should also move delete and update endpoints over to the V2 endpoints.
3. **Remove old events**. Your server should now be receiving both V1 and V2 events and responding to each correctly. Now that this is the case, you can go ahead and remove the V1 events using either the developer settings UI or the V1 delete endpoint.
4. **Clean up any V1 handling code**. Now that you are no longer receiving V1 events, you are welcome to clean up any code on your servers that handled the V1 events.
# Overview
Source: https://docs.attio.com/rest-api/overview
Learn about the REST API
Attio provides a public REST API which exchanges JSON over HTTPS. In this section, you will find
guidance about working with the API.
To get up and running, read our guide on [how to authenticate requests](/rest-api/guides/authentication).
From there, feel free to explore other topics or jump straight to the [reference documentation](/rest-api/endpoint-reference).
* [Building an OAuth flow for your app](/rest-api/tutorials/connect-an-app-through-oauth)
* [Working within our rate limits](/rest-api/guides/rate-limiting)
* [Setting up and listening to webhooks](/rest-api/guides/webhooks)
* [Filtering and sorting record and entry API queries](/rest-api/guides/filtering-and-sorting)
* [Paginating API responses](/rest-api/guides/pagination)
If you're looking to move beyond just the REST API and understand the Attio developer platform as a
whole, you can find [general documentation for Attio developers here](/docs).
# Create a SCIM group
Source: https://docs.attio.com/rest-api/scim/create-group
POST https://api.attio.com/scim/v2/Groups
Creates a SCIM group (team) in the workspace.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Body
application/json
Must include `"urn:ietf:params:scim:schemas:core:2.0:Group"`.
The display name of the group. Must be non-empty after trimming whitespace. Must be unique within the workspace.
Members to add to the group on creation. Each `value` may be a workspace membership ID (active user) or a workspace invite ID (pending user).
Workspace membership ID or workspace invite ID.
URI reference for the member resource.
The type of the member resource.
Response
Always `["urn:ietf:params:scim:schemas:core:2.0:Group"]`.
The unique ID of the created group.
The display name of the created group.
The members in the group.
Workspace membership ID or workspace invite ID.
URI reference in the form `/scim/v2/Users/{id}`.
Always `"Group"`.
ISO 8601 timestamp of when the group was created.
ISO 8601 timestamp of when the group was last modified.
```json theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Engineering",
"members": [
{
"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"
}
]
}
```
```json 201 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "b5d3f8a1-c920-47e6-8b4d-3f17a9c82e50",
"displayName": "Engineering",
"members": [
{
"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"$ref": "/scim/v2/Users/3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"
}
],
"meta": {
"resourceType": "Group",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-01T00:00:00.000Z"
}
}
```
# Create a SCIM user
Source: https://docs.attio.com/rest-api/scim/create-user
POST https://api.attio.com/scim/v2/Users
Creates a SCIM user in the workspace. For verified domains the user is added as an active member immediately. For unverified domains an invite is sent and the invite ID is returned as the SCIM user ID.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Body
application/json
Must include `"urn:ietf:params:scim:schemas:core:2.0:User"`.
The user's email address.
The user's first name.
The user's last name.
Whether the user should be active. Setting to `false` creates a suspended member (only applies to verified domains).
The user's role in the workspace. Must be `"admin"` or `"member"`. Defaults to `"member"` if not specified.
Response
Always `["urn:ietf:params:scim:schemas:core:2.0:User"]`.
The workspace membership ID, or the workspace invite ID for unverified domains.
The user's email address.
The user's first name.
The user's last name.
The email address.
Whether this is the primary email address.
Always `"work"`.
Whether the user is active. Pending invites always return `true`.
The user's role: `"admin"` or `"member"`. Not present for suspended users.
Always `"User"`.
ISO 8601 timestamp of when the user was created.
ISO 8601 timestamp of when the user was last modified.
```json theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "john.doe@example.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"appRole": "member",
"active": true
}
```
```json 201 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"userName": "john.doe@example.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"emails": [
{
"value": "john.doe@example.com",
"primary": true,
"type": "work"
}
],
"appRole": "member",
"active": true,
"meta": {
"resourceType": "User",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-01T00:00:00.000Z"
}
}
```
# Delete a SCIM group
Source: https://docs.attio.com/rest-api/scim/delete-group
DELETE https://api.attio.com/scim/v2/Groups/{workspace_team_id}
Deletes a SCIM group from the workspace. Returns 204 No Content on success.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The ID of the group to delete.
Response
Returns `204 No Content` with an empty body on success.
```bash theme={"system"}
curl -X DELETE https://api.attio.com/scim/v2/Groups/b5d3f8a1-c920-47e6-8b4d-3f17a9c82e50 \
-H "Authorization: Bearer "
```
```json 204 theme={"system"}
{}
```
# Delete a SCIM user
Source: https://docs.attio.com/rest-api/scim/delete-user
DELETE https://api.attio.com/scim/v2/Users/{user_id}
Deletes a SCIM user from the workspace, suspending active members and cancelling pending invites. Returns 204 No Content on success.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The workspace membership ID or workspace invite ID of the user to delete.
Response
Returns `204 No Content` with an empty body on success.
```bash theme={"system"}
curl -X DELETE https://api.attio.com/scim/v2/Users/3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47 \
-H "Authorization: Bearer "
```
```json 204 theme={"system"}
{}
```
# Get a SCIM group
Source: https://docs.attio.com/rest-api/scim/get-group-by-id
GET https://api.attio.com/scim/v2/Groups/{workspace_team_id}
Gets a SCIM group by ID.
Required scopes: `user_management:read`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The ID of the group to retrieve.
Response
Always `["urn:ietf:params:scim:schemas:core:2.0:Group"]`.
The unique ID of the group.
The display name of the group.
The members of the group.
Workspace membership ID or workspace invite ID.
URI reference in the form `/scim/v2/Users/{id}`.
Always `"Group"`.
ISO 8601 timestamp of when the group was created.
ISO 8601 timestamp of when the group was last modified.
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "b5d3f8a1-c920-47e6-8b4d-3f17a9c82e50",
"displayName": "Engineering",
"members": [
{
"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"$ref": "/scim/v2/Users/3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"
}
],
"meta": {
"resourceType": "Group",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-01T00:00:00.000Z"
}
}
```
# Get a SCIM user
Source: https://docs.attio.com/rest-api/scim/get-user-by-id
GET https://api.attio.com/scim/v2/Users/{user_id}
Gets a SCIM user by ID. Returns both active members and pending invites.
Required scopes: `user_management:read`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The workspace membership ID or workspace invite ID of the user to retrieve.
Response
Always `["urn:ietf:params:scim:schemas:core:2.0:User"]`.
The workspace membership ID or workspace invite ID.
The user's email address.
The user's first name.
The user's last name.
The email address.
Whether this is the primary email address.
Always `"work"`.
Whether the user is active. Suspended members return `false`. Pending invites return `true`.
The user's role: `"admin"` or `"member"`. Not present for suspended users.
Always `"User"`.
ISO 8601 timestamp of when the user was created.
ISO 8601 timestamp of when the user was last modified.
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"userName": "john.doe@example.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"emails": [
{
"value": "john.doe@example.com",
"primary": true,
"type": "work"
}
],
"active": true,
"appRole": "member",
"meta": {
"resourceType": "User",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-01T00:00:00.000Z"
}
}
```
# List SCIM groups
Source: https://docs.attio.com/rest-api/scim/list-groups
GET https://api.attio.com/scim/v2/Groups
Lists all SCIM groups in the workspace, with optional filtering and pagination.
Required scopes: `user_management:read`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Query
A SCIM filter expression. Supported attributes: `displayName`. Example: `displayName eq "Engineering"`.
1-based index of the first result to return.
Maximum number of results to return. Capped at 100.
Comma-separated list of attributes to exclude from results. Supports `"members"` to omit the members array for efficiency.
Response
Always `["urn:ietf:params:scim:api:messages:2.0:ListResponse"]`.
Total number of groups matching the filter.
The 1-based index of the first result returned.
The number of results returned in this page.
The list of groups. Each group has the same shape as the [Get a SCIM group](/rest-api/scim/get-group-by-id) response. When `excludedAttributes=members` is specified, the `members` field is omitted entirely from each group object.
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 2,
"startIndex": 1,
"itemsPerPage": 2,
"Resources": [
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "b5d3f8a1-c920-47e6-8b4d-3f17a9c82e50",
"displayName": "Engineering",
"members": [
{
"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"$ref": "/scim/v2/Users/3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"
}
],
"meta": {
"resourceType": "Group",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-01T00:00:00.000Z"
}
}
]
}
```
# List SCIM schemas
Source: https://docs.attio.com/rest-api/scim/list-schemas
GET https://api.attio.com/scim/v2/Schemas
Returns the SCIM 2.0 resource schemas supported by Attio (User and Group), allowing SCIM clients to discover supported attributes.
Required scopes: `user_management:read`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Response
Always `["urn:ietf:params:scim:api:messages:2.0:ListResponse"]`.
Total number of schemas returned.
Always `1`.
Number of schemas in this response.
The supported schemas. Each entry describes a resource type (`User` or `Group`) and its attributes.
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 2,
"startIndex": 1,
"itemsPerPage": 2,
"Resources": [
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
"id": "urn:ietf:params:scim:schemas:core:2.0:User",
"name": "User",
"description": "Attio workspace member",
"attributes": [
{
"name": "userName",
"type": "string",
"multiValued": false,
"description": "Primary email of the user",
"required": true,
"mutability": "readWrite",
"returned": "default",
"uniqueness": "server"
},
{
"name": "name",
"type": "complex",
"multiValued": false,
"description": "The user's name",
"required": false,
"mutability": "readWrite",
"returned": "default",
"subAttributes": [
{
"name": "givenName",
"type": "string",
"multiValued": false,
"description": "The user's first name",
"required": false,
"mutability": "readWrite",
"returned": "default"
},
{
"name": "familyName",
"type": "string",
"multiValued": false,
"description": "The user's last name",
"required": false,
"mutability": "readWrite",
"returned": "default"
}
]
},
{
"name": "emails",
"type": "complex",
"multiValued": true,
"description": "Email addresses for the user",
"required": false,
"mutability": "readOnly",
"returned": "default",
"subAttributes": [
{
"name": "value",
"type": "string",
"multiValued": false,
"description": "The email address",
"required": false,
"mutability": "readOnly",
"returned": "default"
},
{
"name": "primary",
"type": "boolean",
"multiValued": false,
"description": "Whether this is the primary email address",
"required": false,
"mutability": "readOnly",
"returned": "default"
},
{
"name": "type",
"type": "string",
"multiValued": false,
"description": "The type of email address",
"required": false,
"mutability": "readOnly",
"returned": "default"
}
]
},
{
"name": "active",
"type": "boolean",
"multiValued": false,
"description": "A Boolean value indicating the User's access to a workspace",
"required": false,
"mutability": "readWrite",
"returned": "default",
"uniqueness": "none"
},
{
"name": "appRole",
"type": "string",
"multiValued": false,
"description": "The user's access level within the workspace. Can be set to 'admin' or 'member'.",
"required": false,
"canonicalValues": ["admin", "member"],
"mutability": "readWrite",
"returned": "default",
"uniqueness": "none"
}
],
"meta": {
"resourceType": "Schema",
"location": "/scim/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:User"
}
},
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Schema"],
"id": "urn:ietf:params:scim:schemas:core:2.0:Group",
"name": "Group",
"description": "Attio's team",
"attributes": [
{
"name": "displayName",
"type": "string",
"multiValued": false,
"description": "A human-readable name for the team",
"required": true,
"mutability": "readWrite",
"returned": "default",
"uniqueness": "none"
},
{
"name": "members",
"type": "complex",
"multiValued": true,
"description": "Members of the group",
"required": false,
"mutability": "readWrite",
"returned": "default",
"subAttributes": [
{
"name": "value",
"type": "string",
"multiValued": false,
"description": "Workspace membership ID or workspace invite ID",
"required": false,
"mutability": "readWrite",
"returned": "default"
},
{
"name": "$ref",
"type": "reference",
"multiValued": false,
"description": "URI reference for the member",
"required": false,
"mutability": "readWrite",
"returned": "default"
},
{
"name": "type",
"type": "string",
"multiValued": false,
"description": "The type of the member resource",
"required": false,
"mutability": "readWrite",
"returned": "default"
}
]
}
],
"meta": {
"resourceType": "Schema",
"location": "/scim/v2/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group"
}
}
]
}
```
# List SCIM users
Source: https://docs.attio.com/rest-api/scim/list-users
GET https://api.attio.com/scim/v2/Users
Lists all SCIM users in the workspace, including active members and pending invites, with optional filtering and pagination.
Required scopes: `user_management:read`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Query
A SCIM filter expression. Supported attributes: `userName`, `emails.value`, `id`, `name.givenName`, `name.familyName`, `active`. Supported operators: `eq`, `ne`, `co`, `sw`, `ew`. Example: `userName eq "john@example.com"`.
1-based index of the first result to return.
Maximum number of results to return. Capped at 100.
Response
Always `["urn:ietf:params:scim:api:messages:2.0:ListResponse"]`.
Total number of users matching the filter.
The 1-based index of the first result returned.
The number of results returned in this page.
The list of users. Each user has the same shape as the [Get a SCIM user](/rest-api/scim/get-user-by-id) response.
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1,
"startIndex": 1,
"itemsPerPage": 1,
"Resources": [
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"userName": "john.doe@example.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"emails": [
{
"value": "john.doe@example.com",
"primary": true,
"type": "work"
}
],
"active": true,
"appRole": "member",
"meta": {
"resourceType": "User",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-01T00:00:00.000Z"
}
}
]
}
```
# Patch a SCIM group
Source: https://docs.attio.com/rest-api/scim/patch-group
PATCH https://api.attio.com/scim/v2/Groups/{workspace_team_id}
Applies partial updates to a SCIM group using SCIM PatchOp operations. Supports updating the display name and adding, removing, or replacing members.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The ID of the group to update.
Body
application/json
Must include `"urn:ietf:params:scim:api:messages:2.0:PatchOp"`.
List of patch operations to apply sequentially.
The operation type: `"add"`, `"remove"`, or `"replace"`.
The attribute to target, e.g. `"displayName"` or `"members"`. Also supports the SCIM filter path syntax `members[value eq "{id}"]` with `op: "remove"` to remove a specific member. May be omitted when `value` contains a keyed object.
The value for the operation. For member additions and replacements, must be an array of objects with a `value` property containing a workspace membership ID or workspace invite ID.
Response
The response has the same shape as [Update a SCIM group](/rest-api/scim/update-group).
```json Add members theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "add",
"path": "members",
"value": [{"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"}]
}
]
}
```
```json Rename group theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "displayName",
"value": "Platform Engineering"
}
]
}
```
```json Remove all members theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "remove",
"path": "members"
}
]
}
```
```json Remove specific member theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "remove",
"path": "members[value eq \"3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47\"]"
}
]
}
```
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "b5d3f8a1-c920-47e6-8b4d-3f17a9c82e50",
"displayName": "Platform Engineering",
"members": [
{
"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"$ref": "/scim/v2/Users/3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"
}
],
"meta": {
"resourceType": "Group",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-02T00:00:00.000Z"
}
}
```
# Patch a SCIM user
Source: https://docs.attio.com/rest-api/scim/patch-user
PATCH https://api.attio.com/scim/v2/Users/{user_id}
Applies partial updates to a SCIM user using SCIM PatchOp operations. Returns 200 with the updated user. Returns 204 when setting `active` to `false` on a pending invite.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The workspace membership ID or workspace invite ID of the user to update.
Body
application/json
Must include `"urn:ietf:params:scim:api:messages:2.0:PatchOp"`.
List of patch operations to apply sequentially.
The operation type: `"add"`, `"replace"`, or `"remove"`. `"remove"` is only supported for `"name.givenName"` and `"name.familyName"`.
The attribute to target. Supported paths: `"name.givenName"`, `"name.familyName"`, `"active"`, `"appRole"`. May be omitted when `value` contains a keyed object.
The new value for the operation.
Response
Returns `200` with the updated user object (same shape as [Update a SCIM user](/rest-api/scim/update-user)). Returns `204 No Content` when setting `active` to `false` on a pending invite; deprovisioning an active member returns `200`.
```json Update name theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "name.givenName",
"value": "Jane"
}
]
}
```
```json Deprovision user theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "active",
"value": false
}
]
}
```
```json Promote to admin theme={"system"}
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "replace",
"path": "appRole",
"value": "admin"
}
]
}
```
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"userName": "john.doe@example.com",
"name": {
"givenName": "Jane",
"familyName": "Doe"
},
"emails": [
{
"value": "john.doe@example.com",
"primary": true,
"type": "work"
}
],
"appRole": "member",
"active": true,
"meta": {
"resourceType": "User",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-02T00:00:00.000Z"
}
}
```
```json 204 theme={"system"}
{}
```
# Update a SCIM group
Source: https://docs.attio.com/rest-api/scim/update-group
PUT https://api.attio.com/scim/v2/Groups/{workspace_team_id}
Fully replaces a SCIM group's display name and member list.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The ID of the group to update.
Body
application/json
Must include `"urn:ietf:params:scim:schemas:core:2.0:Group"`.
The new display name for the group. Must be non-empty after trimming whitespace. Must be unique within the workspace.
The complete new member list. This fully replaces the existing membership — members not included are removed.
Workspace membership ID or workspace invite ID of the member.
URI reference for the member resource.
The type of the member resource.
Response
Always `["urn:ietf:params:scim:schemas:core:2.0:Group"]`.
The unique ID of the group.
The updated display name of the group.
The updated member list.
Workspace membership ID or workspace invite ID.
URI reference in the form `/scim/v2/Users/{id}`.
Always `"Group"`.
ISO 8601 timestamp of when the group was created.
ISO 8601 timestamp of when the group was last modified.
```json theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Platform Engineering",
"members": [{"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"}]
}
```
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "b5d3f8a1-c920-47e6-8b4d-3f17a9c82e50",
"displayName": "Platform Engineering",
"members": [
{
"value": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"$ref": "/scim/v2/Users/3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47"
}
],
"meta": {
"resourceType": "Group",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-02T00:00:00.000Z"
}
}
```
# Update a SCIM user
Source: https://docs.attio.com/rest-api/scim/update-user
PUT https://api.attio.com/scim/v2/Users/{user_id}
Fully replaces a SCIM user's attributes. Returns 200 with the updated user. Returns 204 when setting `active` to `false` on a pending invite.
Required scopes: `user_management:read-write`.
Authorizations
This API uses OAuth 2.0 with the authorization code grant flow.
Path
The workspace membership ID or workspace invite ID of the user to update.
Body
application/json
Must include `"urn:ietf:params:scim:schemas:core:2.0:User"`.
The user's email address. Must be a valid email.
The user's first name.
The user's last name.
Whether the user should be active. Setting to `false` suspends the member and revokes their active sessions.
The user's role: `"admin"` or `"member"`.
Response
Returns `200` with the updated user. Returns `204 No Content` when setting `active` to `false` on a pending invite; deprovisioning an active member returns `200`.
Always `["urn:ietf:params:scim:schemas:core:2.0:User"]`.
The workspace membership ID or workspace invite ID.
The user's email address.
The user's first name.
The user's last name.
The email address.
Whether this is the primary email address.
Always `"work"`.
Whether the user is active.
The user's role: `"admin"` or `"member"`. Not present for suspended users.
Always `"User"`.
ISO 8601 timestamp of when the user was created.
ISO 8601 timestamp of when the user was last modified.
```json theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "john.doe@example.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"appRole": "admin",
"active": true
}
```
```json 200 theme={"system"}
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"id": "3a8f5b2c-9e14-4d7a-b832-1c6f85d90e47",
"userName": "john.doe@example.com",
"name": {
"givenName": "John",
"familyName": "Doe"
},
"emails": [
{
"value": "john.doe@example.com",
"primary": true,
"type": "work"
}
],
"appRole": "admin",
"active": true,
"meta": {
"resourceType": "User",
"created": "2024-01-01T00:00:00.000Z",
"lastModified": "2024-01-02T00:00:00.000Z"
}
}
```
```json 204 theme={"system"}
{}
```
# Connect an app to Attio through OAuth
Source: https://docs.attio.com/rest-api/tutorials/connect-an-app-through-oauth
Learn how to create an OAuth 2.0 app to access Attio's REST API
If you're building an app that needs to make requests to Attio's REST API on behalf of many users,
you should use OAuth 2.0 to authenticate your app.
This tutorial will walk you through the process of modifying an example Node.js app to connect to
Attio through OAuth. We'll start with a boilerplate app and modify it step-by-step to support
generating an OAuth token for each user and then use that token to display a list of tasks they have
in Attio.
If you would prefer to jump straight ahead to reference information for each endpoint in our OAuth
flow, you can find documentation [here](/docs/oauth/authorize).
Our next job is to enable OAuth 2.0 for the app. Head to the OAuth tab in your app's settings and
enable OAuth 2.0 via the toggle at the top of the page.
Next, configure the redirect URIs for your app. For our tutorial, we'll use the following URL:
```
http://localhost:3050/integrations/attio/callback
```
Of course, for a real app, you'd also include a publicly available URL such as
`https://my-app.com/integrations/attio/callback`.
Lastly, we need to configure the app's scopes. Heads to the scopes tab to enable these. For our
demonstration app, we'll set tasks, user management, object configuration and records to "read" so
we can fetch a list of tasks and which users they are assigned to.
Make a new directory and setup your new Node.js project inside it:
```bash theme={"system"}
mkdir my-app
cd my-app
npm init -y
npm install express dotenv sqlite3 bcrypt express-session
```
Create a new file called `server.js` and add the following code:
```js theme={"system"}
require("dotenv").config()
const express = require("express")
const session = require("express-session")
const sqlite3 = require("sqlite3").verbose()
const bcrypt = require("bcrypt")
const app = express()
const PORT = 3050
// NOTE: The following code is heavily simplified for educational purposes and should not be copied
// for production use without careful consideration of security implications.
// 1) Setup a database
// Use ":memory:" for a temporary database in RAM. A real app should use a persistent database.
const db = new sqlite3.Database(":memory:")
// Seed a "users" table if it doesn't exist
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
passwordHash TEXT NOT NULL
)
`)
// 2) Middleware
// Enable parsing of form data
app.use(express.urlencoded({extended: false}))
// Setup session middleware
app.use(
session({
secret: "mysecret", // Replace with a strong secret in production
resave: false,
saveUninitialized: false,
}),
)
// 3) Page routes
app.get("/", (req, res) => {
if (req.session.userId) {
return res.send(`
`)
}
return res.send("An error occurred. Please try again.")
}
// If insert succeeds, log the user in
req.session.userId = this.lastID // ID of the newly created user
req.session.username = username
// Show the home page
return res.redirect("/")
},
)
} catch (error) {
console.error("Error hashing password:", error)
res.send("An error occurred. Please try again.")
}
})
app.get("/logout", (req, res) => {
req.session.destroy(() => {
res.redirect("/")
})
})
// 4) Start the server
app.listen(PORT, () => {
console.log(`App running at http://localhost:${PORT}`)
})
```
You should now be able to run your app from the command line and visit it in your browser at
`http://localhost:3050`:
```bash theme={"system"}
node server.js
```
Run through the signup flow to ensure everything works as expected.
To add support for OAuth, we need to ensure that our Node.js code has access to the OAuth client ID
and client secret.
Create a new file called `.env` and add your app's client ID and client secret. You can find these
in the OAuth tab in your app's settings.
```
ATTIO_CLIENT_ID=your-client-id
ATTIO_CLIENT_SECRET=your-client-secret
```
When we complete the OAuth flow, we'll need a place to store the OAuth access token for each user.
Modify the code that creates the `users` table as follows:
```js theme={"system"}
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
passwordHash TEXT NOT NULL,
attio_access_token TEXT
)
`)
```
In a real app, you should encrypt these values before storing them. Access tokens are highly
sensitive data and should be stored securely.
Next, we need to implement the OAuth flow itself. An OAuth flow consists of the following steps:
1. Redirect to Attio's OAuth authorization page when prompted by the user
2. Handle the redirect back from Attio
3. Exchange the authorization code for an access token
4. Persist the access token
5. Make API requests using the access token
```mermaid theme={"system"}
sequenceDiagram
participant User
participant Your App
participant Attio
User->>Your App: Click "Connect to Attio"
Your App->>Attio: Redirect to OAuth authorization page
Attio->>Your App: Redirect to app with authorization code
Your App->>Attio: Exchange authorization code for access token securely
Attio->>Your App: Return access token
Your App->>Your App: Store access token
Your App->>Attio: Make API request with access token
```
We'll start by adding a new route to our app that redirects the user to the OAuth authorization
page.
```js theme={"system"}
const crypto = require("crypto")
app.get("/integrations/attio/connect", (req, res) => {
if (!req.session.userId) {
return res.redirect("/signup") // Must be logged in
}
// Generate a secure random state parameter and store it in the user's session
const state = crypto.randomBytes(16).toString("hex")
req.session.oauthState = state
const authUrl = `https://app.attio.com/authorize?response_type=code&client_id=${process.env.ATTIO_CLIENT_ID}&redirect_uri=http://localhost:3050/integrations/attio/callback&state=${state}`
res.redirect(authUrl)
})
```
A second route will handle the redirect back from Attio.
```js theme={"system"}
const ATTIO_TOKEN_URL = "https://app.attio.com/oauth/token"
app.get("/integrations/attio/callback", async (req, res) => {
if (!req.session.userId) {
return res.redirect("/signup")
}
const {code, state} = req.query
// Verify the state parameter
if (!state || state !== req.session.oauthState) {
return res.status(403).send("Invalid state parameter. Possible CSRF attack detected.")
}
if (!code) {
return res.status(400).send("Missing authorization code from Attio")
}
try {
// Exchange authorization code for an access token using fetch
const tokenResponse = await fetch(ATTIO_TOKEN_URL, {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: new URLSearchParams({
grant_type: "authorization_code",
code: code,
redirect_uri: "http://localhost:3050/integrations/attio/callback",
client_id: process.env.ATTIO_CLIENT_ID,
client_secret: process.env.ATTIO_CLIENT_SECRET,
}),
})
if (!tokenResponse.ok) {
console.error("Attio token exchange failed with status:", tokenResponse.status)
return res.status(500).send("Error exchanging code for token")
}
const tokenData = await tokenResponse.json()
const attioAccessToken = tokenData.access_token
// Update the user's record with the access token
db.run(
`UPDATE users SET attio_access_token = ? WHERE id = ?`,
[attioAccessToken, req.session.userId],
(err) => {
if (err) {
console.error("Failed to store Attio token in DB:", err)
return res.status(500).send("Database error storing Attio token")
}
// Redirect back to home
res.redirect("/")
},
)
} catch (err) {
console.error("Error fetching token from Attio:", err)
res.status(500).send("Internal error")
}
})
```
Last, we need to ensure the user can navigate to the start of this flow. Let's add a button to the
home page that redirects to the `/integrations/attio/connect` route.
```js theme={"system"}
app.get("/", async (req, res) => {
if (req.session.userId) {
await db.get("SELECT * FROM users WHERE id = ?", [req.session.userId], async (err, user) => {
if (err) {
console.error("Error fetching users:", err)
return res.status(500).send("Error fetching users")
}
const hasAttioConnection = user !== null && user.attio_access_token !== null
return res.send(`
`
}
`)
})
} else {
// ...
}
})
```
Please note, the example above stores a raw access token in the database. The access tokens that
we grant to your app are highly sensitive data and should be stored securely. Please ensure any
production apps you build encrypt the token before storing it.
Now we have a token, all that remains is to make a request to the Attio API and render the results.
To make a request to the Attio API, we need to call the right endpoint and pass in our new oauth
token in the `Authorization` header like so.
```js theme={"system"}
app.get("/", async (req, res) => {
if (req.session.userId) {
await db.get("SELECT * FROM users WHERE id = ?", [req.session.userId], async (err, user) => {
if (err) {
console.error("Error fetching users:", err)
return res.status(500).send("Error fetching users")
}
const hasAttioConnection = user !== null && user.attio_access_token !== null
if (hasAttioConnection) {
const fetchResult = await fetch(`https://api.attio.com/v2/tasks?limit=10`, {
headers: {
Authorization: `Bearer ${user.attio_access_token}`, // Pass in the token here
},
})
const data = await fetchResult.json()
const taskItems = data.data.map((task) => {
return `
`)
}
})
```
All that remains is to spin up your app and test it out!
Run your app from the command line and visit it in your browser at `http://localhost:3050`.
```bash theme={"system"}
node server.js
```
# Call recording created
Source: https://docs.attio.com/rest-api/webhook-reference/call-recording-events/call-recordingcreated
https://api.attio.com/openapi/webhooks webhook call-recording.created
This event fires after a call recording has finished and its media upload is complete.
# Comment created
Source: https://docs.attio.com/rest-api/webhook-reference/comment-events/commentcreated
https://api.attio.com/openapi/webhooks webhook comment.created
This event is fired whenever a comment is created.
# Comment deleted
Source: https://docs.attio.com/rest-api/webhook-reference/comment-events/commentdeleted
https://api.attio.com/openapi/webhooks webhook comment.deleted
This event is fired whenever a comment is deleted.
# Comment resolved
Source: https://docs.attio.com/rest-api/webhook-reference/comment-events/commentresolved
https://api.attio.com/openapi/webhooks webhook comment.resolved
This event is fired whenever a comment is resolved.
# Comment unresolved
Source: https://docs.attio.com/rest-api/webhook-reference/comment-events/commentunresolved
https://api.attio.com/openapi/webhooks webhook comment.unresolved
This event is fired whenever a comment is un-resolved.
# List attribute created
Source: https://docs.attio.com/rest-api/webhook-reference/list-attribute-events/list-attributecreated
https://api.attio.com/openapi/webhooks webhook list-attribute.created
This event is fired whenever a list attribute is created (e.g. adding an "Owner" attribute).
# List attribute updated
Source: https://docs.attio.com/rest-api/webhook-reference/list-attribute-events/list-attributeupdated
https://api.attio.com/openapi/webhooks webhook list-attribute.updated
This event is fired whenever a list attribute is updated (e.g. when changing the name of the "Owner" attribute to "Proprietor").
# List entry created
Source: https://docs.attio.com/rest-api/webhook-reference/list-entry-events/list-entrycreated
https://api.attio.com/openapi/webhooks webhook list-entry.created
This event is fired whenever a list entry is created (i.e. when a record is added to a list).
# List entry deleted
Source: https://docs.attio.com/rest-api/webhook-reference/list-entry-events/list-entrydeleted
https://api.attio.com/openapi/webhooks webhook list-entry.deleted
This event is fired whenever a list entry is deleted (i.e. when a record is removed from a list).
# List entry updated
Source: https://docs.attio.com/rest-api/webhook-reference/list-entry-events/list-entryupdated
https://api.attio.com/openapi/webhooks webhook list-entry.updated
This event is fired whenever an existing list entry is updated (i.e. when a list attribute is changed for a specific list entry, e.g. when setting "Owner").
# List created
Source: https://docs.attio.com/rest-api/webhook-reference/list-events/listcreated
https://api.attio.com/openapi/webhooks webhook list.created
This event is fired whenever a list is created.
# List deleted
Source: https://docs.attio.com/rest-api/webhook-reference/list-events/listdeleted
https://api.attio.com/openapi/webhooks webhook list.deleted
This event is fired whenever a list is deleted.
# List updated
Source: https://docs.attio.com/rest-api/webhook-reference/list-events/listupdated
https://api.attio.com/openapi/webhooks webhook list.updated
This event is fired whenever a list is updated (e.g. when changing the name or icon of the list).
# Note content updated
Source: https://docs.attio.com/rest-api/webhook-reference/note-content-events/note-contentupdated
https://api.attio.com/openapi/webhooks webhook note-content.updated
This event is fired whenever the content (body) of a note is updated. The `parent_object_id` refers to the object that the note references (e.g. the person object), and the `parent_record_id` refers to the record that the note references.
# Note created
Source: https://docs.attio.com/rest-api/webhook-reference/note-events/notecreated
https://api.attio.com/openapi/webhooks webhook note.created
This event is fired whenever a note is created. The `parent_object_id` refers to the object that the note references (e.g. the person object), and the `parent_record_id` refers to the record that the note references.
# Note deleted
Source: https://docs.attio.com/rest-api/webhook-reference/note-events/notedeleted
https://api.attio.com/openapi/webhooks webhook note.deleted
This event is fired whenever a note is deleted.
# Note updated
Source: https://docs.attio.com/rest-api/webhook-reference/note-events/noteupdated
https://api.attio.com/openapi/webhooks webhook note.updated
This event is fired whenever the title of a note is modified. Body updates do not currently trigger webhooks.
# Object attribute created
Source: https://docs.attio.com/rest-api/webhook-reference/object-attribute-events/object-attributecreated
https://api.attio.com/openapi/webhooks webhook object-attribute.created
This event is fired whenever an object attribute is created (e.g. when defining a new attribute "Rating" on the company object).
# Object attribute updated
Source: https://docs.attio.com/rest-api/webhook-reference/object-attribute-events/object-attributeupdated
https://api.attio.com/openapi/webhooks webhook object-attribute.updated
This event is fired whenever an object attribute is updated (e.g. when renaming the "Rating" attribute to "Score" on the company object).
# Record created
Source: https://docs.attio.com/rest-api/webhook-reference/record-events/recordcreated
https://api.attio.com/openapi/webhooks webhook record.created
This event is fired whenever a record is created.
# Record deleted
Source: https://docs.attio.com/rest-api/webhook-reference/record-events/recorddeleted
https://api.attio.com/openapi/webhooks webhook record.deleted
This event is fired whenever a record is deleted.
# Record merged
Source: https://docs.attio.com/rest-api/webhook-reference/record-events/recordmerged
https://api.attio.com/openapi/webhooks webhook record.merged
This event is fired whenever two records are merged together. Merging copies properties from the "duplicate" record into the original record, so that the original record has the properties of both, and the duplicate record is deleted.
# Record updated
Source: https://docs.attio.com/rest-api/webhook-reference/record-events/recordupdated
https://api.attio.com/openapi/webhooks webhook record.updated
This event is fired whenever an attribute on a record is updated (e.g. changing the "name" field on a record).
# Task created
Source: https://docs.attio.com/rest-api/webhook-reference/task-events/taskcreated
https://api.attio.com/openapi/webhooks webhook task.created
This event is fired whenever a task is created.
# Task deleted
Source: https://docs.attio.com/rest-api/webhook-reference/task-events/taskdeleted
https://api.attio.com/openapi/webhooks webhook task.deleted
This event is fired whenever a task is deleted.
# Task updated
Source: https://docs.attio.com/rest-api/webhook-reference/task-events/taskupdated
https://api.attio.com/openapi/webhooks webhook task.updated
This event is fired whenever a task is updated (e.g. the assignees or deadline are changed).
# Workspace member created
Source: https://docs.attio.com/rest-api/webhook-reference/workspace-member-events/workspace-membercreated
https://api.attio.com/openapi/webhooks webhook workspace-member.created
This event is fired whenever a workspace member is added to the workspace.
#
Source: https://docs.attio.com/sdk/components/avatar
An avatar component for displaying profile pictures or organization logos.
```js theme={"system"}
import {Avatar} from "attio/client"
```
## Example
```jsx theme={"system"}
```
## Props
The name of the person or organization represented by the avatar. This is used for accessibility
and may also be used as a fallback display when no image is provided.
The source URL of the avatar image. If not provided or if the image fails to load, the avatar will
typically display initials or a fallback representation based on the name.
The shape of the avatar.
Defaults to `'round'`.
The visual variant of the avatar, affecting its styling and prominence.
Defaults to `'bold'`.
The size of the avatar.
* `'small'` - `16px`
* `'medium'` - `24px`
* `'large'` - `32px`
Defaults to `'medium'`.
#
Source: https://docs.attio.com/sdk/components/badge
A colored badge of information.
```js theme={"system"}
import {Badge} from "attio/client"
```
## Example
```tsx TypeScript theme={"system"}
import {DescriptionList, Badge} from "attio/client"
export function InformationDialog() {
return (
FolkloreCornwallParanormal
)
}
```
## Props
The text in the badge.
Only text is allowed.
The color variant for the badge
#
Source: https://docs.attio.com/sdk/components/banner
A component displaying important information to the user.
```js theme={"system"}
import {Banner} from "attio/client"
```
```tsx TypeScript theme={"system"}
import React from "react"
import {Banner} from "attio/client"
export function HelloWorldDialog() {
return (
<>
{}},
{label: "Primary action", variant: "primary", onTrigger: () => {}},
]}
>
Some important information with two actions
>
)
}
```
## Props
The text of the banner.
Only text is allowed.
The variant of the banner.
Optional actions to include.
#
Source: https://docs.attio.com/sdk/components/button
A button component.
```js theme={"system"}
import {Button} from "attio/client"
```
Any buttons placed inside a dialog will be rendered at the bottom of the dialog.
## Example
```jsx theme={"system"}