Running a personal-feel cold outreach cadence on Attio (founder emailing creators, composer sends through connected Gmail). Two findings that shape any follow-up design, both verified live 2026-08-23 on a Pro workspace.
1. You can tell who replied without the email scope.
An API token scoped to objects/lists/entries but NOT email gets 403 on GET /v2/emails, so message bodies are unreadable. Reply state is still derivable: POST /v2/objects/people/records/query returns two interaction attributes on every person, first_email_interaction and last_email_interaction, each a list of entries with interacted_at (take the one whose active_until is null).
first == last-> outbound-only, i.e. you emailed them and nobody has replied.first != last-> there has been a second interaction; on a cold list that is almost always their reply.lastis also the send timestamp, sonow - last >= 3 days AND first == lastis a complete, cheap "due for a follow-up ping" predicate computed entirely from one paginated query.
This matters because Attio's UI has no Sent view (/emails/ only shows Drafts, Outbox, Templates, Failed, all reading 0 after a batch goes out), so these attributes are the only bulk reconciliation surface for what actually got sent.
Caveat: the mailbox sync writes these attributes for the whole connected mailbox, so people auto-created from Gmail sync also carry them. Filter to your own records (e.g. by a channel_url/custom attribute being set) before counting.
2. Workflows can detect the cadence but cannot send it.
Attio workflows DO have a Recurring schedule trigger, so "every morning, find everyone Contacted more than 3 days ago" is expressible. What does not exist is a native send-email block: the only email action is Enroll in sequence, and sequences force an unsubscribe footer on every message including single-step ones (https://attio.com/help/reference/automations/sequences/email-deliverability-guide). Third-party blocks (Outreach, Mailchimp, Mixmax) send from those systems, not from your connected Gmail.
So on Attio the automatable half of a follow-up cadence is strictly detection and bookkeeping; the send stays manual (composer) or goes out through Gmail directly with Attio's mailbox sync attaching it to the record.
Design that falls out of the two. Use a stage status per touch (Contacted, then Followed Up) and one attribute-changed workflow per hop: trigger on Last email interaction changed, Find list entries filtered on the previous stage AND Parent Record is {Updated Person}, then update the entry to the next stage. Consecutive hops are disjoint because each guards on a different current stage, so they never fight, and the stage itself becomes the double-ping guard: an entry already at Followed Up cannot be picked up by the Contacted sweep. The parent-record filter is not optional at scale; without it the block returns the first 100 of your Lead entries and the right one usually is not among them.
One known false positive, in both this design and the simpler Lead -> Contacted version: an inbound reply also updates Last email interaction and will advance the stage. Triage replies to a Replied stage by hand; the first != last check above is what finds them.