Apollo Sequences and Emails
These endpoints run outreach through your workspace’s Apollo account. You can create a sequence, enroll contacts in it, turn it on and off, and send one email to a contact now or at a set time. Apollo does the sending, from a mailbox of your Apollo team, and tracks opens and replies. Pathbound records each enrollment and each send on the contact’s timeline.
Use them when a script or an AI agent should start outreach from what Pathbound knows about a contact. Every enrollment and every send starts from a Pathbound contact ID.
Before the first call, connect Apollo under Integrations with a connection that includes the sequence permissions, have a workspace admin turn on Sequences or Single email under MCP Tools → Apollo, and use an API key that has the actions:write scope. The MCP server’s apollo_* tools call these endpoints (tools reference), except apollo_add_segment_to_sequence, which calls the segment endpoint.
| Endpoint | Scope | What it does |
|---|---|---|
GET /v1/integrations/apollo/senders | actions:write | List the mailboxes you may send from. |
GET /v1/integrations/apollo/sequences | actions:write | List the team’s sequences. |
GET /v1/integrations/apollo/sequence-options | actions:write | List the sending schedules and the rulesets in use. |
POST /v1/integrations/apollo/sequences | actions:write | Create an inactive sequence. |
POST /v1/integrations/apollo/sequences/:sequence_id/activate | actions:write | Turn a sequence on. |
POST /v1/integrations/apollo/sequences/:sequence_id/pause | actions:write | Turn a sequence off. |
POST /v1/integrations/apollo/sequences/:sequence_id/schedule | actions:write | Point a sequence at a sending schedule. |
POST /v1/integrations/apollo/sequences/:sequence_id/contacts | actions:write | Enroll up to 25 contacts in a sequence. |
POST /v1/integrations/apollo/sequences/:sequence_id/contacts/stop | actions:write | Stop or remove up to 25 contacts. |
GET /v1/integrations/apollo/contacts/:contact_id/sequence-activity | actions:write | Read what happened to one contact in sequences. |
POST /v1/integrations/apollo/contacts/:contact_id/send_email | actions:write | Send one email to a contact, now or at a set time. |
GET /v1/integrations/apollo/scheduled-emails | actions:write | List single emails and their state. |
DELETE /v1/integrations/apollo/scheduled-emails/:id | actions:write | Cancel a single email before it is sent. |
The reads need actions:write too, because they show your team’s mailboxes, sequences and outreach to each contact. actions:write is opt-in: a key has it only when it was selected at creation.
What has to be on
Section titled “What has to be on”The Apollo connection
Section titled “The Apollo connection”The connection must include Apollo’s sequence and email permissions. A connection made before September 2026 does not, and is answered 412 needs_reconsent with the missing permissions in details.missing_scopes. A workspace admin reconnects Apollo under Integrations → Apollo.
Apollo answers some of these calls only on a paid Apollo plan. When it refuses for that reason the answer is 412 plan_required.
The switches
Section titled “The switches”Two switches under MCP Tools → Apollo decide what may be done. Both are off until a workspace admin turns them on.
| Switch | Endpoints it allows |
|---|---|
| Sequences | Every endpoint under /v1/integrations/apollo/sequences, GET /v1/integrations/apollo/sequence-options, and GET /v1/integrations/apollo/contacts/:contact_id/sequence-activity. |
| Single email | POST /v1/integrations/apollo/contacts/:contact_id/send_email. A scheduled email reads the switch again when it is due. |
| Either one | GET /v1/integrations/apollo/senders. |
| Neither | GET /v1/integrations/apollo/scheduled-emails and DELETE /v1/integrations/apollo/scheduled-emails/:id. |
Where a switch applies, the API reads it on every call and for every caller. A REST API key is held to it exactly as an MCP connection is, and a call with the switch off is answered 412 tool_disabled. The MCP server also reads the switches to decide which tools it lists.
Listing and canceling single emails are behind no switch in the API, so you can still review and cancel what is queued after Single email is turned off. Over MCP those two tools are listed only while the switch is on.
The sending mailbox
Section titled “The sending mailbox”Every enrollment and every single email names the mailbox it goes out from, in sender_account_id. There is no default.
The mailbox must be one that GET /v1/integrations/apollo/senders returns: active in your Apollo team and, when a workspace admin has set Allowed sender mailboxes under Integrations → Apollo, on that list. With no list set, every active mailbox of the Apollo team may be used.
The mailbox is checked against Apollo again on every write, and once more when a scheduled email is due. A mailbox that is not allowed is refused with 403 sender_not_allowed, and one that is inactive in Apollo with 400 sender_inactive. Another mailbox is never used in its place.
Idempotency
Section titled “Idempotency”Every POST on this page requires an Idempotency-Key header. Without it the call is refused with 400 IDEMPOTENCY_KEY_REQUIRED, before the body is checked.
| Header | Required | Description |
|---|---|---|
Idempotency-Key | yes, on every POST | Any string that is unique to the thing you intend to do. Use a new one for each new intention, and the same one when you retry. |
Within 24 hours, the same key on the same endpoint does the action once:
- While the first call is still running, a second is answered
409 ACTION_IN_PROGRESS. - After the first call succeeded, a second is answered
200withreplayed: true, theaction_idand a summary of the first result. On an enrollment or a stop the summary holds counts (enrolled_count,skipped_count,updated_count), not the per-contact lists. Nothing is done again. - After the first call failed, a second runs.
The key alone identifies the action: a different body under a used key is not run. Activate and pause are separate endpoints, so a key used to activate does not block a pause. To activate the same sequence a second time, after a pause, use a new key.
Send an Email keeps its own record: the same key within 24 hours returns the email that the first call created, in whatever state it is now, with replayed: true.
If your client times out, call again with the same key. See also Idempotency.
How long a call takes
Section titled “How long a call takes”These endpoints call Apollo while you wait, and Pathbound paces its calls to Apollo. A call that Apollo rate-limits is retried for up to 45 seconds before it is answered 429 rate_limited.
List Senders
Section titled “List Senders”GET /v1/integrations/apollo/sendersRequired scope: actions:write
Returns the mailboxes an enrollment or a single email may name. Call it before you enroll or send, and take sender_account_id from it.
curl https://api.pathbound.ai/v1/integrations/apollo/senders \ -H "Authorization: Bearer $PATHBOUND_API_KEY"Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "senders": [ { "sender_account_id": "66c8d9e0f1a2b3c4d5e6f701", "user_id": "66b7c8d9e0f1a2b3c4d5e6f7", "is_default": true, "active": true, "provider": "Gmail", "daily_limit": 50 } ], "note": "Ask the user which mailbox to send from and confirm it by email address before enrolling or sending. There is no default."}| Field | Description |
|---|---|
senders[].sender_account_id | Apollo’s ID for the mailbox. This is the value to send as sender_account_id. |
senders[].email | The mailbox’s address. |
senders[].user_id | The Apollo user the mailbox belongs to. |
senders[].is_default | Apollo’s own default flag. Pathbound does not use it to choose a mailbox. |
senders[].active | Always true here: inactive mailboxes are not listed. |
senders[].provider | The mailbox provider, as Apollo names it. |
senders[].daily_limit | The mailbox’s daily sending limit in Apollo, when it has one. |
senders[].deliverability | Apollo’s figures for the mailbox, when it reports them: open_rate, reply_rate, bounce_rate, spam_block_rate, domain_health_score. |
note | A sentence for a person or an AI agent. With no mailbox available it says how to add one. |
An empty senders means no mailbox may be used: link an active mailbox in Apollo, or have an admin widen Allowed sender mailboxes. The list is read from Apollo and kept for up to a minute.
List Sequences
Section titled “List Sequences”GET /v1/integrations/apollo/sequencesRequired scope: actions:write
Returns the sequences in your Apollo team, including inactive ones created through Create a Sequence.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | — | Part of a sequence name, up to 200 characters. |
include_archived | boolean | false | Include archived sequences. |
page | number | 1 | Page number, 1 to 500. |
per_page | number | 50 | Sequences per page, 1 to 100. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "sequences": [ { "sequence_id": "66f1a2b3c4d5e6f701234567", "name": "Q4 renewals", "active": false, "archived": false, "num_steps": 3, "schedule_id": "66e0f1a2b3c4d5e6f7012345", "is_owner": true, "last_used_at": "2026-10-06T14:02:11.000Z", "stats": { "scheduled": 0, "delivered": 42, "opened": 30, "clicked": 4, "replied": 6, "bounced": 1 } } ], "page": 1, "per_page": 50, "total": 12, "total_pages": 1}| Field | Description |
|---|---|
sequences[].sequence_id | Apollo’s ID for the sequence. Use it in the paths below. |
sequences[].active | Whether the sequence is on. |
sequences[].num_steps | The number of steps. |
sequences[].schedule_id | The sending schedule in force. Absent when the sequence uses Apollo’s team default. Resolve it with List Sequence Options. |
sequences[].ruleset_id | The ruleset applied when the sequence was created. Absent when there is none. |
sequences[].is_owner | Whether the Apollo user behind the connection owns the sequence. |
sequences[].stats | Apollo’s counts for the sequence: scheduled, delivered, opened, clicked, replied, bounced. Also reply_rate, open_rate and bounce_rate, as Apollo reports them. |
total, total_pages | Apollo’s totals for the search. They count archived sequences too. |
Archived sequences are taken out of each page after Apollo returns it, so a page can hold fewer than per_page sequences.
List Sequence Options
Section titled “List Sequence Options”GET /v1/integrations/apollo/sequence-optionsRequired scope: actions:write
Returns the two things a sequence can be pointed at: the team’s sending schedules, and the rulesets already applied to the team’s sequences. It takes no parameters.
A sending schedule decides the days and hours Apollo may send a sequence’s emails. You can read the schedules and attach one to a sequence. The windows themselves are edited only in Apollo, under Settings → Sequences → Schedules.
A ruleset is a set of shared settings applied to a sequence. Apollo has no way to list or name rulesets, so this endpoint reports the ruleset IDs found on the team’s sequences, with the sequences that use each one.
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "schedules": [ { "schedule_id": "66e0f1a2b3c4d5e6f7012345", "name": "Normal Business Hours", "is_default": true, "time_zone": "America/Chicago", "use_contacts_time_zone": false, "skip_holidays": true, "windows": { "monday": [[8, 17]], "tuesday": [[8, 17]], "wednesday": [[8, 17]], "thursday": [[8, 17]], "friday": [[8, 17]] }, "sending_hours": "Mon–Fri 8:00–17:00" } ], "rulesets_in_use": [ { "ruleset_id": "66d9e0f1a2b3c4d5e6f70123", "sequence_count": 4, "used_by": ["Q4 renewals", "Trial follow-up"] } ], "note": "Sending windows are set in Apollo (Settings → Sequences → Schedules); …"}| Field | Description |
|---|---|
schedules[].windows | Day of the week to a list of [start_hour, end_hour] pairs in 24-hour time. A day without a window is left out. |
schedules[].sending_hours | The same windows as one line of text. Empty when the schedule sends on no day. |
schedules[].time_zone | The time zone the windows are in. |
schedules[].use_contacts_time_zone | When true, Apollo sends in each contact’s own time zone instead. |
rulesets_in_use[].used_by | Up to five sequences that use the ruleset, so a person can recognize it. |
rulesets_in_use is read from the team’s first 100 sequences.
Create a Sequence
Section titled “Create a Sequence”POST /v1/integrations/apollo/sequencesRequired scope: actions:write
Required header: Idempotency-Key
Creates a sequence of 1 to 10 email steps in your Apollo team. The sequence is created inactive and nobody is enrolled, so this call sends nothing. Its steps are ready to send: once contacts are enrolled, activating the sequence is all that starts the emails.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | The sequence’s name, 1 to 200 characters. |
steps | object[] | yes | 1 to 10 email steps, in order. |
steps[].body | string | yes | The email body, up to 100,000 characters. Apollo’s personalization variables, such as {{first_name}}, are passed on as written. |
steps[].body_format | string | no | html (default) or text. Text is converted to HTML. |
steps[].subject | string | for a new thread | The subject, up to 998 characters. Required on a step that starts a new thread. Leave it out on a reply. |
steps[].wait_time | number | yes | How long to wait after the previous step, 0 to 365. |
steps[].wait_mode | string | yes | The unit of wait_time: minute, hour or day. |
steps[].thread_mode | string | no | new_thread or reply_to_thread. The first step is new_thread and cannot be anything else. Later steps default to reply_to_thread. |
steps[].include_signature | boolean | no | Append the sender’s Apollo signature. Default true. |
labels | string[] | no | Up to 10 Apollo labels, each 1 to 100 characters. |
max_emails_per_day | number | no | The sequence’s daily cap in Apollo, 1 to 1000. |
schedule_id | string | no | A sending schedule from List Sequence Options. Without it the sequence uses Apollo’s team default. You can change it later. |
ruleset_id | string | no | A ruleset from List Sequence Options. Without it the sequence has none. It cannot be set or changed after the sequence is created. |
{ "name": "Q4 renewals", "steps": [ { "subject": "Your renewal in December", "body": "<p>Hi {{first_name}},</p><p>Your plan renews on December 1.</p>", "wait_time": 0, "wait_mode": "day" }, { "body": "<p>Hi {{first_name}}, a quick follow-up on my note below.</p>", "wait_time": 3, "wait_mode": "day" } ]}Some settings are fixed. Every step is an automatic email with one version of its text. The sequence finishes for a contact who replies or is marked interested, pauses for one who answers with an out-of-office message, and keeps to Apollo’s bounce list.
Response — 201 Created
Section titled “Response — 201 Created”{ "status": "success", "action_id": "670f3a1b2c3d4e5f60718293", "sequence_id": "66f1a2b3c4d5e6f701234567", "sequence_name": "Q4 renewals", "active": false, "num_steps": 2, "note": "Created as an inactive draft. …"}schedule_id and ruleset_id are returned when the sequence has them. action_id is the ID of Pathbound’s record of the action.
Activate or Pause a Sequence
Section titled “Activate or Pause a Sequence”POST /v1/integrations/apollo/sequences/:sequence_id/activatePOST /v1/integrations/apollo/sequences/:sequence_id/pauseRequired scope: actions:write
Required header: Idempotency-Key
activate turns the sequence on: Apollo starts sending its steps to every enrolled contact, from the mailbox each was enrolled with, on the sequence’s schedule. pause turns it off: every enrolled contact is paused and nothing more is sent until the sequence is activated again. To stop particular contacts, use Stop Contacts.
Neither takes a body.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
sequence_id | string | Apollo’s sequence ID, from List Sequences or Create a Sequence. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "action_id": "670f3a1c2c3d4e5f60718294", "sequence_id": "66f1a2b3c4d5e6f701234567", "sequence_name": "Q4 renewals", "active": true, "num_steps": 2, "scheduled": 18, "note": "Sequence is active: Apollo now sends its steps to enrolled contacts on the sequence schedule. …"}active is the state Apollo reports after the call, not the one you asked for. scheduled is the sequence’s scheduled count, as Apollo reports it.
Set a Sequence’s Schedule
Section titled “Set a Sequence’s Schedule”POST /v1/integrations/apollo/sequences/:sequence_id/scheduleRequired scope: actions:write
Required header: Idempotency-Key
Points a sequence at one of the team’s sending schedules. This changes the days and hours its emails may go out, for contacts already enrolled too. Only the schedule is changed: the steps, the owner and the on or off state are not touched.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
schedule_id | string | yes | A schedule_id from List Sequence Options. |
A schedule that is not in your Apollo team is refused with 404 not_found. A schedule with no sending windows is refused with 400 validation, because Apollo does not accept an empty one.
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "action_id": "670f3a1d2c3d4e5f60718295", "sequence_id": "66f1a2b3c4d5e6f701234567", "sequence_name": "Q4 renewals", "schedule_id": "66e0f1a2b3c4d5e6f7012345", "schedule_name": "Normal Business Hours", "sending_hours": "Mon–Fri 8:00–17:00", "time_zone": "America/Chicago", "use_contacts_time_zone": false, "active": false, "num_steps": 2, "note": "Emails for this sequence now go out Mon–Fri 8:00–17:00 (America/Chicago). …"}Enroll Contacts
Section titled “Enroll Contacts”POST /v1/integrations/apollo/sequences/:sequence_id/contactsRequired scope: actions:write
Required header: Idempotency-Key
Enrolls up to 25 contacts in an existing sequence, from the mailbox you name. If the sequence is active, Apollo starts emailing them on the sequence’s schedule. If it is inactive, nothing is sent until it is activated.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
sequence_id | string | Apollo’s sequence ID. |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
contact_ids | string[] | yes | 1 to 25 Pathbound contact IDs. Duplicates count once. |
sender_account_id | string | yes | The mailbox to send from: a sender_account_id from List Senders. |
start_at | string | no | An ISO 8601 time in the future. The contacts are enrolled paused, and Apollo starts them at that time. Without it they start in the sequence’s next sending window. |
tags | string[] | no | Up to 20 tags, each up to 64 characters, written on every contact that was enrolled. |
curl -X POST https://api.pathbound.ai/v1/integrations/apollo/sequences/66f1a2b3c4d5e6f701234567/contacts \ -H "Authorization: Bearer $PATHBOUND_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: q4-renewals-batch-1" \ -d '{ "contact_ids": [ "665f1c2ab3d4e5f601234567", "665f1c2ab3d4e5f601234568", "665f1c2ab3d4e5f601234569" ], "sender_account_id": "66c8d9e0f1a2b3c4d5e6f701", "tags": ["campaign:q4-renewals"] }'Each contact is matched to a contact in your Apollo team by email address, or created there. That costs no Apollo credits.
Apollo decides whom it accepts. It refuses a contact who is already in another sequence, whose email is unverified, who is at a do-not-contact stage, and so on. Apollo has options that override these refusals, and Pathbound never sends them. A refused contact comes back in skipped with Apollo’s reason.
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "action_id": "670f3a1e2c3d4e5f60718296", "sequence": { "sequence_id": "66f1a2b3c4d5e6f701234567", "name": "Q4 renewals" }, "requested": 3, "enrolled": 2, "enrolled_contact_ids": ["665f1c2ab3d4e5f601234567", "665f1c2ab3d4e5f601234568"], "skipped": [ { "contact_id": "665f1c2ab3d4e5f601234569", "reason": "no_email" } ], "tags_applied": ["campaign:q4-renewals"]}start_at is echoed when you sent one. A call in which every contact is skipped is still a 200, with enrolled: 0.
skipped[].reason | Meaning |
|---|---|
not_found_in_pathbound | No contact with this ID in your workspace. |
no_email | The contact has no email address. |
already_enrolled | The contact is already active or paused in this sequence. |
bridge_failed | The contact could not be matched or created in Apollo. |
| anything else | Apollo’s own reason for refusing the contact, as Apollo wrote it. |
A workspace may enroll a set number of contacts a day through these endpoints and the MCP tools together, counted in UTC. Skipped contacts do not count. A call that would pass the cap is refused with 429 budget_exceeded.
To enroll every member of a saved segment as a background job, use POST /v1/segments/:ref/actions.
Stop Contacts
Section titled “Stop Contacts”POST /v1/integrations/apollo/sequences/:sequence_id/contacts/stopRequired scope: actions:write
Required header: Idempotency-Key
Stops up to 25 contacts in a sequence so that no further step is sent to them, or removes them from it.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
contact_ids | string[] | yes | 1 to 25 Pathbound contact IDs. |
mode | string | no | stop (default) ends future steps and keeps the contact’s history in the sequence. remove takes the contact out of the sequence. |
reason | string | no | A short reason, up to 300 characters. It is kept on Pathbound’s record and on the contact’s timeline. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "action_id": "670f3a1f2c3d4e5f60718297", "sequence_id": "66f1a2b3c4d5e6f701234567", "mode": "stop", "requested": 2, "updated": 1, "updated_contact_ids": ["665f1c2ab3d4e5f601234567"], "skipped": [ { "contact_id": "665f1c2ab3d4e5f601234569", "reason": "not_enrolled" } ]}updated counts the contacts Apollo was asked to stop or remove. A contact is skipped with not_found_in_pathbound, or with not_enrolled when Pathbound has never enrolled or emailed it through Apollo. Pathbound does not check with Apollo that each of the others was in this sequence.
Get a Contact’s Sequence Activity
Section titled “Get a Contact’s Sequence Activity”GET /v1/integrations/apollo/contacts/:contact_id/sequence-activityRequired scope: actions:write
Returns what happened to one contact in Apollo sequences: Pathbound’s own enrollment records, and Apollo’s activity feed for the contact.
This read counts toward your usage the way reading the contact’s timeline does. See Usage and metering.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
contact_id | string | The Pathbound contact ID. |
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
sequence_id | string | — | Only this sequence. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "contact_id": "665f1c2ab3d4e5f601234567", "apollo_contact_id": "66a6b7c8d9e0f1a2b3c4d5e6", "enrollments": [ { "sequence_id": "66f1a2b3c4d5e6f701234567", "sequence_name": "Q4 renewals", "status": "replied", "enrolled_at": "2026-10-06T14:02:11.000Z", "last_event_at": "2026-10-08T09:41:27.000Z" } ], "events": [ { "type": "replied", "occurred_at": "2026-10-08T09:41:27.000Z", "sequence_id": "66f1a2b3c4d5e6f701234567", "sequence_name": "Q4 renewals", "step_position": 1 }, { "type": "enrolled", "occurred_at": "2026-10-06T14:02:11.000Z", "sequence_id": "66f1a2b3c4d5e6f701234567", "sequence_name": "Q4 renewals" } ]}| Field | Description |
|---|---|
enrollments[] | Pathbound’s records of the enrollments made through Pathbound, latest first. start_at is present for a delayed start, and reason when a stop gave one. |
enrollments[].status | enrolled, paused, replied, bounced, finished, stopped, removed or failed. |
events[] | Up to 50 entries of Apollo’s activity feed for the contact. |
events[].type | enrolled, paused, resumed, replied, completed, removed or failed. |
note | Present when the contact was never enrolled or emailed through Apollo by Pathbound. events is then empty. |
Opens, clicks and replies to each message are on the contact’s timeline, as events.
Send an Email
Section titled “Send an Email”POST /v1/integrations/apollo/contacts/:contact_id/send_emailRequired scope: actions:write
Required header: Idempotency-Key
Sends one email to a contact through Apollo, from the mailbox you name, now or at send_at. The email goes to the contact’s email address, read from the contact record. The request cannot set it.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
contact_id | string | The Pathbound contact to email. |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
sender_account_id | string | yes | The mailbox to send from: a sender_account_id from List Senders. |
subject | string | yes | The subject, up to 998 characters. |
body | string | yes | The email body. |
body_format | string | no | html (default) or text. Text is converted to HTML. |
cc | string[] | no | Up to 5 addresses. |
bcc | string[] | no | Up to 5 addresses. |
track | boolean | no | Apollo’s open and click tracking. Default true. |
send_at | string | no | An ISO 8601 time within the next 30 days. Without it, or with a time less than a minute away, the email is sent now. |
curl -X POST https://api.pathbound.ai/v1/integrations/apollo/contacts/665f1c2ab3d4e5f601234567/send_email \ -H "Authorization: Bearer $PATHBOUND_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: followup-665f1c2a-2026-10-13" \ -d '{ "sender_account_id": "66c8d9e0f1a2b3c4d5e6f701", "subject": "Following up on Thursday", "body": "Hi Ada,\n\nHere are the notes from our call.", "body_format": "text" }'Response — 200 OK or 202 Accepted
Section titled “Response — 200 OK or 202 Accepted”The answer is 202 when the email was scheduled and 200 otherwise.
{ "status": "success", "email": { "scheduled_email_id": "670f3b2c3d4e5f6071829304", "contact_id": "665f1c2ab3d4e5f601234567", "sender_account_id": "66c8d9e0f1a2b3c4d5e6f701", "subject": "Following up on Thursday", "send_at": "2026-10-13T15:04:05.000Z", "status": "sent", "attempts": 1, "apollo_message_id": "670f3b2d9a8b7c6d5e4f3a2b", "sent_at": "2026-10-13T15:04:08.000Z", "action_id": "670f3b2c3d4e5f6071829303", "created_at": "2026-10-13T15:04:05.000Z" }}A 200 does not mean the email was sent. Read email.status. Once the request has passed its checks, whatever happens to the email is reported on the email, not as an HTTP error.
email.status | Meaning |
|---|---|
scheduled | Waiting for send_at. |
sent | Apollo reports the email sent. sent_at is when, and sent_from is the address Apollo used. |
sending | Apollo has the email and has not confirmed it yet, because it paces each mailbox. Pathbound keeps checking. Look again with List Scheduled Emails. |
delayed | Not sent yet. It is tried again at next_attempt_at, and failure says why. |
failed | Not sent, and it will not be tried again. failure.code and failure.message say why. |
canceled | Canceled before it was sent. |
The body of the email is not returned.
What happens when the email is due
Section titled “What happens when the email is due”Pathbound holds a scheduled email itself and hands it to Apollo at send_at. At that moment, and for an email sent now, it checks again that:
- the API key that asked is still active and still has
actions:write, and its owner is still a verified member of the workspace (principal_revokedotherwise); - Single email is still on and the Apollo connection still works;
- the contact still exists, and its email address is the one it had when you asked (
recipient_changedotherwise); - the mailbox may still be used.
An email that fails one of these ends failed. It is never sent from another mailbox or to another address.
An email is delayed and tried again when the mailbox has reached its Apollo sending limit (an hour later), when Apollo rate-limits the call, when Apollo fails (15 minutes later), or when the workspace has sent its daily allowance of single emails (at the start of the next day, UTC). After four attempts it ends failed.
An email is handed to Apollo at most once. If Apollo’s answer is lost, the email stays sending while Pathbound asks Apollo for its status, and it ends failed with failure.code unconfirmed if Apollo never confirms it. Such an email may have gone out: look at the message in Apollo before you send it again.
failure.code is one of the error codes below, principal_revoked, recipient_changed, unconfirmed, or Apollo’s own reason when Apollo reports that the send failed.
List Scheduled Emails
Section titled “List Scheduled Emails”GET /v1/integrations/apollo/scheduled-emailsRequired scope: actions:write
Returns the single emails created through Send an Email, whatever their state, latest send_at first. Emails that were sent at once are listed too.
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
contact_id | string | — | Only emails to this contact. |
status | string | — | One of scheduled, sending, delayed, sent, failed, canceled. |
limit | number | 50 | The most emails to return, 1 to 200. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "emails": [ { "scheduled_email_id": "670f3b2c3d4e5f6071829305", "contact_id": "665f1c2ab3d4e5f601234567", "sender_account_id": "66c8d9e0f1a2b3c4d5e6f701", "subject": "Your renewal in December", "send_at": "2026-10-20T14:00:00.000Z", "status": "scheduled", "attempts": 0, "action_id": "670f3b2c3d4e5f6071829306", "created_at": "2026-10-13T15:10:00.000Z" } ]}Each entry has the fields of email in Send an Email. Before an email is sent, sent_from is the address of the mailbox you chose.
Cancel a Scheduled Email
Section titled “Cancel a Scheduled Email”DELETE /v1/integrations/apollo/scheduled-emails/:idRequired scope: actions:write
Cancels an email that is scheduled or delayed. An email in any other state cannot be canceled, and the call is refused with 409 validation and the state in details.status. The Idempotency-Key header is optional here.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | string | The scheduled_email_id of the email. |
Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "email": { "scheduled_email_id": "670f3b2c3d4e5f6071829305", "contact_id": "665f1c2ab3d4e5f601234567", "sender_account_id": "66c8d9e0f1a2b3c4d5e6f701", "subject": "Your renewal in December", "send_at": "2026-10-20T14:00:00.000Z", "status": "canceled", "attempts": 0, "action_id": "670f3b2c3d4e5f6071829306", "created_at": "2026-10-13T15:10:00.000Z" }}An email that was still scheduled created nothing in Apollo, so there is nothing to remove there. A delayed email has been through a send attempt: the contact was matched or created in Apollo and a draft message was written there before the send was refused, and canceling does not remove them. Delete the draft in Apollo if you do not want it kept.
What lands on the contact’s timeline
Section titled “What lands on the contact’s timeline”Each enrollment, each stop and each email that was sent is written to the contact’s timeline as a pathbound_action event. Its data.action_type is apollo_add_to_sequence, apollo_stop_in_sequence or apollo_send_email, and data carries the sequence, the subject and the sending address, as they apply.
What recipients then do is read back from Apollo about every 30 minutes and written to the same timeline: apollo_email_sent, apollo_email_opened, apollo_email_clicked, apollo_email_replied, apollo_email_bounced, apollo_email_unsubscribed, apollo_email_spam_blocked, apollo_sequence_finished, apollo_sequence_stopped and apollo_sequence_failed.
Errors
Section titled “Errors”An error has the shape { "status": "error", "code": "…", "error": "…" }, with a hint and details where the tables name them. See Errors for the answers every endpoint shares.
Every endpoint
Section titled “Every endpoint”| Status | code | Meaning |
|---|---|---|
400 | none | A path, query or body value failed validation. details lists each field. |
403 | none | The API key does not have actions:write. required_scopes lists it. |
412 | integration_missing | Apollo is not connected. Connect it under Integrations. |
412 | invalid_credentials | Apollo no longer accepts the connection. Reconnect Apollo. |
412 | needs_reconsent | The connection predates the permissions this endpoint needs (details.missing_scopes). A workspace admin reconnects Apollo. |
412 | tool_disabled | The switch for this endpoint is off (details.tool). A workspace admin turns it on under MCP Tools → Apollo. |
412 | plan_required | Apollo refused the call for the Apollo plan or for the permissions of the connection. If the plan is a paid one, reconnect Apollo. |
403 | forbidden | Apollo refused the call for another reason. The message carries Apollo’s own. |
404 | not_found | Apollo answered that the record does not exist. |
409 | dnc_blocked | Apollo’s Do Not Contact settings block the call. |
422 | validation | Apollo rejected the request. The message carries Apollo’s own. |
429 | mailbox_limit | Apollo answered that the sending mailbox reached its limit. Try later, or choose another mailbox. |
429 | rate_limited | Apollo’s rate limit held after retries. details.retry_after is in seconds. |
502 | upstream_error | Apollo failed or did not answer. |
500 | upstream_error or none | The call failed unexpectedly. |
The first four 412 answers are checked in the order of the table: connected, accepted, permissions, switch.
GET /v1/integrations/apollo/scheduled-emails and DELETE /v1/integrations/apollo/scheduled-emails/:id do not call Apollo. Of the rows above, only the 400 and the scope 403 apply to them.
Every POST
Section titled “Every POST”| Status | code | Meaning |
|---|---|---|
400 | IDEMPOTENCY_KEY_REQUIRED | The Idempotency-Key header is missing. |
409 | ACTION_IN_PROGRESS | A call with this key is still running. Wait and call again with the same key. |
Sequences
Section titled “Sequences”| Status | code | Meaning |
|---|---|---|
400 | validation | Create: the first step is not a new thread, or a step that starts a new thread has no subject. Schedule: the schedule has no sending windows. |
404 | not_found | Schedule: the schedule_id is not one of your team’s schedules. |
Enrollment, stops and activity
Section titled “Enrollment, stops and activity”| Status | code | Meaning |
|---|---|---|
400 | validation | Enroll: start_at is not in the future. |
400 | sender_inactive | Enroll: the mailbox is inactive or disconnected in Apollo. Choose another from List Senders. |
403 | sender_not_allowed | Enroll: the mailbox is not one you may send from. An admin can widen Allowed sender mailboxes. |
404 | none | Activity: the contact does not exist in your workspace. |
429 | budget_exceeded | Enroll: the workspace’s enrollments for the day are used (details.cap, details.used). Try again the next day, UTC. |
Single emails
Section titled “Single emails”| Status | code | Meaning |
|---|---|---|
400 | validation | send_at is more than 30 days ahead. |
400 | sender_inactive | Send: the mailbox is inactive or disconnected in Apollo. |
403 | sender_not_allowed | Send: the mailbox is not one you may send from. |
404 | not_found | The contact, or the scheduled email, does not exist in your workspace. |
409 | validation | Cancel: the email is no longer scheduled or delayed (details.status). |
422 | validation | The contact has no email address (details.code is CONTACT_EMAIL_MISSING). |
Everything Apollo refuses after these checks, such as a contact under Do Not Contact or a mailbox at its limit, is reported in email.status and email.failure, not as an HTTP error.