Skip to main content
This guide shows how to create activities, add attributes to them, log activity records and link those records to your CRM data. If you haven’t yet, start with the activities concept page to learn how activities, activity records and schemas relate.

Required scopes

The activity APIs use two scopes:
  • activity_configuration for reading and writing activities themselves and their attributes.
  • activity_record for reading and writing activity records.
Activity records link to object records, so the activity record endpoints additionally require activity_configuration:read, object_configuration:read and record_permission:read. Creating referenced records as part of a write also needs record_permission:read-write.

Creating an activity

Create an activity with POST /v2/activities. Besides a slug and its display names, you must pick the schema the activity extends: activities, interactions, calls or emails. When creating custom activities, it’s good practice to prefix the slug with an app-specific prefix like {app_name}_site_visits to prevent conflicts.
The response contains the new activity, including the schemas it directly extends:
The choice of schema is permanent: PATCH /v2/activities/{activity} can update the slug and nouns, but rejects extends with a 400. You can list all activities with GET /v2/activities and fetch a single one with GET /v2/activities/{activity}. Wherever {activity} appears in a path, you can pass either the activity’s UUID or its api_slug.

Deleting an activity

DELETE /v2/activities/{activity} deletes an activity and all of its records. It needs both activity_configuration:read-write and activity_record:read-write, and workspace members must be an admin. Archived activities can be deleted too.
Deleting an activity removes every record logged against it. This cannot be undone.
Only custom activities can be changed. PATCH and DELETE on a system activity such as phone_calls fail with a 400.

Adding attributes

The attribute endpoints accept activities as a target, alongside objects and lists. For example, to add a “Location” attribute to the site visits activity:
Attributes inherited from the schema (e.g. timestamp or direction) are listed alongside your custom attributes and look identical in the API. However, they are managed by the schema: attempting to update them or to add select options to them fails with a 400 error explaining that the attribute is inherited.

Creating activity records

Log an event with POST /v2/activities/{activity}/records. The body has the same shape as for object records: a values map keyed by attribute slug. The example below logs an outbound renewal call on an activity extending the calls schema. It links the Acme company by domain and the participant by email address, without knowing any record IDs:
A few things to note:
  • timestamp (when the event happened) is required on every activity record.
  • created_at and created_by are set by the system and cannot be written.
  • The response contains the record’s composite ID (workspace_id, activity_id, record_id), its created_at and all of its values. Unlike object records, activity records have no web_url.

Linking records by attribute value

Record reference values such as linked_records and participants can be written in three formats, just like on object records:
  • ID format: {"target_object": "people", "target_record_id": "<uuid>"}
  • Matching format: identify the target record by one of its attribute values, e.g. {"target_object": "people", "email_addresses": [{"email_address": "alice@acme.com"}]}
  • Target record reference format: {"target_object": "people", "target_record_reference": {…}}, which also says what to do when nothing matches or several records match
The last two are especially useful for activities: integrations that ingest calls or meetings usually know a phone number, email address or domain, not an Attio record ID. The matching format works with domain, email address, phone number, number and text attributes. On activity record endpoints it behaves as follows:
  • Exactly one record must match. If a matching value resolves to several records, e.g. a phone number shared by two people, the request fails with a 400 instead of picking one arbitrarily. To link all of them, up to 50 records per value, use the target record reference format with if_multiple_matches: "match_all".
  • Duplicates are rejected. If the same record is referenced more than once in one attribute, for example by ID and by a matching value, the request fails with a 400.
  • Records are not created. If no record matches, the request fails with a 400 naming the value that could not be found.
To create the missing record instead of failing, use the target record reference format. The example below logs a call and creates the Acme company record if no company has that domain:
See linking referenced records for the full behaviour, the limits and the errors. Note that every referenced record created this way counts against your write rate limit.

Querying activity records

Query records of an activity with POST /v2/activities/{activity}/records/query. The body supports the same filter and sorts as object record queries, plus limit (default 500) and offset for pagination.
A single record can be fetched with GET /v2/activities/{activity}/records/{record_id} (UUID only). Activity records are also queryable with SQL: each activity is exposed as a table under the activities schema, named by its api_slug, e.g. activities.phone_calls. See Available data for the full picture of what’s queryable.

Updating activity records

Activity records support the same two update methods as object records:
  • PUT /v2/activities/{activity}/records/{record_id} overwrites: for multiselect attributes, the values you supply replace the existing list of values.
  • PATCH /v2/activities/{activity}/records/{record_id} appends: for multiselect attributes, the values you supply are added to the existing list of values.
Both accept the same values map, including all three record reference formats, as record creation.

Upserting activity records

PUT /v2/activities/{activity}/records?matching_attribute={attribute} creates an activity record, or updates the record that has the same value for the matching attribute. {attribute} is the slug or ID of an attribute on the activity, for example an ID from your own system. If more than one record matches the value, the request fails with a 400. It needs the same scopes as record creation.

Deleting activity records

DELETE /v2/activities/{activity}/records/{record_id} deletes a single activity record. It needs activity_record:read-write.

Webhooks

Activities emit seven webhook event types. Every payload also carries an actor. Note that the id of an activity event uses activity_id where an object event would use object_id.

Subscribing to one activity

A workspace can have many activities, and activity-record.created fires for all of them. If you only care about one, use a filter on the subscription:
Filters match on the fields of the payload, so they take the activity ID, not its api_slug. The other fields you can filter on are id.workspace_id, id.record_id, id.attribute_id, actor.type and actor.id. This is strongly recommended for activities: it keeps the deliveries you do not need from counting against your endpoint’s delivery rate limit.

Current limitations

  • Activity records have no web_url.
  • There is no activity-record.updated webhook event.
  • Activities cannot be added to lists.
  • The status endpoints do not accept activities as a target. Use select attributes instead.

Activities concept guide

How activities, activity records and schemas fit into Attio’s data model.