Attio
Datafly Signal delivers your identity-bearing website and product events to Attio server-to-server, creating or updating a person record for each contact. Records are matched on email address, so an existing person is updated rather than duplicated. Because delivery happens from your own server, contact capture is unaffected by ad blockers or browser tracking prevention.
Prerequisites
Before configuring Attio in Signal you need an Attio workspace and an API key with permission to read and write records.
Create an API key
- In Attio, open Settings (bottom-left) and go to Developers.
- Click API keys > Create new API key (or open an existing app and add a key).
- Give the key a name (e.g. “Datafly Signal”).
Grant the required scopes
The key must include both of these scopes, or the assert call will be rejected:
record_permission:read-write— to create and update records.object_configuration:read— to resolve the person object’s attributes.
Copy the key
Copy the API key and store it securely. Attio shows the full value once.
The API key is scoped to a single workspace. Use a key for the workspace whose CRM should receive the contacts.
Configuration
In Signal, add the Attio integration to your pipeline and provide:
| Field | Description |
|---|---|
| API Key | Your Attio API key (or OAuth access token), sent as Authorization: Bearer. Must have record_permission:read-write and object_configuration:read. |
Configure in Signal
Add the connector
In your pipeline, click Add integration, choose Attio from the CRM category, and select the Default preset.
Paste your API key
Enter the API key you created above into the API Key field.
Map your identity traits
Attio matches people on email, so this connector only delivers events that carry an identified email. Make sure your site calls datafly.identify() with at least an email trait (and, ideally, first_name, last_name, phone, and job_title) before the events you want sent to Attio fire.
Save and enable
Save the integration and enable the pipeline. Signal will begin upserting person records on identity-bearing events.
API Endpoint
Signal delivers to the Attio assert (upsert) person endpoint:
PUT https://api.attio.com/v2/objects/people/records?matching_attribute=email_addresses
Authorization: Bearer YOUR_API_KEY
Content-Type: application/jsonmatching_attribute=email_addresses tells Attio to find an existing person with the same email and update it, or create a new one if none exists.
Identity Signals
Attio is a first-party CRM: it stores readable contact details and uses the email address as the unique match key. For that reason this connector sends contact fields in clear text (not hashed) — hashing would break matching and make the records unusable.
| Signal trait | Attio attribute |
|---|---|
email | email_addresses[].email_address (lowercased, trimmed — the match key) |
first_name | name[].first_name |
last_name | name[].last_name |
name | name[].full_name |
phone | phone_numbers[].original_phone_number (normalised to E.164) |
job_title | job_title[].value |
An event with no email trait has no match key, so it is not delivered to Attio. Always call datafly.identify() with an email before the events you want synced.
Event Mapping
This connector upserts a person on identity-bearing events. The default preset enables Identified, Signed Up, Lead Generated, and Logged In; all other events are dropped.
A typical sign-up flow on your site:
datafly.identify({
email: "ada@example.com",
first_name: "Ada",
last_name: "Lovelace",
phone: "+1 555 867 5309",
job_title: "Senior Developer"
});
datafly.track("Signed Up", { method: "email" });Signal upserts the following person record into Attio:
{
"data": {
"values": {
"email_addresses": [
{ "email_address": "ada@example.com" }
],
"name": [
{ "first_name": "Ada", "last_name": "Lovelace", "full_name": "Ada Lovelace" }
],
"phone_numbers": [
{ "original_phone_number": "+15558675309" }
],
"job_title": [
{ "value": "Senior Developer" }
]
}
}
}For a Lead Generated event, a lead_source property is additionally written to the person’s description:
datafly.track("Lead Generated", { lead_source: "Pricing page demo request" });{
"data": {
"values": {
"email_addresses": [{ "email_address": "ada@example.com" }],
"description": [{ "value": "Pricing page demo request" }]
}
}
}Testing
- With the integration enabled, trigger an identified event on your site (sign up, or call
datafly.identify()followed by aSigned Uptrack). - In Attio, open the People list and search for the email address you used.
- Confirm the person exists and that the name, phone, and job title are populated.
- Re-trigger the same email with updated traits and confirm the same record is updated (not duplicated) — this verifies the email match key is working.
Troubleshooting
Nothing appears in Attio. Check that the event carried an email trait — events without an email are not delivered. Confirm datafly.identify() runs before the tracked event.
403 Forbidden. The API key is missing a scope. Ensure it has both record_permission:read-write and object_configuration:read, then regenerate if needed.
400 Bad Request. Usually an unknown attribute. If your workspace has renamed or removed a standard person attribute (email_addresses, name, phone_numbers, job_title, description), the assert is rejected. Restore the standard slug or adjust the mapping.
Duplicate people. Confirm the email is being sent and is consistent (the connector lowercases and trims it). Attio only deduplicates on email_addresses; two different emails for the same human will create two records.
429 Too Many Requests. Attio rate-limits per token. Signal retries automatically; if you see sustained 429s, lower the event volume routed to Attio or contact Attio about your workspace’s limit.