# Choose your interface
Source: https://docs.reply.io/agents/choose-your-interface
MCP vs Skills vs CLI vs API — how the four Reply.io access layers differ and which to pick.
MCP, Skills, CLI, and API are not separate products — they are four ways to access the same
Reply.io platform. Same account, same [API key](/api-reference/authentication), same object model,
same [rate limits](/api-reference/rate-limits).
## Comparison
| | **MCP** | **Skills** | **CLI** | **API** |
| ------------------- | -------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------- | ------------------------------------------ |
| **Best for** | Agents in MCP clients (Claude, Cursor) | Coding agents that need procedure, not just tools | Shell scripts, CI, coding agents | Production integrations, webhooks, bulk |
| **Granularity** | 70 curated tools | 18 skills in 3 installable packs | 5 command groups + `reply api` passthrough | Every endpoint |
| **Install cost** | One client config entry | One `reply skills install` | One `npm install` | None (HTTP) |
| **Safety built in** | `readOnlyHint`/`destructiveHint` annotations | Hard confirmation gates + previews | None — the caller gates | Scoped keys only |
| **Coverage today** | Day-to-day operations + Jason AI SDR | Planning, launch, triage, analysis, guardrails, durable work | Auth and identity, skill installs, plus every endpoint via `reply api` | Full surface |
| **Start** | [Connect](/mcp/connect) | [Install](/skills/overview#install) | [Install](/cli/overview#install) | [Get a key](/api-reference/authentication) |
## Decision rules
* **Your agent runs in an MCP client** (Claude, Cursor, Make, n8n) → use **[MCP](/mcp/overview)**.
Richest interactive surface, and the only one with per-tool safety annotations the client can
gate on automatically.
* **Your agent has a shell** (Claude Code, Codex, CI pipelines) → use the **[CLI](/cli/overview)**,
and add **[Skills](/skills/overview)** so it follows proven procedure — previews,
verification steps, and confirmation gates included.
* **You're building a production integration** — webhooks, bulk imports, background jobs, or
anything not exposed as a tool/command → use the **[REST API](/api-reference/introduction)**.
* **You want Jason (AI SDR) programmable** — approval queues, knowledge bases, offers, playbooks →
**[MCP](/mcp/overview)** has the richest Jason toolset today; the REST
API covers the knowledge/offer/playbook layer.
## They compose
The layers are designed to be combined, not chosen once:
* **Skills drive an execution surface** — the skill decides *what* to do; the CLI, MCP, or the API
does it. `reply-adapter` is the pack that carries that execution layer, so it is the one to install
when you want your agent operating Reply.io.
* An agent can follow a **skill's procedure using MCP tools** instead of CLI commands — the
vendor-neutral skills describe platform operations, not surface-specific calls.
* **MCP for the interactive path, REST for the exhaustive one** — drop to REST for bulk
imports/updates, report exports, and webhook administration that aren't exposed as tools.
* A **CLI cron job** can do the weekly hygiene sweep while an **MCP agent** handles the
conversational work in the same workspace.
## Consistent everywhere
Whatever surface you choose, these hold:
* **One key, scoped** — `domain:verb` scopes (`contacts:read`, `sequences:operate`, …) behave
identically on every surface. See [Authentication](/api-reference/authentication).
* **One object model** — contacts, lists, sequences, threads, tasks, and Jason objects carry the
same IDs and semantics everywhere.
* **One rate-limit budget** — see [Rate limits](/api-reference/rate-limits).
* **Same safety expectations** — see [Agent safety rules](/agents/safety).
# Agentic capabilities
Source: https://docs.reply.io/agents/overview
The capability map for agents on the Reply.io platform — outcomes, access layers, and where the human boundary sits.
Reply.io is a sales-engagement platform an AI agent can operate end to end: find and manage
contacts, launch multichannel outreach, triage and answer replies, pull performance data, and
program Jason — Reply's AI SDR — as a supervised autopilot. Everything runs behind one scoped
[API key](/api-reference/authentication) across four access layers.
## What an agent can do today
| Outcome | Status | Where to start |
| ---------------------------------------------------------------- | -------------- | -------------------------------------------------------------------- |
| Import and manage contacts, lists, accounts | ✅ Available | [Import and clean contacts](/guides/import-contacts) |
| Launch multichannel sequences (email + LinkedIn) | ✅ Available | [Launch multichannel outreach](/guides/launch-outreach) |
| Manage and optimize running sequences | ✅ Available | [Improve sequences every cycle](/guides/improve-sequences) |
| Triage the inbox and send approved replies | ✅ Available | [Convert replies into meetings](/guides/convert-replies-to-meetings) |
| Pull reports and per-sequence stats | ✅ Available | [Decide what to scale or fix](/guides/analyze-performance) |
| Configure Jason AI SDR (knowledge, offers, playbooks, approvals) | ✅ Available | [Build a supervised AI SDR](/guides/build-an-ai-sdr) |
| Event-driven automation via webhooks | ✅ Available | [Automate outbound end-to-end](/guides/automate-outbound) |
| Validate emails, enforce blacklists, keep lists clean | ✅ Available | [Automate outbound end-to-end](/guides/automate-outbound) |
| Search the 1B+ B2B database for new prospects | 🔜 Coming soon | [Find your ideal prospects](/guides/find-ideal-prospects) |
| Waterfall-enrich contacts (emails, phones, firmographics) | 🔜 Coming soon | [Find your ideal prospects](/guides/find-ideal-prospects) |
## The four access layers
All four surfaces operate the same platform — same key, same objects, same limits. They differ in
how an agent consumes them:
```
┌────────────────────────────┐
MCP client ──▶ │ MCP · 70 annotated tools │ ──┐
└────────────────────────────┘ │
┌────────────────────────────┐ │ ┌───────────────┐
Coding agent ──▶ │ Skill · packaged workflows│ ──┤ ───▶ │ Reply.io v3 │
│ └─▶ CLI · `reply` command │ │ │ platform │
└────────────────────────────┘ │ └───────────────┘
┌────────────────────────────┐ │
Any program ──▶ │ REST API · api.reply.io/v3│ ──┘
└────────────────────────────┘
```
* **[MCP](/mcp/overview)** — direct tool access for AI agents in MCP clients.
* **[Skills](/skills/overview)** — outbound expertise and guardrails as markdown packs; the skill
decides *what* to do, the CLI or MCP does it.
* **[CLI](/cli/overview)** — terminal and automation interface with JSON output and safety flags.
* **[API](/api-reference/introduction)** — low-level programmable integration; the exhaustive surface.
Not sure which fits? [Choose your interface](/agents/choose-your-interface).
## The human boundary today
An agent can operate everything behind the API key. Three things still need a human:
1. **Account creation** — a human signs up (14-day free trial, no credit card required).
2. **API key issuance** — created in the Reply app under **Settings → API Keys** and handed to the
agent. Keys support granular scopes — see [Authentication](/api-reference/authentication).
3. **Billing** — plan upgrades and credit purchases happen in the Reply app.
## Next steps
MCP vs Skills vs CLI vs API — decision rules and a comparison table.
High-stakes gating, scoped keys, and rate-limit etiquette across all surfaces.
Ready-to-paste prompts for agents operating Reply.
Task-based guides for every outcome in the table above.
# Drop-in system prompts
Source: https://docs.reply.io/agents/prompts
Ready-to-paste system prompts for AI agents operating Reply.io — cross-surface, MCP, and CLI variants.
Paste these into an agent's system prompt to encode the platform's object model, safety rules, and
error handling. Pick the variant matching your agent's surface.
## Cross-surface platform prompt
Works for any agent operating Reply.io, regardless of access layer:
```text theme={null}
You operate the Reply.io sales-engagement platform. Rules:
1. One platform, four surfaces (API, MCP, CLI, Skills) — same objects everywhere:
contacts, lists, sequences (with steps and variants), inbox threads, tasks,
and Jason AI SDR objects (knowledge bases, offers, playbooks, approval queue).
2. Resolve before you mutate: look up exact IDs with search/list operations first.
Never invent an ID and never ask the user for one — users don't know internal IDs.
3. High-stakes actions require explicit user confirmation BEFORE executing:
sending any message, approving drafts, starting sequences, enrolling contacts,
blacklisting, changing contact owners, deleting, and switching Jason to
Autonomous. A successful call executes immediately; there is no undo.
4. Report honestly from per-item results: state exact counts ("imported 48 of 50"),
and never claim success for an item the response didn't confirm.
5. On timeouts of send-like operations, verify state with a read before retrying —
the operation may have already succeeded.
6. Respect rate limits: honor Retry-After on 429, back off on 5xx, paginate lists.
7. If a capability seems missing, check the docs (docs.reply.io) before concluding;
prefer the REST API for anything not exposed on your surface.
```
## MCP variant
For agents connected to the [Reply MCP server](/mcp/overview), use the canonical MCP prompt — it
adds tool-annotation gating, response-envelope parsing, and the MCP-specific failure guidance:
The canonical prompt for MCP agents, maintained with the MCP docs.
## CLI variant
For coding agents that shell out to the [`reply` CLI](/cli/overview):
```text theme={null}
You operate Reply.io through the `reply` CLI. Rules:
1. The CLI covers authentication and identity (auth, profile, team). Everything
else goes through `reply api ` — a raw authenticated call to a v3
endpoint, e.g. `reply api /v3/sequences`. Use paths exactly as documented,
starting with /v3; the query string goes in the path.
2. Always pass --json. Data arrives on stdout, status/errors on stderr.
3. `reply api` prints {"code":,"data":} for every status and
exits 1 on >= 400 — branch on .code and read .data for the problem details.
Other commands print errors as one stderr line:
{"error":{status,code,title,detail,hint}}. Act on `hint`.
4. Exit codes: 0 = success, 1 = API/runtime error, 2 = usage error.
5. There is NO --dry-run and NO confirmation prompt. `reply api` sends exactly
what you give it. Before any write — POST/PUT/PATCH/DELETE, starting a
sequence, sending a reply — show the user the exact request and get explicit
approval first.
6. A --body switches the method to POST; override with --method. Bodies accept
inline JSON, @file, or - for stdin.
7. Paginate with the endpoint's own parameters (usually top/skip) and keep going
while the response has hasMore: true.
8. Rate limits and transient 5xx are retried by the CLI (Retry-After, backoff) —
do not add your own retry loop around writes; verify state with a GET instead.
9. Resolve IDs with a GET before mutating — never invent numeric IDs.
10. If a call fails with TEAM_REQUIRED, run `reply team list`, ask the user which
workspace to act in, then `reply team use ` or pass --team-id.
```
## When to hand the agent a Skill instead
If your agent platform supports skills (e.g. Claude Code, Codex), install
[Reply Skills](/skills/overview) rather than writing procedure into the prompt — they package the
procedure above with verification steps and confirmation gates, and update through
`reply skills update`. Keep the CLI variant prompt as a fallback for platforms without skill support.
# Agent safety rules
Source: https://docs.reply.io/agents/safety
Cross-surface safety for agents operating Reply.io — high-stakes gating, scoped keys, and rate-limit etiquette.
An agent operating Reply.io can send real messages to real prospects. A successful call executes
immediately — there is no undo. These rules apply across all four access layers.
## What counts as high-stakes
Confirm with the user before any action that:
* **Sends to a prospect** — inbox replies, approving Jason drafts, one-off outreach actions
* **Starts outreach** — starting a sequence, enrolling contacts into a sequence
* **Changes ownership or reach** — reassigning contacts, blacklisting an email or a whole domain
* **Removes work irreversibly** — rejecting a Jason draft (this also removes the contact from the
sequence), deleting contacts or lists
* **Removes supervision** — switching Jason from Review to Autonomous mode
Configuration changes (assigning mailboxes, schedules, attaching offers/playbooks) are
production-affecting but not prospect-visible — usually worth confirming too.
## How each surface enforces it
| Surface | Mechanism |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **MCP** | Every tool is annotated in `tools/list`: `readOnlyHint` (31 tools) or `destructiveHint` (39). Gate on these before adding your own confirmation UX. See the [tool contract](/mcp/tool-contract) and the [high-stakes list](/mcp/agent-guide#high-stakes-tools). |
| **CLI** | No built-in gating — `reply api` sends exactly the request it is given. The caller owns the confirmation step: show the exact request, get approval, then run it. See [Using the CLI from agents](/cli/agents). |
| **Skills** | Hard confirmation gates — nothing sends, starts, or deletes without explicit approval — plus previews and verification steps. The rules live in one skill, `approval-boundaries`, shipped inside `ai-sdr-core` so no selective install can omit them. See [Skills](/skills/overview#safety-model). |
| **API** | No built-in gating — your integration is the gate. Use scoped keys (below) to bound the blast radius, and verify state with a read before retrying any timed-out write. |
A timed-out send may already have succeeded server-side — on any surface. Verify with a read
before retrying anything that reaches a prospect.
## Scoped keys bound the blast radius
API keys support granular `domain:verb` scopes (`contacts:read`, `sequences:operate`,
`inbox:write`, …) that behave identically on every surface — a key with `sequences:read` can list
sequences but not start them, whether the call arrives via REST, MCP, or the CLI.
Give an agent the narrowest scope that covers its job; issue separate keys for separate agents so
one can be revoked without stopping the others. Details: [Authentication](/api-reference/authentication).
## Rate-limit etiquette
One budget covers all surfaces — see [Rate limits](/api-reference/rate-limits).
* Honor `Retry-After` on `429`; back off exponentially on `5xx`. (The [CLI](/cli/agents) and MCP
server handle this for you.)
* Paginate instead of hammering: `top`/`skip` on MCP, and the endpoint's own paging parameters
(usually `top`/`skip`) when calling through REST or `reply api`.
* Never auto-retry a send, approval, start, or enrollment — verify first (see the
[safe-retry matrix](/mcp/tool-contract#safe-retry-matrix); the same logic applies to REST and CLI).
## Report honestly
Batch operations return per-item results (`NotProcessed`, `AffectedContactIds`, per-row import
failures). Read them and report exact counts — "reassigned 48 of 50" — and never claim an item
succeeded unless the response says so.
## Next
* [Drop-in system prompts](/agents/prompts) — these rules, pre-packaged for your agent
* [MCP tool contract](/mcp/tool-contract) — envelopes, error codes, and the retry matrix in detail
# Bulk add contacts to an account
Source: https://docs.reply.io/api-reference/accounts/bulk-add-contacts-to-an-account
/api-reference/bundled.yaml post /v3/contact-accounts/{id}/contact-links/bulk
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Adds multiple contacts to the specified account. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Bulk create accounts
Source: https://docs.reply.io/api-reference/accounts/bulk-create-accounts
/api-reference/bundled.yaml post /v3/contact-accounts/bulk
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates multiple accounts. Non-atomic — some may succeed while others fail.
Each item in the response array contains the created ID or error details.
# Bulk delete accounts
Source: https://docs.reply.io/api-reference/accounts/bulk-delete-accounts
/api-reference/bundled.yaml post /v3/contact-accounts/bulk-delete
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes multiple accounts by ID. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Bulk remove contacts from an account
Source: https://docs.reply.io/api-reference/accounts/bulk-remove-contacts-from-an-account
/api-reference/bundled.yaml post /v3/contact-accounts/{id}/contact-links/bulk-delete
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Removes multiple contacts from the specified account. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Create an account
Source: https://docs.reply.io/api-reference/accounts/create-an-account
/api-reference/bundled.yaml post /v3/contact-accounts
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates a new account.
# Delete an account
Source: https://docs.reply.io/api-reference/accounts/delete-an-account
/api-reference/bundled.yaml delete /v3/contact-accounts/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes an existing account.
# Filter accounts
Source: https://docs.reply.io/api-reference/accounts/filter-accounts
/api-reference/bundled.yaml post /v3/contact-accounts/filter
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated, filtered list of accounts using advanced filter criteria.
# Get an account
Source: https://docs.reply.io/api-reference/accounts/get-an-account
/api-reference/bundled.yaml get /v3/contact-accounts/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single account by ID.
# List accounts
Source: https://docs.reply.io/api-reference/accounts/list-accounts
/api-reference/bundled.yaml get /v3/contact-accounts
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of accounts.
# List contacts for an account
Source: https://docs.reply.io/api-reference/accounts/list-contacts-for-an-account
/api-reference/bundled.yaml get /v3/contact-accounts/{id}/contacts
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of contacts linked to the specified account.
# Update account owner
Source: https://docs.reply.io/api-reference/accounts/update-account-owner
/api-reference/bundled.yaml put /v3/contact-accounts/{id}/owner
_Requires the `contacts:write` scope (or a broader one that includes it)._
Changes the owner of an account. The new owner must be a member of the caller's team — an `ownerUserId` outside the team returns 400 `contactAccount.invalidInput` (404 is reserved for "account row missing").
# Update an account
Source: https://docs.reply.io/api-reference/accounts/update-an-account
/api-reference/bundled.yaml put /v3/contact-accounts/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Updates an existing account.
# Create a personal AI prompt
Source: https://docs.reply.io/api-reference/ai-prompts/create-a-personal-ai-prompt
/api-reference/bundled.yaml post /v3/ai-prompts
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:write` scope (or a broader one that includes it)._
Use this endpoint to save your own AI prompt for a sequence step type, so you can reuse it instead of retyping
the instructions on every step. The prompt is created as a personal prompt that you can later update or delete.
Its name must be unique among your personal prompts for that step type, and the step type is fixed at creation —
it cannot be changed afterwards.
# Delete a personal AI prompt
Source: https://docs.reply.io/api-reference/ai-prompts/delete-a-personal-ai-prompt
/api-reference/bundled.yaml delete /v3/ai-prompts/{id}
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:write` scope (or a broader one that includes it)._
Use this endpoint to remove one of your personal AI prompts once you no longer need it. Sequence steps already
configured with this prompt keep the prompt text they were saved with and continue to run. Only personal prompts
can be deleted; library prompts are read-only.
# List AI prompts
Source: https://docs.reply.io/api-reference/ai-prompts/list-ai-prompts
/api-reference/bundled.yaml get /v3/ai-prompts
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:read` scope (or a broader one that includes it)._
Use this endpoint when you need the AI prompts available for a given sequence step type. Pass the step type you
are configuring and the full set is returned in one call — there is no paging. The result mixes two kinds of
prompt: library prompts, which Reply curates and you can read but not change, and personal prompts, which you
own and can edit or delete. Each item's scope tells you which kind it is.
# Preview a comment prompt against a LinkedIn post
Source: https://docs.reply.io/api-reference/ai-prompts/preview-a-comment-prompt-against-a-linkedin-post
/api-reference/bundled.yaml post /v3/ai-prompts/preview/linkedin-post
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Use this endpoint to test a comment prompt against a real person's latest LinkedIn post. Pass the profile you
want to comment on and the connected LinkedIn account to look it up with; postText and postUrl in the response
show which post the comment was written for. One Reply credit is held before generation starts and is spent
whatever the result, including when no comment is produced, so prefer the sample preview while you are still
iterating on wording. Check outcome to see what happened: generated means commentText holds the result, while
any other value explains why the AI produced nothing.
# Preview a comment prompt against a sample post
Source: https://docs.reply.io/api-reference/ai-prompts/preview-a-comment-prompt-against-a-sample-post
/api-reference/bundled.yaml post /v3/ai-prompts/preview/sample
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Use this endpoint to try out a comment prompt against a generic sample post while you are still writing it.
Nothing is fetched from LinkedIn and no Reply credits are spent, so this is the cheap way to iterate on wording.
Pass sequenceId if the prompt text contains sequence variables and you want them filled in for the preview.
Check outcome to see whether a comment was produced: generated means commentText holds the result, while any
other value explains why the AI produced nothing. The number of sample previews you can run per day is capped.
# Update a personal AI prompt
Source: https://docs.reply.io/api-reference/ai-prompts/update-a-personal-ai-prompt
/api-reference/bundled.yaml put /v3/ai-prompts/{id}
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:write` scope (or a broader one that includes it)._
Use this endpoint to rename one of your personal AI prompts or rewrite its instructions. Send both the name and
the text — the prompt is replaced with exactly what you send, so any value you omit is not preserved. The new
name must still be unique among your personal prompts for the same step type. Only personal prompts can be
edited; library prompts are read-only.
# Get AI insights for a contact
Source: https://docs.reply.io/api-reference/ai-sdr-insights/get-ai-insights-for-a-contact
/api-reference/bundled.yaml get /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/ai-insights
**Coming soon.** This endpoint will be available by late August 2026.
Returns the AI-generated insights summary for a single (sequence, contact) pair. The summary is rendered as Markdown.
Only available after at least one personalized message has been generated for the contact in this sequence. Returns `404` otherwise.
Requires the AI SDR feature on the caller's team.
# Industries typeahead
Source: https://docs.reply.io/api-reference/ai-sdr-intent-signals/industries-typeahead
/api-reference/bundled.yaml get /v3/ai-sdr/intent-signals/industries
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of Reply industry IDs used in intent-signal configuration (e.g. `technologyUsed.industryId`, `jobHiring.industries[]`).
Distinct taxonomy from the Live Data filter-value industries — those are string-only Sales Navigator industries.
Requires the AI SDR feature on the caller's team.
# Technologies typeahead
Source: https://docs.reply.io/api-reference/ai-sdr-intent-signals/technologies-typeahead
/api-reference/bundled.yaml get /v3/ai-sdr/intent-signals/technologies
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of Reply technology slugs used in intent-signal configuration (e.g. `technologyUsed.technologies[]`). The slug is the persisted identifier; name is display-only.
Requires the AI SDR feature on the caller's team.
# Add a link
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/add-a-link
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/links
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Adds a web link to a knowledge base.
Requires the AI SDR feature on the caller's team.
# Create a knowledge base
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/create-a-knowledge-base
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a new knowledge base. Only `name` is required.
Requires the AI SDR feature on the caller's team.
# Create a reengagement card
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/create-a-reengagement-card
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a new reengagement card attached to a knowledge base.
Requires the AI SDR feature on the caller's team.
# Create a reply handler
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/create-a-reply-handler
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a new reply handler attached to a knowledge base.
Requires the AI SDR feature on the caller's team.
# Delete a document
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-a-document
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/documents/{document_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Removes a document from a knowledge base.
Requires the AI SDR feature on the caller's team.
# Delete a knowledge base
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-a-knowledge-base
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Deletes a knowledge base.
Requires the AI SDR feature on the caller's team.
# Delete a link
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-a-link
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/links/{link_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Removes a link from a knowledge base.
Requires the AI SDR feature on the caller's team.
# Delete a reengagement card
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-a-reengagement-card
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards/{reengagement_card_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Deletes a reengagement card.
Requires the AI SDR feature on the caller's team.
# Delete a reply handler
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-a-reply-handler
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers/{reply_handler_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Deletes a reply handler.
Requires the AI SDR feature on the caller's team.
# Delete reengagement card media
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-reengagement-card-media
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards/{reengagement_card_id}/media/{media_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Removes a media item from a reengagement card.
Requires the AI SDR feature on the caller's team.
# Delete reply handler media
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/delete-reply-handler-media
/api-reference/bundled.yaml delete /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers/{reply_handler_id}/media/{media_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Removes a media item from a reply handler.
Requires the AI SDR feature on the caller's team.
# Duplicate a knowledge base
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/duplicate-a-knowledge-base
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{id}/duplicate
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a copy of an existing knowledge base, including its links, documents, reply handlers, and reengagement cards.
Requires the AI SDR feature on the caller's team.
# Get a knowledge base
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/get-a-knowledge-base
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{id}
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a single knowledge base by id.
Requires the AI SDR feature on the caller's team.
# Get a reengagement card
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/get-a-reengagement-card
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards/{reengagement_card_id}
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a single reengagement card by id.
Requires the AI SDR feature on the caller's team.
# Get a reply handler
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/get-a-reply-handler
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers/{reply_handler_id}
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a single reply handler by id.
Requires the AI SDR feature on the caller's team.
# List documents
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/list-documents
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/documents
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of file documents attached to a knowledge base.
Requires the AI SDR feature on the caller's team.
# List knowledge bases
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/list-knowledge-bases
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of knowledge bases visible to the caller.
Requires the AI SDR feature on the caller's team.
# List links
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/list-links
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/links
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of web links attached to a knowledge base.
Requires the AI SDR feature on the caller's team.
# List reengagement cards
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/list-reengagement-cards
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of reengagement cards attached to a knowledge base.
Requires the AI SDR feature on the caller's team.
# List reply handlers
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/list-reply-handlers
/api-reference/bundled.yaml get /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of reply handlers attached to a knowledge base.
Requires the AI SDR feature on the caller's team.
# Update a knowledge base
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/update-a-knowledge-base
/api-reference/bundled.yaml patch /v3/ai-sdr/knowledge-bases/{id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Partial update — send only the fields you want to change. At least one field must be present.
Requires the AI SDR feature on the caller's team.
# Update a reengagement card
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/update-a-reengagement-card
/api-reference/bundled.yaml patch /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards/{reengagement_card_id}
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Partial update — send only the fields you want to change. At least one field must be present. Media is not patched here; use the `/media` sub-resource.
Requires the AI SDR feature on the caller's team.
# Update a reply handler
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/update-a-reply-handler
/api-reference/bundled.yaml patch /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers/{reply_handler_id}
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Partial update — send only the fields you want to change. At least one field must be present. Media is not patched here; use the `/media` sub-resource.
Requires the AI SDR feature on the caller's team.
# Upload a document
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/upload-a-document
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/documents
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Uploads a file document to a knowledge base. Maximum upload size: 100 MB.
Requires the AI SDR feature on the caller's team.
# Upload reengagement card media
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/upload-reengagement-card-media
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reengagement-cards/{reengagement_card_id}/media
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Attaches a media file to a reengagement card. Maximum upload size: 100 MB.
Requires the AI SDR feature on the caller's team.
# Upload reply handler media
Source: https://docs.reply.io/api-reference/ai-sdr-knowledge-bases/upload-reply-handler-media
/api-reference/bundled.yaml post /v3/ai-sdr/knowledge-bases/{knowledge_base_id}/reply-handlers/{reply_handler_id}/media
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Attaches a media file to a reply handler. Maximum upload size: 100 MB.
Requires the AI SDR feature on the caller's team.
# Create an offer
Source: https://docs.reply.io/api-reference/ai-sdr-offers/create-an-offer
/api-reference/bundled.yaml post /v3/ai-sdr/offers
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a new offer. Only `name` is strictly required; all other fields default to empty.
Requires the AI SDR feature on the caller's team.
# Delete an offer
Source: https://docs.reply.io/api-reference/ai-sdr-offers/delete-an-offer
/api-reference/bundled.yaml delete /v3/ai-sdr/offers/{id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Deletes an offer.
Requires the AI SDR feature on the caller's team.
# Duplicate an offer
Source: https://docs.reply.io/api-reference/ai-sdr-offers/duplicate-an-offer
/api-reference/bundled.yaml post /v3/ai-sdr/offers/{id}/duplicate
**Coming soon.** This endpoint will be available by early August 2026.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a copy of an existing offer, including its company-context fields (ICP, reason for outreach, case studies, pain points, proof points, value propositions, and calls to action). The copy is returned in the response body.
Requires the AI SDR feature on the caller's team.
# Generate an offer
Source: https://docs.reply.io/api-reference/ai-sdr-offers/generate-an-offer
/api-reference/bundled.yaml post /v3/ai-sdr/offers/generate
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Generates an offer from a prompt or company URL, optionally seeded with previously uploaded attachments. The generated offer is persisted directly — clients do not need a separate create call.
Returns a background job to poll via `GET /v3/background-jobs/{jobId}`; on completion its `jsonDataResult` carries the generated offer (`offerId` plus the same company-context fields returned by `GET /v3/ai-sdr/offers/{id}`).
Requires the AI SDR feature on the caller's team.
# Get an offer
Source: https://docs.reply.io/api-reference/ai-sdr-offers/get-an-offer
/api-reference/bundled.yaml get /v3/ai-sdr/offers/{id}
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a single offer by id.
Requires the AI SDR feature on the caller's team.
# List offers
Source: https://docs.reply.io/api-reference/ai-sdr-offers/list-offers
/api-reference/bundled.yaml get /v3/ai-sdr/offers
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of offers visible to the caller.
Requires the AI SDR feature on the caller's team.
# Update an offer
Source: https://docs.reply.io/api-reference/ai-sdr-offers/update-an-offer
/api-reference/bundled.yaml patch /v3/ai-sdr/offers/{id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Partial update — send only the fields you want to change. Array fields, if present, fully replace the current value (no item-level merge). At least one field must be present.
Requires the AI SDR feature on the caller's team.
# Upload an attachment for offer generation
Source: https://docs.reply.io/api-reference/ai-sdr-offers/upload-an-attachment-for-offer-generation
/api-reference/bundled.yaml post /v3/ai-sdr/offers/generation-attachments
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Uploads a single attachment whose id can be passed in `attachmentIds` of `POST /v3/ai-sdr/offers/generate` to seed the generation. Maximum size 10 MB; supported formats are `.txt`, `.pdf`, `.doc`, `.docx`, and `.rtf`.
Requires the AI SDR feature on the caller's team.
# Get pending approval for a contact
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/get-pending-approval-for-a-contact
/api-reference/bundled.yaml get /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/approval
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns the pending AI-generated message for a single (sequence, contact) pair, including the prior message thread for context.
Requires the AI SDR feature on the caller's team.
# List pending approvals
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/list-pending-approvals
/api-reference/bundled.yaml get /v3/ai-sdr/approvals
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of AI-generated messages awaiting human approval before being sent.
Requires the AI SDR feature on the caller's team.
# Regenerate a pending approval
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/regenerate-a-pending-approval
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/approval/regenerate
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Starts a background job that regenerates the AI-generated message for a single (sequence, contact) pair, and returns the job id. Optionally pass freeform `recommendations` to steer the regeneration.
Poll `GET /v3/background-jobs/{jobId}` for status; when the job finishes, its result holds the regenerated `subject` and `body`.
Requires the AI SDR feature on the caller's team.
# Reject (delete) a pending approval
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/reject-delete-a-pending-approval
/api-reference/bundled.yaml delete /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/approval
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Discards the pending AI-generated message for a contact and removes the contact from the sequence. The queued draft and its personalization step are deleted.
Requires the AI SDR feature on the caller's team.
# Send a batch of pending approvals
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/send-a-batch-of-pending-approvals
/api-reference/bundled.yaml post /v3/ai-sdr/approvals/send-bulk
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Approves and sends the queued AI-generated messages for up to 20 (sequence, contact) pairs in one call. Bodies and subjects cannot be overridden in bulk — use the per-contact endpoint for that.
Requires the AI SDR feature on the caller's team.
# Send a pending approval
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/send-a-pending-approval
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/approval/send
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Approves and sends the queued AI-generated message for a single (sequence, contact) pair. Optionally override the draft body and/or subject before sending.
Requires the AI SDR feature on the caller's team.
# Submit feedback on a pending approval
Source: https://docs.reply.io/api-reference/ai-sdr-pending-approvals/submit-feedback-on-a-pending-approval
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/approval/feedback
**Coming soon.** This endpoint will be available by late August 2026.
Records a thumbs-up / thumbs-down reaction on the queued AI-generated message, with optional dislike reasons and a freeform comment. Used as a training signal for the AI SDR. No step identifier is needed — the approval is unique per (sequence, contact).
Requires the AI SDR feature on the caller's team.
# Create a playbook
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/create-a-playbook
/api-reference/bundled.yaml post /v3/ai-sdr/playbooks
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a new playbook in the `organization` or `team` scope. `global` playbooks are Reply-curated and cannot be created via the API.
Requires the AI SDR feature on the caller's team.
# Delete a playbook
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/delete-a-playbook
/api-reference/bundled.yaml delete /v3/ai-sdr/playbooks/{id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Deletes a playbook. `global` playbooks cannot be deleted via the API.
Requires the AI SDR feature on the caller's team.
# Delete a style file
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/delete-a-style-file
/api-reference/bundled.yaml delete /v3/ai-sdr/playbooks/{playbook_id}/style-files/{style_file_id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Removes a style file from a playbook.
Requires the AI SDR feature on the caller's team.
# Duplicate a playbook
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/duplicate-a-playbook
/api-reference/bundled.yaml post /v3/ai-sdr/playbooks/{id}/duplicate
**Coming soon.** This endpoint will be available by early August 2026.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a copy of an existing playbook and returns the new playbook in the response body. `global` playbooks can also be duplicated.
Requires the AI SDR feature on the caller's team.
# Get a playbook
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/get-a-playbook
/api-reference/bundled.yaml get /v3/ai-sdr/playbooks/{id}
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a single playbook by its composite id (`g-N` / `o-N` / `t-N`).
Requires the AI SDR feature on the caller's team.
# List playbooks
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/list-playbooks
/api-reference/bundled.yaml get /v3/ai-sdr/playbooks
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns a paginated list of playbooks visible to the caller. When `type` is omitted, results from all three scopes (`global`, `organization`, `team`) are merged.
Requires the AI SDR feature on the caller's team.
# Update a playbook
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/update-a-playbook
/api-reference/bundled.yaml patch /v3/ai-sdr/playbooks/{id}
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Partial update — send only the fields you want to change. `type` is immutable.
Requires the AI SDR feature on the caller's team.
# Upload a style file
Source: https://docs.reply.io/api-reference/ai-sdr-playbooks/upload-a-style-file
/api-reference/bundled.yaml post /v3/ai-sdr/playbooks/{playbook_id}/style-files
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Uploads a style file (reference document) to a playbook. Style files inform tone and voice when the playbook is applied. Maximum upload size: 100 MB.
Requires the AI SDR feature on the caller's team.
# Personalize the preview for a contact
Source: https://docs.reply.io/api-reference/ai-sdr-sequence-preview/personalize-the-preview-for-a-contact
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/preview/personalize
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Kicks off (or re-runs) AI personalization for a single contact in an AI SDR sequence.
Personalization runs step-by-step in the background. The whole step chain for the contact is tracked by a single background job: the response returns its `jobId`, and progress advances as `personalizedSteps / totalSteps`. Poll the generic `GET /v3/background-jobs/{id}` endpoint for status and progress; on completion the job's `jsonDataResult` carries `{ sequenceId, contactId, personalizedSteps, totalSteps }`. A chain that ended early (for example, the contact was removed mid-chain) reports `personalizedSteps < totalSteps`.
Requires the AI SDR feature on the caller's team.
# Submit feedback on a preview step
Source: https://docs.reply.io/api-reference/ai-sdr-sequence-preview/submit-feedback-on-a-preview-step
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/contacts/{contact_id}/preview/steps/{step_id}/feedback
**Coming soon.** This endpoint will be available by late August 2026.
Records a thumbs-up / thumbs-down reaction on the AI-generated message for a specific step in a contact's preview, with optional dislike reasons and a freeform comment. Used as a training signal for the AI SDR.
Requires the AI SDR feature on the caller's team.
# Attach a playbook to the sequence
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/attach-a-playbook-to-the-sequence
/api-reference/bundled.yaml put /v3/sequences/ai-sdr/{sequence_id}/playbook
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Attaches a playbook to the sequence by composite id. Replaces any previously-attached playbook.
Requires the AI SDR feature on the caller's team.
# Connect a knowledge base to the sequence
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/connect-a-knowledge-base-to-the-sequence
/api-reference/bundled.yaml put /v3/sequences/ai-sdr/{sequence_id}/knowledge-base
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Connects a knowledge base to the sequence by id. Replaces any previously-connected knowledge base.
Requires the AI SDR feature on the caller's team.
# Create an AI SDR sequence
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/create-an-ai-sdr-sequence
/api-reference/bundled.yaml post /v3/sequences/ai-sdr
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Creates a new AI SDR sequence with its full AI SDR settings payload. The sequence is seeded either from a saved offer (`offerId`) or from inline offer-context inputs (`offerGenerationInputs`) — exactly one is required.
Sequence steps are generated by the AI SDR — clients do not pass `steps` here. Generic sequence fields (`name`, `scheduleId`, `settings`, `emailAccounts`, `linkedInAccounts`) are accepted and applied after the AI SDR-specific setup.
Requires the AI SDR feature on the caller's team.
# Detach the playbook from the sequence
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/detach-the-playbook-from-the-sequence
/api-reference/bundled.yaml delete /v3/sequences/ai-sdr/{sequence_id}/playbook
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Detaches the playbook attached to the sequence. Returns 404 if no playbook is attached.
Requires the AI SDR feature on the caller's team.
# Disable autopilot
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/disable-autopilot
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/autopilot/disable
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Disables autopilot on the sequence. No-op when autopilot is already disabled.
Requires the AI SDR feature on the caller's team.
# Disconnect the knowledge base from the sequence
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/disconnect-the-knowledge-base-from-the-sequence
/api-reference/bundled.yaml delete /v3/sequences/ai-sdr/{sequence_id}/knowledge-base
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Disconnects the knowledge base connected to the sequence. Returns 404 if no knowledge base is connected.
Requires the AI SDR feature on the caller's team.
# Enable autopilot
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/enable-autopilot
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/autopilot/enable
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Enables autopilot on the sequence. No-op when autopilot is already enabled.
Requires the AI SDR feature on the caller's team.
# Force-start an autopilot search
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/force-start-an-autopilot-search
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/autopilot/force-start
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Force-starts an autopilot contact search for the sequence, bypassing the normal schedule. Autopilot must already be enabled; rejected with 409 otherwise.
Requires the AI SDR feature on the caller's team.
# Get the sequence's knowledge base
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/get-the-sequences-knowledge-base
/api-reference/bundled.yaml get /v3/sequences/ai-sdr/{sequence_id}/knowledge-base
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns the knowledge base connected to the sequence. Each sequence is connected to at most one knowledge base.
Requires the AI SDR feature on the caller's team.
# Get the sequence's playbook
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/get-the-sequences-playbook
/api-reference/bundled.yaml get /v3/sequences/ai-sdr/{sequence_id}/playbook
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns the playbook attached to the sequence. Each sequence is attached to at most one playbook.
Requires the AI SDR feature on the caller's team.
# Preview autopilot results
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/preview-autopilot-results
/api-reference/bundled.yaml post /v3/sequences/ai-sdr/{sequence_id}/autopilot/preview
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Previews the expected autopilot results for the sequence. Clients pass the autopilot filter config in the body — they are NOT required to save it to the sequence first (supports try-before-save in the filter UI).
Returns a background job to poll via `GET /v3/background-jobs/{id}`; on completion its `jsonDataResult` carries `{ contactsCount, companiesCount, sampleContacts }`, where `sampleContacts` is a capped sample of matching contacts.
Requires the AI SDR feature on the caller's team.
# Read AI SDR sequence settings
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/read-ai-sdr-sequence-settings
/api-reference/bundled.yaml get /v3/sequences/ai-sdr/{sequence_id}/settings
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:read` scope (or a broader one that includes it)._
Returns the AI SDR-specific settings of a sequence. Mirrors the shape of the create/patch body. Generic sequence fields (name, schedule, etc.) are read via `GET /v3/sequences/{id}`.
Requires the AI SDR feature on the caller's team.
# Set generated step types
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/set-generated-step-types
/api-reference/bundled.yaml put /v3/sequences/ai-sdr/{sequence_id}/generated-step-types
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Replaces the step channels the AI SDR is allowed to generate, and triggers regeneration of the sequence's steps using the new mix.
Rejected with 409 once the sequence has already acted on contacts (sent emails or LinkedIn messages, generated tasks, etc.).
Requires the AI SDR feature on the caller's team.
# Set the approval mode
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/set-the-approval-mode
/api-reference/bundled.yaml put /v3/sequences/ai-sdr/{sequence_id}/approval-mode
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:operate` scope (or a broader one that includes it)._
Sets the sequence's approval mode — whether AI-generated messages require human approval before sending. Settable only via this dedicated PUT (or at create time); NOT in the AI SDR sequence PATCH.
When disabling (`enabled` = `false`), `approveAllPendingContacts` may be set to `true` to immediately approve all currently-pending contacts. `approveAllPendingContacts` is rejected when enabling.
Requires the AI SDR feature on the caller's team.
# Update AI SDR sequence settings
Source: https://docs.reply.io/api-reference/ai-sdr-sequences/update-ai-sdr-sequence-settings
/api-reference/bundled.yaml patch /v3/sequences/ai-sdr/{sequence_id}/settings
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Partial update of AI SDR-specific sequence settings (generation inputs, tone of voice, personalization points, provider LLM, autopilot config, intent signals).
PATCH semantics: a field absent from the body is skipped; a field explicitly set to `null` is rejected. `intentSignals`, when present, fully replaces the current list. `autopilot.filters`, when present, fully replaces the current filters object. At least one field must be present.
Out of scope for this PATCH (use the dedicated sub-resources instead): name and other generic sequence fields, approval mode, generated step types, attached playbook, connected knowledge base.
Requires the AI SDR feature on the caller's team.
# Start a strategist run
Source: https://docs.reply.io/api-reference/ai-sdr-strategist/start-a-strategist-run
/api-reference/bundled.yaml post /v3/ai-sdr/strategist/start
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `ai-sdr:write` scope (or a broader one that includes it)._
Starts an AI Strategist run for the caller's team. The strategist generates sequence ideas and creates a sequence for each one.
Returns a background job to poll via `GET /v3/background-jobs/{jobId}`; on completion its `jsonDataResult` carries `{ sequenceIds }` — the ids of the sequences created from the generated ideas.
Requires the AI SDR feature on the caller's team.
# List AI web searches for a sequence
Source: https://docs.reply.io/api-reference/ai-sdr-web-search/list-ai-web-searches-for-a-sequence
/api-reference/bundled.yaml get /v3/ai-web-search/searches
**Coming soon.** This endpoint will be available by late August 2026.
Returns AI web searches started for a given sequence.
# Start an AI web search
Source: https://docs.reply.io/api-reference/ai-sdr-web-search/start-an-ai-web-search
/api-reference/bundled.yaml post /v3/ai-web-search/start
**Coming soon.** This endpoint will be available by late August 2026.
Kicks off an AI-driven web search to find contacts for a sequence.
# Authentication
Source: https://docs.reply.io/api-reference/authentication
Learn how to authenticate requests and manage API keys.
The Reply API uses Bearer token authentication. Every request must include your API key in the `Authorization` header.
## Get your API key
Sign in to your [Reply.io dashboard](https://run.reply.io).
Go to **Settings → API Key**.
Create a new API key or copy an existing one. Treat your API key like a password.
## Authenticate a request
Include your API key as a Bearer token:
```http theme={null}
GET /v3/whoami HTTP/1.1
Host: api.reply.io
Authorization: Bearer YOUR_API_KEY
```
### curl example
```bash theme={null}
curl https://api.reply.io/v3/whoami \
-H "Authorization: Bearer YOUR_API_KEY"
```
## Verify your setup
Call `/v3/whoami` to verify that your API key works. A successful response returns information about the authenticated user:
```json theme={null}
{ "userId": 12345 }
```
## Unauthorized response
If the `Authorization` header is missing, invalid, or contains a revoked API key, the API returns `401 Unauthorized` with an empty response body.
The response includes a `WWW-Authenticate` header:
```text theme={null}
HTTP/1.1 401 Unauthorized
Content-Length: 0
WWW-Authenticate: Bearer
```
Do not expect a JSON error response. Use the status code and headers to handle authentication failures.
This empty-body `401` covers **key** failures — a missing, malformed, or revoked key. A request that presents a valid Team or Organization key but cannot resolve an acting user is different: it returns an `application/problem+json` body with a `code` (see [Organization API key → Rejection responses](#rejection-responses)).
## Scopes
Every API key is granted a set of **scopes**. A scope is a permission such as `contacts:read` that controls which endpoints the key can call. Each endpoint requires a single **minimal scope**, shown as a small note near the top of that endpoint's reference description (below any Beta notice) — for example:
*Requires the `contacts:read` scope (or a broader one that includes it).*
Endpoints that need no particular permission say **No scope required** instead — any valid API key can call them (for example, `GET /v3/whoami`).
Scopes are configured **per API key** in **Settings → API Key**. Grant a key only the scopes its integration needs.
### Scope format
A scope is written as `domain:verb`:
* **`domain`** — the area of the API the permission applies to (for example `contacts`, `sequences`, `inbox`).
* **`verb`** — the level of access within that domain:
* **`read`** — read data.
* **`write`** — create, update, or delete data.
* **`operate`** — trigger runtime actions (for example, push a contact to a sequence, start or pause a sequence, or reply to an inbox thread).
### How a key satisfies a required scope
A key can hold a required scope directly, or hold a **broader** scope that includes it:
* **`write` and `operate` each also satisfy a `read` requirement** — a key that can write or operate in a domain can also read in that domain. (`write` and `operate` are independent of each other; neither one includes the other.)
* A **verb wildcard** `domain:*` satisfies any verb in that domain — for example, `contacts:*` satisfies `contacts:read`, `contacts:write`, and `contacts:operate`.
* The **global wildcard** `*:*` satisfies every scope.
For example, an endpoint that requires `contacts:read` can be called by a key granted any of `contacts:read`, `contacts:write`, `contacts:operate`, `contacts:*`, or `*:*`.
### Available scopes
| Domain | Scopes | Covers |
| ----------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `settings` | `settings:read`, `settings:write` | Read and modify account settings. |
| `contacts` | `contacts:read`, `contacts:write`, `contacts:operate` | Read contacts, accounts, and related data; create/update/delete them; and trigger contact-level runtime actions (push to sequence, enrich, etc.). |
| `sequences` | `sequences:read`, `sequences:write`, `sequences:operate` | Read sequences, steps, variants, and templates; create/update/delete them; and start, pause, or resume running sequences. |
| `channels` | `channels:read`, `channels:write`, `channels:operate` | Read connected email and LinkedIn channels, manage their configuration, and connect/disconnect/re-authorize them. |
| `inbox` | `inbox:read`, `inbox:write`, `inbox:operate` | Read inbox threads, messages, and categories; manage inbox configuration; and reply, mark, or categorize threads. |
| `tasks` | `tasks:read`, `tasks:write`, `tasks:operate` | Read tasks, create/update/delete them, and complete or execute them. |
| `reporting` | `reporting:read` | Read reporting data. **Read-only** — there is no `write` or `operate` verb. |
| `webhooks` | `webhooks:read`, `webhooks:write`, `webhooks:operate` | Read webhook subscriptions and deliveries, manage subscriptions, and test/enable/disable them. |
| `ai-sdr` | `ai-sdr:read`, `ai-sdr:write`, `ai-sdr:operate` | Read AI-SDR campaigns and configuration, manage them, and run/stop/manage their runtime state. |
| `other` | `other:read`, `other:write`, `other:operate` | Read, manage, or trigger miscellaneous resources not covered by a domain-specific scope (background jobs, etc.). |
A separate `legacy:use` scope governs the older, non-V3 API and does not apply to the V3 endpoints documented here.
## Special API keys
In addition to user API keys, Reply supports two centralized authentication methods:
* **Team keys** for a single team/workspace
* **Organization API keys** for all team/workspaces in an organization
### Team key — team level
A Team key provides team/workspace-wide access.
Unlike a user API key, which always acts as a single user, a Team key can make requests as any user in the team/workspace.
Common use cases include:
* Team-wide automation
* Cross-user reporting
* Bulk operations
#### Permissions and access control
* Only the team/workspace owner can generate and view the Team key.
* The key must be created manually.
* A Team key is unique to its team/workspace.
For a credential that spans every team/workspace in an organization, see [Organization API key](#organization-api-key-—-organization-level) below.
#### Acting on behalf of a user
Use one of the following headers:
* `X-User-Id`
* `X-User-Email`
You only need one header.
#### Behavior and permission model
Team keys use the same permission model as user API keys.
| Request | Behavior |
| ------------------------- | ------------------------------------- |
| Team key only | Acts as the team/workspace owner |
| Team key + `X-User-Id` | Acts as the specified user (by ID) |
| Team key + `X-User-Email` | Acts as the specified user (by email) |
In all cases, the permissions of the resolved user are applied.
#### Important notes
* If an impersonation header is provided, the request runs with that user's permissions.
* If no impersonation header is provided, the request runs with the team/workspace owner's permissions.
* A Team key does not grant permissions beyond those of the resolved user.
### Organization API key — organization level
An **Organization API** key provides centralized access across every team/workspace in an organization.
Unlike a Team key, which is limited to a single team/workspace, an Organization API key can act on behalf of users across all team/workspaces owned by the organization.
#### Availability
* Organization-scoped
* Works across all team/workspaces in the organization
* Multiple keys can be created
* Keys can be named, revoked, and rotated independently
#### Permissions and access control
By default, Organization Owners can create, view, and revoke organization API keys.
Other organization users can be granted role with permission 'Manage organization API keys' to manage organization API keys.
Keys are managed in: **Settings → API keys**
#### Acting on behalf of a user
Organization API keys always require impersonation.
Use one of the following:
* `X-USER-ID`
* `X-User-Email` together with `X-TEAM-ID`
If no impersonation header is provided, the request is rejected.
If `X-User-Email` is provided without `X-TEAM-ID`, the request is rejected.
Example:
```http theme={null}
GET /v3/whoami HTTP/1.1
Host: api.reply.io
Authorization: Bearer YOUR_ORG_API_KEY
X-USER-ID: 12345
```
#### Behavior and permission model
| Request | Behavior |
| ----------------------------------------------- | ------------------------------------------------------------ |
| Organization key + `X-USER-ID` | Acts as the user with that ID |
| Organization key + `X-User-Email` + `X-TEAM-ID` | Acts as the user with that email in the given team/workspace |
| Organization key without impersonation | Rejected — `403 USER_REQUIRED` |
| User belongs to a different organization | Rejected — `403 TEAM_NOT_ACCESSIBLE` |
In all accepted requests, the permissions of the resolved user are applied.
#### Rejection responses
A rejected impersonation request returns `application/problem+json` with a stable `code` you can branch on:
| Condition | Status | `code` |
| --------------------------------------------------------------- | ------ | --------------------- |
| No impersonation header (or `X-User-Email` without `X-TEAM-ID`) | `403` | `USER_REQUIRED` |
| Resolved user does not exist | `401` | `USER_NOT_FOUND` |
| Resolved user belongs to a different organization | `403` | `TEAM_NOT_ACCESSIBLE` |
```json theme={null}
{
"title": "Access forbidden",
"status": 403,
"code": "USER_REQUIRED",
"detail": "This credential requires an acting-user header."
}
```
This is distinct from an invalid or expired key, which returns `401` with an empty body (see [Unauthorized response](#unauthorized-response)).
#### Important notes
* Every request runs with the permissions of the resolved user.
* The resolved user must belong to the same organization that issued the key.
* An Organization API key does not grant permissions beyond those of the resolved user.
## Keep your API key safe
* Never commit API keys to source control.
* Never share API keys in public chats or screenshots.
* Rotate your API key from **Settings → API Key** if you think it has been exposed.
# Introduction
Source: https://docs.reply.io/api-reference/introduction
Reply is an AI-powered sales engagement platform. The Reply API gives you access to the platform's core features, including contacts, multichannel sequences, the unified inbox, scheduling, reporting, and webhooks. Use it to manage prospecting and outreach from your own systems.
Reply is an AI-powered sales engagement platform. The Reply API (v3) gives you access to the platform through a REST API, so you can automate the same workflows available in the Reply dashboard from your own applications, scripts, or AI agents.
## At a glance
Every request is an HTTPS call to a `/v3` endpoint and includes your API key in the `Authorization` header. This example returns information about the authenticated user and confirms that your credentials work:
```bash theme={null}
curl https://api.reply.io/v3/whoami \
-H "Authorization: Bearer YOUR_API_KEY"
```
* **Base URL**: `https://api.reply.io`, with all endpoints under the `/v3` prefix (for example, `/v3/sequences`).
* **Authentication**: Bearer token in the `Authorization` header on every request.
* **Format**: JSON request and response bodies. Errors use `application/problem+json`.
* **Rate limits**: 100 requests per minute and 3,000 requests per hour, per user.
## What you can do
The API covers every major area of the platform. Each area has its own group of endpoints in the API reference.
| Area | What you can manage |
| ------------------------ | ----------------------------------------------------------------------------- |
| **Contacts & data** | Contacts, contact lists, custom fields, and blacklist rules |
| **Sequences** | Multichannel sequences across email, LinkedIn, calls, and manual tasks |
| **Accounts & templates** | Connected email and LinkedIn accounts, plus shared templates |
| **Inbox** | Replies handled through the unified Inbox |
| **Scheduling** | Sending schedules and holiday calendars that control send times |
| **Tasks** | Manual tasks assigned to your team |
| **Reporting** | Performance reports across all channels |
| **Automation** | Webhooks for real-time events and background jobs for long-running operations |
## API conventions
The API follows consistent patterns across endpoints.
### REST
Resource URLs use plural nouns and standard HTTP methods:
* `GET` to read
* `POST` to create
* `PATCH` to update
* `DELETE` to remove
Request and response bodies use JSON.
### Errors
All `4xx` and `5xx` responses use `application/problem+json`.
Business errors include a stable code in `.` format, so you can handle them programmatically:
```json theme={null}
{
"title": "Bad Request",
"status": 400,
"detail": "Unknown webhook event 'email_clicked'.",
"code": "webHook.invalidEvent"
}
```
Validation errors (`400 Bad Request`) also include an `errors[]` array. Each entry identifies the invalid field using a JSON Pointer.
### Asynchronous jobs
Long-running operations, such as email validation and large bulk imports, return `202 Accepted` with a `Location` header that points to `/v3/background-jobs/{id}`.
Use the Background Jobs endpoint to check job status or cancel a job.
## Authentication
v3 uses Bearer token authentication. Every request must include an `Authorization: Bearer ` header.
If the token is missing, invalid, or revoked, the API returns `401 Unauthorized` with a `WWW-Authenticate: Bearer` header and an empty response body.
See **Authentication** for instructions on generating an API key, making your first authenticated request, using Master and Organization keys, and verifying credentials with `/v3/whoami`.
## Rate limits
To protect the platform, requests are limited to 100 per minute and 3,000 per hour, per user.
When you exceed a limit, the API returns `429 Too Many Requests` and includes a `Retry-After` header that tells you when to retry.
Reporting and statistics endpoints have stricter limits. See **Rate Limits** for details.
## Versioning
v3 is the current supported version and should be used for all new integrations.
API v1 and v2 remain available but are no longer supported and will be deprecated in the future. Their documentation is available at `apidocs.reply.io`.
## Connect an AI agent
Reply MCP connects Reply to Claude, Cursor, and other MCP-compatible AI clients, allowing agents to use Reply without making direct HTTP requests.
Connect Reply to your AI client.
Get an API key and make your first authenticated request.
## Get help
Questions, feedback, or stuck on an endpoint? The Reply developer community on Slack is the
fastest way to reach us and other builders.
Ask questions, share feedback, and discuss the API — no Reply account required.
# Rate limits
Source: https://docs.reply.io/api-reference/rate-limits
Rate limits, 429 responses, and retry guidance.
The Reply API limits the number of requests each user can make. When a limit is exceeded, the API returns `429 Too Many Requests`.
## Limits
* **100 requests per minute**
* **3,000 requests per hour**
Limits are applied per user. Requests made by multiple applications, scripts, or services on behalf of the same user count toward the same quota.
## Stricter endpoints
Some endpoints have lower hourly limits than the default quotas, including:
* **Reporting:** `/v3/reporting/*`
* **Sequence statistics:**
* `/v3/sequences/{id}/stats`
* `/v3/sequences/stats`
Analytics and reporting endpoints may return `429 Too Many Requests` before the general limits are reached.
## The 429 response
When a rate limit is exceeded, the API returns:
* **Status:** `429 Too Many Requests`
* **Headers:**
* `Retry-After: ` — seconds to wait before the quota window resets
* `Content-Type: application/problem+json`
## Handling 429
Wait for the number of seconds specified in the `Retry-After` header before retrying the request.
For bulk operations, process requests sequentially or use a small concurrency limit instead of sending many requests in parallel.
# Platform overview
Source: https://docs.reply.io/getting-started
One platform, four access layers — API, MCP, CLI, and Skills over the same Reply.io workspace.
Reply.io exposes one sales-engagement platform through four access layers. They share the same
account, the same [API key](/api-reference/authentication), the same object model, and the same
[rate limits](/api-reference/rate-limits) — pick the surface that fits how you (or your agent) work.
## Pick your surface
The exhaustive surface. Every endpoint, schema, and webhook — for production integrations.
70 annotated tools for MCP clients — Claude, Cursor, and custom agents.
`reply` in your terminal — sign in once, then call any v3 endpoint as yourself.
Outbound expertise and guardrails as markdown skill packs for your AI assistant.
Not sure which to use? See [Choose your interface](/agents/choose-your-interface).
## Go further
Task-based guides — from importing contacts to building an AI SDR.
Agentic capabilities, safety rules, and drop-in prompts.
Download the OpenAPI spec or import the Postman collection.
Ask questions, share feedback, and request features.
# OpenAPI & Postman
Source: https://docs.reply.io/openapi-postman
OpenAPI specs and Postman collections for the Reply API.
## OpenAPI
The complete Reply API specification is available in OpenAPI 3.1 format.
Use it to:
* Generate client libraries
* Explore the API structure
* Import the API into tools such as Postman
## OpenAPI Spec
Open the complete OpenAPI specification (YAML)
## Postman
Import the Reply API into Postman to explore and test it.
### What's included
Importing the OpenAPI spec gives you:
* Every Reply API endpoint, grouped by tag — Sequences, Contacts, Email Accounts, Templates, and more
* Request and response schemas for each operation
* A `baseUrl` collection variable, taken from the spec
* Collection-level Bearer authentication, ready for your API key
## Setup
1. Sign in to [Reply.io account](https://run.reply.io)
2. Go to **Settings → API Key**.
3. Create a new API key or copy an existing one.
1. Open Postman.
2. Click **Import**, then select **Link**.
3. Paste the OpenAPI spec URL below, then click **Continue** and **Import**.
```text theme={null}
https://docs.reply.io/api-reference/bundled.yaml
```
Postman builds a collection from the spec.
The import sets `baseUrl` from the spec. Add your API key as an environment variable:
| Variable | Description | Example |
| -------- | --------------------- | -------------- |
| `apiKey` | Your Reply.io API key | `************` |
Include your API key as a Bearer token:
```text theme={null}
Authorization: Bearer {{apiKey}}
```
## Test your setup
Send a request to:
```bash theme={null}
GET https://api.reply.io/v3/whoami
Authorization: Bearer your_api_key_here
```
If authentication succeeds, the API returns information about the authenticated user.
# Slack community
Source: https://docs.reply.io/slack
Connect with other developers, ask questions, and share feedback.
Join the Reply developer community on Slack to ask questions, share feedback, and discuss the API.
No Reply account required.
# Add accounts to an account list
Source: https://docs.reply.io/api-reference/account-lists/add-accounts-to-an-account-list
/api-reference/bundled.yaml post /v3/contact-account-lists/{id}/add-accounts
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Adds accounts to the specified account list, preserving any other list memberships.
This is a non-atomic operation: successfully added accounts are not rolled back if some fail.
The response contains only the failed items with their error details.
# Create an account list
Source: https://docs.reply.io/api-reference/account-lists/create-an-account-list
/api-reference/bundled.yaml post /v3/contact-account-lists
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates a new account list for the current user's team. Names must be 3–128 characters and unique per team.
# Delete an account list
Source: https://docs.reply.io/api-reference/account-lists/delete-an-account-list
/api-reference/bundled.yaml delete /v3/contact-account-lists/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes an existing account list. The accounts themselves are not deleted —
only their membership in this list is removed.
# Get an account list
Source: https://docs.reply.io/api-reference/account-lists/get-an-account-list
/api-reference/bundled.yaml get /v3/contact-account-lists/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single account list by ID.
# List account lists
Source: https://docs.reply.io/api-reference/account-lists/list-account-lists
/api-reference/bundled.yaml get /v3/contact-account-lists
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of account lists for the current user's team.
# Move accounts to an account list
Source: https://docs.reply.io/api-reference/account-lists/move-accounts-to-an-account-list
/api-reference/bundled.yaml post /v3/contact-account-lists/{id}/move-accounts
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Moves accounts to the specified account list, removing them from any other lists they belong to.
This is a non-atomic operation: successfully moved accounts are not rolled back if some fail.
The response contains only the failed items with their error details.
# Update an account list
Source: https://docs.reply.io/api-reference/account-lists/update-an-account-list
/api-reference/bundled.yaml put /v3/contact-account-lists/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Updates an existing account list name. Names must be 3–128 characters and unique per team.
# Upload attachment
Source: https://docs.reply.io/api-reference/attachments/upload-attachment
/api-reference/bundled.yaml post /v3/attachments
_Requires the `other:write` scope (or a broader one that includes it)._
Uploads a single file as a `multipart/form-data` request and returns its attachment id. Use the returned `id` in `attachmentIds` arrays on email-template, sequence-step, and inbox-message endpoints to attach the file to outbound communications.
# Upload voice attachment
Source: https://docs.reply.io/api-reference/attachments/upload-voice-attachment
/api-reference/bundled.yaml post /v3/attachments/voice
Uploads an audio file as a `multipart/form-data` request, converts it to LinkedIn voice format, and returns the attachment metadata including the duration. Use the returned `id` to attach the voice message to LinkedIn voice outreach endpoints.
# Cancel a background job
Source: https://docs.reply.io/api-reference/background-jobs/cancel-a-background-job
/api-reference/bundled.yaml post /v3/background-jobs/{jobId}/cancel
_Requires the `other:operate` scope (or a broader one that includes it)._
Requests cancellation of a background job. Idempotent — cancelling an already-terminal job returns 204 without effect on the worker. The request body is optional; if provided, the `reason` is recorded alongside the cancellation event.
# Get a background job
Source: https://docs.reply.io/api-reference/background-jobs/get-a-background-job
/api-reference/bundled.yaml get /v3/background-jobs/{jobId}
_Requires the `other:read` scope (or a broader one that includes it)._
Returns the full record for a background job, including raw input (`jsonData`) and result (`jsonDataResult`) JSON payloads when available.
# List background jobs
Source: https://docs.reply.io/api-reference/background-jobs/list-background-jobs
/api-reference/bundled.yaml get /v3/background-jobs
_Requires the `other:read` scope (or a broader one that includes it)._
Returns a paginated list of background jobs created by the current user. Optionally filter by category suffix.
# Bulk create domain blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/bulk-create-domain-blacklist-rules
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/domains/bulk
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates multiple domain blacklist rules. Non-atomic — some may succeed while others fail.
Each item in the response array contains the created ID or error details.
# Bulk create email blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/bulk-create-email-blacklist-rules
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/emails/bulk
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates multiple email blacklist rules. Non-atomic — some may succeed while others fail.
Each item in the response array contains the created ID or error details.
# Bulk create email exception blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/bulk-create-email-exception-blacklist-rules
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/email-exceptions/bulk
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates multiple email exception blacklist rules. Non-atomic — some may succeed while others fail.
Each item in the response array contains the created ID or error details.
# Bulk delete domain blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/bulk-delete-domain-blacklist-rules
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/domains/bulk-delete
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes multiple domain blacklist rules by ID. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Bulk delete email blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/bulk-delete-email-blacklist-rules
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/emails/bulk-delete
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes multiple email blacklist rules by ID. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Bulk delete email exception blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/bulk-delete-email-exception-blacklist-rules
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/email-exceptions/bulk-delete
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes multiple email exception blacklist rules by ID. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Create a domain blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/create-a-domain-blacklist-rule
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/domains
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates a new domain blacklist rule.
# Create an email blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/create-an-email-blacklist-rule
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/emails
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates a new email blacklist rule.
# Create an email exception blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/create-an-email-exception-blacklist-rule
/api-reference/bundled.yaml post /v3/contact-blacklist-rules/email-exceptions
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates a new email exception blacklist rule.
# Delete a domain blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/delete-a-domain-blacklist-rule
/api-reference/bundled.yaml delete /v3/contact-blacklist-rules/domains/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes an existing domain blacklist rule. Global rules (seeded by the system) cannot be deleted.
# Delete an email blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/delete-an-email-blacklist-rule
/api-reference/bundled.yaml delete /v3/contact-blacklist-rules/emails/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes an existing email blacklist rule. Global rules (seeded by the system) cannot be deleted.
# Delete an email exception blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/delete-an-email-exception-blacklist-rule
/api-reference/bundled.yaml delete /v3/contact-blacklist-rules/email-exceptions/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes an existing email exception blacklist rule.
# Get a domain blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/get-a-domain-blacklist-rule
/api-reference/bundled.yaml get /v3/contact-blacklist-rules/domains/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single domain blacklist rule by ID.
# Get an email blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/get-an-email-blacklist-rule
/api-reference/bundled.yaml get /v3/contact-blacklist-rules/emails/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single email blacklist rule by ID.
# Get an email exception blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/get-an-email-exception-blacklist-rule
/api-reference/bundled.yaml get /v3/contact-blacklist-rules/email-exceptions/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single email exception blacklist rule by ID.
# List domain blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/list-domain-blacklist-rules
/api-reference/bundled.yaml get /v3/contact-blacklist-rules/domains
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of domain blacklist rules.
# List email blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/list-email-blacklist-rules
/api-reference/bundled.yaml get /v3/contact-blacklist-rules/emails
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of email blacklist rules.
# List email exception blacklist rules
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/list-email-exception-blacklist-rules
/api-reference/bundled.yaml get /v3/contact-blacklist-rules/email-exceptions
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of email exception blacklist rules.
# Update a domain blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/update-a-domain-blacklist-rule
/api-reference/bundled.yaml put /v3/contact-blacklist-rules/domains/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Updates an existing domain blacklist rule.
# Update an email blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/update-an-email-blacklist-rule
/api-reference/bundled.yaml put /v3/contact-blacklist-rules/emails/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Updates an existing email blacklist rule.
# Update an email exception blacklist rule
Source: https://docs.reply.io/api-reference/contact-blacklist-rules/update-an-email-exception-blacklist-rule
/api-reference/bundled.yaml put /v3/contact-blacklist-rules/email-exceptions/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Updates an existing email exception blacklist rule.
# Enrich contacts by email
Source: https://docs.reply.io/api-reference/contact-enrichment/enrich-contacts-by-email
/api-reference/bundled.yaml post /v3/contacts/enrich-by-email
**Coming soon.** This endpoint will be available by late August 2026.
Enriches contact data using each contact's existing email address.
Consumes credits from the Reply credit pool — 1 credit per contact.
# Enrich contacts by LinkedIn URL
Source: https://docs.reply.io/api-reference/contact-enrichment/enrich-contacts-by-linkedin-url
/api-reference/bundled.yaml post /v3/contacts/enrich-by-linkedin
**Coming soon.** This endpoint will be available by late August 2026.
Enriches contact data using each contact's LinkedIn URL. Optionally also finds email and/or phone in the same call.
Consumes credits from the Reply credit pool: 1 per contact, plus (when find-email is enabled) 2 and (when find-phone is enabled) 3 per contact.
# Fill AI custom fields
Source: https://docs.reply.io/api-reference/contact-enrichment/fill-ai-custom-fields
/api-reference/bundled.yaml post /v3/contacts/enrich-ai-custom-fields
**Coming soon.** This endpoint will be available by late August 2026.
Fills configured AI custom field values for contacts using AI.
Consumes credits from the Reply credit pool — 1 credit per contact-field combination.
# Find missing email addresses
Source: https://docs.reply.io/api-reference/contact-enrichment/find-missing-email-addresses
/api-reference/bundled.yaml post /v3/contacts/find-emails
**Coming soon.** This endpoint will be available by late August 2026.
Finds missing email addresses for contacts that currently don't have one.
Consumes credits from the Reply credit pool — 2 credits per contact.
# Find phone numbers
Source: https://docs.reply.io/api-reference/contact-enrichment/find-phone-numbers
/api-reference/bundled.yaml post /v3/contacts/find-phones
**Coming soon.** This endpoint will be available by late August 2026.
Finds phone numbers for contacts.
Consumes credits from the Reply credit pool — 3 credits per contact.
# Add contacts to a contact list
Source: https://docs.reply.io/api-reference/contact-lists/add-contacts-to-a-contact-list
/api-reference/bundled.yaml post /v3/contact-lists/{id}/add-contacts
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to add contacts to this list while leaving the other lists they already belong to in place. To make this list their only list, use the move-contacts endpoint instead. Contacts are processed one by one and successful additions are kept even when others fail; the response returns only the contact ids that were not processed, each with its error details.
# Create a contact list
Source: https://docs.reply.io/api-reference/contact-lists/create-a-contact-list
/api-reference/bundled.yaml post /v3/contact-lists
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need a new contact list to group contacts under. Provide the list name and set isShared to make it visible to your whole team straight away, or leave it false to keep the list private to you. The created list is returned with its assigned id. List names must be unique.
# Delete a contact list
Source: https://docs.reply.io/api-reference/contact-lists/delete-a-contact-list
/api-reference/bundled.yaml delete /v3/contact-lists/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to remove a contact list by its id. A shared list can only be deleted by the user who created it.
# Get a contact list
Source: https://docs.reply.io/api-reference/contact-lists/get-a-contact-list
/api-reference/bundled.yaml get /v3/contact-lists/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need the details of a single contact list, identified by its id — its name and whether it is shared with your team.
# Get contact's lists
Source: https://docs.reply.io/api-reference/contact-lists/get-contacts-lists
/api-reference/bundled.yaml get /v3/contacts/{id}/lists
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need to see which contact lists a contact belongs to, including each list's name and whether it is shared with your team.
# List contact lists
Source: https://docs.reply.io/api-reference/contact-lists/list-contact-lists
/api-reference/bundled.yaml get /v3/contact-lists
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need to browse the contact lists you can work with — the lists you own plus any shared with your team. Results are returned as a page controlled by the top and skip query parameters; omit both to get the first page. Pass search to keep only the lists whose name contains the given text.
# Move contacts to a contact list
Source: https://docs.reply.io/api-reference/contact-lists/move-contacts-to-a-contact-list
/api-reference/bundled.yaml post /v3/contact-lists/{id}/move-contacts
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Use this endpoint when you need the supplied contacts to end up in this list and in no other — each contact is removed from every list it currently belongs to and placed in this one. To keep their existing memberships, use the add-contacts endpoint instead. Contacts are processed one by one and successful moves are kept even when others fail; the response returns only the contact ids that were not processed, each with its error details.
# Share a contact list
Source: https://docs.reply.io/api-reference/contact-lists/share-a-contact-list
/api-reference/bundled.yaml post /v3/contact-lists/{id}/share
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to make one of your private contact lists visible to the rest of your team. To reverse it later, use the unshare endpoint.
# Unshare a contact list
Source: https://docs.reply.io/api-reference/contact-lists/unshare-a-contact-list
/api-reference/bundled.yaml post /v3/contact-lists/{id}/unshare
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to take a shared contact list back out of team visibility and make it private to you again.
# Update a contact list
Source: https://docs.reply.io/api-reference/contact-lists/update-a-contact-list
/api-reference/bundled.yaml put /v3/contact-lists/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to rename an existing contact list. Only the name can be changed here — the list's contacts and its shared state are left untouched; use the share and unshare endpoints to change visibility. The new name must be unique.
# Add a note to a contact
Source: https://docs.reply.io/api-reference/contacts/add-a-note-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/notes
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Adds a note to an existing contact
# Bulk delete contacts
Source: https://docs.reply.io/api-reference/contacts/bulk-delete-contacts
/api-reference/bundled.yaml post /v3/contacts/bulk-delete
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to remove several contacts in a single call. Contacts are processed independently: the response maps each contact id that could not be deleted to the reason it was skipped.
# Change contacts owner
Source: https://docs.reply.io/api-reference/contacts/change-contacts-owner
/api-reference/bundled.yaml put /v3/contacts/owner
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Changes the owner of one or more contacts to a different team member
# Count filtered contacts
Source: https://docs.reply.io/api-reference/contacts/count-filtered-contacts
/api-reference/bundled.yaml post /v3/contacts/filter/count
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need only the number of contacts that match a set of filter criteria — for example to show a total or decide whether to page through the results — without fetching the contacts themselves. It accepts the same request body as the filter endpoint.
# Create a contact
Source: https://docs.reply.io/api-reference/contacts/create-a-contact
/api-reference/bundled.yaml post /v3/contacts
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to add a single new contact to your account — for example when capturing a lead from your own application. Provide any known profile fields and custom field values; the contact is created under your ownership and returned with its assigned id. To add many contacts at once, or to update existing ones by a match key, use the import endpoint instead.
# Delete a contact
Source: https://docs.reply.io/api-reference/contacts/delete-a-contact
/api-reference/bundled.yaml delete /v3/contacts/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to remove a single contact from your account by its id. To remove several contacts in one call, use the bulk-delete endpoint instead.
# Filter contacts
Source: https://docs.reply.io/api-reference/contacts/filter-contacts
/api-reference/bundled.yaml post /v3/contacts/filter
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need a filtered, sorted page of contacts that a plain email or LinkedIn lookup cannot express. Combine field rules (property, condition, value) with optional scoping by list, sequence, or sequence step, a free-text search term, and a sort order. Results are paged with the top and skip query parameters. To get only the total for the same criteria, use filter/count.
# Get a contact
Source: https://docs.reply.io/api-reference/contacts/get-a-contact
/api-reference/bundled.yaml get /v3/contacts/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need the full record of a single contact, identified by its Reply contact id — including profile fields, owner, opt-out, call and meeting status, and all custom field values.
# Get a contact's statuses
Source: https://docs.reply.io/api-reference/contacts/get-a-contacts-statuses
/api-reference/bundled.yaml get /v3/contacts/{id}/statuses
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns the contact's current global flags (`isOptedOut`, `callStatus`, `meetingStatus`) plus per-sequence status (`statusInSequence`) and email disposition (`isReplied`, `isBounced`) for each sequence the contact is enrolled in.
# Get activities for a contact
Source: https://docs.reply.io/api-reference/contacts/get-activities-for-a-contact
/api-reference/bundled.yaml get /v3/contacts/{id}/activities
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need a contact's activity history — the emails, calls, status changes, and other tracked events recorded on its timeline. Results are paged with the top and skip query parameters.
# Get inbox threads for a contact
Source: https://docs.reply.io/api-reference/contacts/get-inbox-threads-for-a-contact
/api-reference/bundled.yaml get /v3/contacts/{id}/inbox-threads
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need the email conversation threads exchanged with a contact, grouped as they appear in your inbox. Results are paged with the top and skip query parameters.
# Get sequences for a contact
Source: https://docs.reply.io/api-reference/contacts/get-sequences-for-a-contact
/api-reference/bundled.yaml get /v3/contacts/{id}/sequences
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need to see which sequences a contact is enrolled in, together with each sequence's name, the contact's status in it, the current step, and when they were added.
# Import contacts
Source: https://docs.reply.io/api-reference/contacts/import-contacts
/api-reference/bundled.yaml post /v3/contacts/import
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to add or update many contacts in one call — for example when syncing contacts from another system. Each item uses the same fields as the contact update model. Use options to control deduplication, list assignment, and other import behavior: which keys identify an existing contact (email plus any of LinkedIn URL, phone, name, company, or custom fields), whether matched contacts are overwritten or skipped, whether previously deleted contacts are restored, and which lists or sequence the imported contacts are added to. The response reports how many contacts were added, updated, skipped, or failed, with a per-item outcome.
# List all contacts
Source: https://docs.reply.io/api-reference/contacts/list-all-contacts
/api-reference/bundled.yaml get /v3/contacts
_Requires the `contacts:read` scope (or a broader one that includes it)._
Use this endpoint when you need to browse your contacts or look one up by its email or LinkedIn URL. Results are returned as a page controlled by the top and skip query parameters; omit both to get the first page. Pass email and/or linkedIn to narrow the results to contacts matching those exact identifiers. For richer, rule-based filtering use the filter endpoint instead.
# Mark or unmark contacts as bounced
Source: https://docs.reply.io/api-reference/contacts/mark-or-unmark-contacts-as-bounced
/api-reference/bundled.yaml post /v3/contacts/set-bounced
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Marks the given contacts as bounced (`isBounced: true`) or clears the bounced flag (`isBounced: false`) across **every sequence** they are currently enrolled in.
When clearing the flag (`isBounced: false`), `resendEmails` is required: pass `true` to retry the previously bounced step or `false` to leave the schedule untouched. When marking as bounced (`isBounced: true`), `resendEmails` must be omitted.
To restrict the change to a single sequence, use `POST /v3/sequences/{id}/contacts/set-bounced` instead.
# Mark or unmark contacts as replied
Source: https://docs.reply.io/api-reference/contacts/mark-or-unmark-contacts-as-replied
/api-reference/bundled.yaml post /v3/contacts/set-replied
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Marks the given contacts as replied (`isReplied: true`) or clears the replied flag (`isReplied: false`) across **every sequence** they are currently enrolled in.
To restrict the change to a single sequence, use `POST /v3/sequences/{id}/contacts/set-replied` instead.
# Move a contact to a sequence
Source: https://docs.reply.io/api-reference/contacts/move-a-contact-to-a-sequence
/api-reference/bundled.yaml post /v3/contacts/{id}/move-to-sequence
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Moves a contact to a different sequence, optionally removing from existing sequences
# Set contacts' in-sequence status
Source: https://docs.reply.io/api-reference/contacts/set-contacts-in-sequence-status
/api-reference/bundled.yaml post /v3/contacts/set-status-in-sequence
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Sets the in-sequence status (`active` / `paused` / `finished` / `outOfOffice`) for the given contacts across **every sequence** they are currently enrolled in.
Replied and Bounced are **not** values of this enum — use `POST /v3/contacts/set-replied` or `POST /v3/contacts/set-bounced` for those.
To restrict the change to a single sequence, use `POST /v3/sequences/{id}/contacts/set-status-in-sequence` instead.
`paused` requires the contact to currently be `active` in the sequence — pausing from any other state is reported per-item as `invalidStatusTransition`. The other targets (`active`, `finished`, `outOfOffice`) accept any current state.
# Update a contact
Source: https://docs.reply.io/api-reference/contacts/update-a-contact
/api-reference/bundled.yaml patch /v3/contacts/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Use this endpoint when you need to change specific fields on an existing contact without resending the whole record. Only the properties present in the request body are updated; omitted properties are left unchanged, while sending an explicit null clears a nullable field. Use it to correct profile data, reassign ownership, or set opt-out, call, and meeting status.
# Create a custom field
Source: https://docs.reply.io/api-reference/custom-fields/create-a-custom-field
/api-reference/bundled.yaml post /v3/custom-fields
_Requires the `contacts:write` scope (or a broader one that includes it)._
Creates a new custom field
# Delete a custom field
Source: https://docs.reply.io/api-reference/custom-fields/delete-a-custom-field
/api-reference/bundled.yaml delete /v3/custom-fields/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Deletes an existing custom field
# Get a custom field
Source: https://docs.reply.io/api-reference/custom-fields/get-a-custom-field
/api-reference/bundled.yaml get /v3/custom-fields/{id}
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single custom field by ID
# List all custom fields
Source: https://docs.reply.io/api-reference/custom-fields/list-all-custom-fields
/api-reference/bundled.yaml get /v3/custom-fields
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a list of all custom fields in your account
# Update a custom field
Source: https://docs.reply.io/api-reference/custom-fields/update-a-custom-field
/api-reference/bundled.yaml put /v3/custom-fields/{id}
_Requires the `contacts:write` scope (or a broader one that includes it)._
Updates an existing custom field
# Send a direct email to a contact
Source: https://docs.reply.io/api-reference/direct-outreach/send-a-direct-email-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/send-direct-email
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Sends a one-off email directly to a contact outside of any sequence
# Send a LinkedIn connection request to a contact
Source: https://docs.reply.io/api-reference/direct-outreach/send-a-linkedin-connection-request-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/send-direct-linkedin-connect
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Sends a direct LinkedIn connection request to the contact outside of any sequence.
The contact must have a LinkedIn profile URL set. The caller must own an active LinkedIn account (`linkedInAccountId`).
# Send a LinkedIn InMail to a contact
Source: https://docs.reply.io/api-reference/direct-outreach/send-a-linkedin-inmail-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/send-direct-linkedin-inmail
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Sends a direct LinkedIn InMail message to the contact outside of any sequence.
Requires a Sales Navigator account with available InMail credits.
# Send a LinkedIn message to a contact
Source: https://docs.reply.io/api-reference/direct-outreach/send-a-linkedin-message-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/send-direct-linkedin-message
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Sends a direct LinkedIn message to the contact outside of any sequence.
The caller must already be connected with the contact on LinkedIn.
# Send a LinkedIn voice message to a contact
Source: https://docs.reply.io/api-reference/direct-outreach/send-a-linkedin-voice-message-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/send-direct-linkedin-voice
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Sends a pre-recorded voice message to the contact via LinkedIn outside of any sequence.
The caller must already be connected with the contact on LinkedIn.
Upload audio files via `POST /v3/attachments/voice` to obtain a `voiceAttachmentId`.
# Send an AI-generated LinkedIn voice message to a contact
Source: https://docs.reply.io/api-reference/direct-outreach/send-an-ai-generated-linkedin-voice-message-to-a-contact
/api-reference/bundled.yaml post /v3/contacts/{id}/send-direct-linkedin-ai-voice
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Generates and sends an AI voice message to the contact via LinkedIn outside of any sequence.
The caller must already be connected with the contact on LinkedIn.
The voice is synthesized from the provided script using the account's text-to-speech settings.
# Add tags to an email account
Source: https://docs.reply.io/api-reference/email-accounts/add-tags-to-an-email-account
/api-reference/bundled.yaml post /v3/email-accounts/{id}/tags
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to label an email account so you can group or filter by it later. Tags are supplied by name, and any name that does not exist yet is created for you — there is no separate create-tag call. Tags already on the account are left as they are.
# Bulk delete email accounts
Source: https://docs.reply.io/api-reference/email-accounts/bulk-delete-email-accounts
/api-reference/bundled.yaml post /v3/email-accounts/bulk-delete
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to disconnect several email accounts in one call. Accounts are processed one by one and successful deletions are kept even when others fail; the response returns only the account ids that were not deleted, each with its error details.
# Connect Gmail account via OAuth
Source: https://docs.reply.io/api-reference/email-accounts/connect-gmail-account-via-oauth
/api-reference/bundled.yaml get /v3/email-accounts/connect/gmail
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint to start connecting a Gmail mailbox. It responds with a 302 redirect to Google's consent screen, so follow the redirect in a browser rather than calling it from a backend client. The email account is created in Reply once the user finishes granting access — no create call is needed. If a connection attempt is already in progress for your account, the request is rejected.
# Connect Office 365 account via OAuth
Source: https://docs.reply.io/api-reference/email-accounts/connect-office-365-account-via-oauth
/api-reference/bundled.yaml get /v3/email-accounts/connect/office-365
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint to start connecting an Office 365 or Outlook mailbox. It responds with a 302 redirect to Microsoft's consent screen, so follow the redirect in a browser rather than calling it from a backend client. The email account is created in Reply once the user finishes granting access — no create call is needed. If a connection attempt is already in progress for your account, the request is rejected.
# Create an email account
Source: https://docs.reply.io/api-reference/email-accounts/create-an-email-account
/api-reference/bundled.yaml post /v3/email-accounts
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to connect a custom SMTP/IMAP mailbox to Reply. Gmail and Office 365 accounts cannot be created this way — start their OAuth flow with the matching connect endpoint instead. Supply the sending and receiving credentials along with any signature, safety, and ramp-up settings; the account is created under your ownership and returned with its assigned id. Validate credentials before committing to them with the SMTP and IMAP test endpoints.
# Delete an email account
Source: https://docs.reply.io/api-reference/email-accounts/delete-an-email-account
/api-reference/bundled.yaml delete /v3/email-accounts/{id}
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to disconnect a single email account and remove it from Reply by its id. To remove several accounts in one call, use the bulk-delete endpoint instead.
# Filter email account stats
Source: https://docs.reply.io/api-reference/email-accounts/filter-email-account-stats
/api-reference/bundled.yaml post /v3/email-accounts/stats
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need sending statistics for a set of email accounts rather than one — for example to spot mailboxes close to their daily limit. It takes the same filter as the account filter endpoint and returns a page of per-account figures controlled by the top and skip query parameters.
# Filter email accounts
Source: https://docs.reply.io/api-reference/email-accounts/filter-email-accounts
/api-reference/bundled.yaml post /v3/email-accounts/filter
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when a plain listing is too broad and you need email accounts matching specific criteria — status, provider type, exact email, domains, owners, or tags. Every filter field is optional, and an empty body matches the same set as the plain listing. Results are paged with the top and skip query parameters. When nothing matches, the response is an empty items array with hasMore false.
# Get an email account
Source: https://docs.reply.io/api-reference/email-accounts/get-an-email-account
/api-reference/bundled.yaml get /v3/email-accounts/{id}
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need the full record of a single email account by its id — its connection, safety, signature, opt-out, and ramp-up sections, its tags, its current connection status with any connectivity errors, and whether it is the default account or in use by a sequence. The listing endpoints return a lighter summary of the same accounts.
# Get email account stats
Source: https://docs.reply.io/api-reference/email-accounts/get-email-account-stats
/api-reference/bundled.yaml get /v3/email-accounts/{id}/stats
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need to know how much sending capacity a single email account has left — how many emails it has sent today, its configured daily limit, and its all-time sent total. To pull the same figures for many accounts at once, use the stats filter endpoint instead.
# List email account tags
Source: https://docs.reply.io/api-reference/email-accounts/list-email-account-tags
/api-reference/bundled.yaml get /v3/email-accounts/tags
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need the email account tags available to you, each with its id, name, and color. Tags are referenced by name when you add or remove them on an account and when you filter accounts by tag; the id is what the tag update endpoint takes.
# List email accounts
Source: https://docs.reply.io/api-reference/email-accounts/list-email-accounts
/api-reference/bundled.yaml get /v3/email-accounts
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need to browse the email accounts you can send from. By default it returns every account visible to you, which in Public or Organization mode includes your teammates' accounts; pass my=true to see only your own. Results are returned as a page controlled by the top and skip query parameters; omit both to get the first page. To narrow the results by status, provider, domain, owner, or tag, use the filter endpoint instead.
# Remove tags from an email account
Source: https://docs.reply.io/api-reference/email-accounts/remove-tags-from-an-email-account
/api-reference/bundled.yaml delete /v3/email-accounts/{id}/tags
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to take one or more tags off an email account. Tags are supplied by name and only the link to this account is removed — the tag itself stays available and keeps its other accounts.
# Resume sending
Source: https://docs.reply.io/api-reference/email-accounts/resume-sending
/api-reference/bundled.yaml post /v3/email-accounts/{id}/resume-sending
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when an email account has stopped sending because its provider locked or throttled it, and you have resolved the underlying problem. Clearing the lock lets the account pick its sequences back up. Check sendingLockedByProvider on the account record to see whether it is currently locked.
# Set default email account
Source: https://docs.reply.io/api-reference/email-accounts/set-default-email-account
/api-reference/bundled.yaml post /v3/email-accounts/{id}/set-default
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to choose which of your email accounts Reply pre-selects for sending. Exactly one account is your default at a time, so setting a new one clears the flag on the previous account.
# Test IMAP connectivity
Source: https://docs.reply.io/api-reference/email-accounts/test-imap-connectivity
/api-reference/bundled.yaml post /v3/email-accounts/test/imap
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint to check IMAP receiving credentials before you commit to them. Nothing is created or changed — the response reports whether the connection succeeded and, when it did not, the error the server returned. A failed test still comes back as 200 with success false.
# Test SMTP connectivity
Source: https://docs.reply.io/api-reference/email-accounts/test-smtp-connectivity
/api-reference/bundled.yaml post /v3/email-accounts/test/smtp
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint to check SMTP sending credentials before you commit to them. Nothing is created or changed — the response reports whether the connection succeeded and, when it did not, the error the server returned. A failed test still comes back as 200 with success false.
# Update a tag
Source: https://docs.reply.io/api-reference/email-accounts/update-a-tag
/api-reference/bundled.yaml put /v3/email-accounts/tags/{tagId}
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to rename a tag or change its color. The change applies everywhere the tag is used, so every email account carrying it picks up the new name straight away.
# Update an email account
Source: https://docs.reply.io/api-reference/email-accounts/update-an-email-account
/api-reference/bundled.yaml patch /v3/email-accounts/{id}
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to change specific settings on an existing email account without resending the whole record. Send at least one of the connection, safety, signature, optOut, rampUp, and tags sections; only the sections and fields you include are updated, and anything omitted is left unchanged. The connection section carries the SMTP/IMAP credentials, so it applies to custom mailboxes rather than OAuth-connected Gmail and Office 365 accounts. A safety daily limit above the ceiling allowed by your workspace plan is rejected.
# Create an email template
Source: https://docs.reply.io/api-reference/email-templates/create-an-email-template
/api-reference/bundled.yaml post /v3/email-templates
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new email template.
# Get an email template
Source: https://docs.reply.io/api-reference/email-templates/get-an-email-template
/api-reference/bundled.yaml get /v3/email-templates/{id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a single email template by ID.
# List email templates
Source: https://docs.reply.io/api-reference/email-templates/list-email-templates
/api-reference/bundled.yaml get /v3/email-templates
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a paginated list of email templates for the authenticated user.
# Clone an email template
Source: https://docs.reply.io/api-reference/email-templates/clone-an-email-template
/api-reference/bundled.yaml post /v3/email-templates/{id}/clone
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a copy of an existing email template.
# Create an email template folder
Source: https://docs.reply.io/api-reference/email-templates/create-an-email-template-folder
/api-reference/bundled.yaml post /v3/email-template-folders
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new email template folder.
# Delete an email template
Source: https://docs.reply.io/api-reference/email-templates/delete-an-email-template
/api-reference/bundled.yaml delete /v3/email-templates/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes an email template by ID.
# Delete an email template folder
Source: https://docs.reply.io/api-reference/email-templates/delete-an-email-template-folder
/api-reference/bundled.yaml delete /v3/email-template-folders/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes an email template folder by ID.
# Get an email template folder
Source: https://docs.reply.io/api-reference/email-templates/get-an-email-template-folder
/api-reference/bundled.yaml get /v3/email-template-folders/{id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a single email template folder by ID.
# Get template variables
Source: https://docs.reply.io/api-reference/email-templates/get-template-variables
/api-reference/bundled.yaml get /v3/email-templates/variables
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all available template variables grouped by category (prospect, team, advanced).
# List email template folders
Source: https://docs.reply.io/api-reference/email-templates/list-email-template-folders
/api-reference/bundled.yaml get /v3/email-template-folders
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all email template folders for the authenticated user.
# Move an email template
Source: https://docs.reply.io/api-reference/email-templates/move-an-email-template
/api-reference/bundled.yaml post /v3/email-templates/{id}/move
_Requires the `sequences:write` scope (or a broader one that includes it)._
Moves an email template to a different folder.
# Render an email template
Source: https://docs.reply.io/api-reference/email-templates/render-an-email-template
/api-reference/bundled.yaml post /v3/email-templates/{id}/render
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Renders an email template by substituting variables with actual contact data.
# Send a test email
Source: https://docs.reply.io/api-reference/email-templates/send-a-test-email
/api-reference/bundled.yaml post /v3/email-templates/{id}/send-test
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Sends a test email using the specified template.
# Share an email template folder
Source: https://docs.reply.io/api-reference/email-templates/share-an-email-template-folder
/api-reference/bundled.yaml post /v3/email-template-folders/{id}/share
_Requires the `sequences:write` scope (or a broader one that includes it)._
Shares a personal email template folder with the team.
# Update an email template
Source: https://docs.reply.io/api-reference/email-templates/update-an-email-template
/api-reference/bundled.yaml put /v3/email-templates/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing email template.
# Update an email template folder
Source: https://docs.reply.io/api-reference/email-templates/update-an-email-template-folder
/api-reference/bundled.yaml put /v3/email-template-folders/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing email template folder.
# Upload a template attachment
Source: https://docs.reply.io/api-reference/email-templates/upload-a-template-attachment
/api-reference/bundled.yaml post /v3/email-template-attachments
Uploads a file to be used as an email template attachment. Returns the attachment metadata including the ID to use when creating or updating templates.
# Estimate email validation
Source: https://docs.reply.io/api-reference/email-validations/estimate-email-validation
/api-reference/bundled.yaml post /v3/email-validations/estimate
_Requires the `contacts:read` scope (or a broader one that includes it)._
Synchronous probe — given a list of contact IDs, returns counts, credit sufficiency, and any blockers that would prevent scheduling. Useful for showing pre-flight feedback in the UI before triggering `/schedule`.
# Schedule email validation
Source: https://docs.reply.io/api-reference/email-validations/schedule-email-validation
/api-reference/bundled.yaml post /v3/email-validations/schedule
_Requires the `contacts:operate` scope (or a broader one that includes it)._
Schedules an asynchronous email-validation job for the supplied contact IDs. Returns `202 Accepted` with a `Location` header pointing at `/v3/background-jobs/{jobId}` — poll that endpoint for status and the eventual result.
The eligibility filter (team scope + `RequiresValidation` status) and the credits cap applied here mirror the `/estimate` semantics. Set `acceptPartial = true` to schedule whatever credits allow when the team's balance is below the eligible count.
# Create a holiday calendar
Source: https://docs.reply.io/api-reference/holiday-calendars/create-a-holiday-calendar
/api-reference/bundled.yaml post /v3/holiday-calendars
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new custom holiday calendar.
# Delete a holiday calendar
Source: https://docs.reply.io/api-reference/holiday-calendars/delete-a-holiday-calendar
/api-reference/bundled.yaml delete /v3/holiday-calendars/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes a custom holiday calendar. Only custom calendars can be deleted.
# Export a holiday calendar as CSV
Source: https://docs.reply.io/api-reference/holiday-calendars/export-a-holiday-calendar-as-csv
/api-reference/bundled.yaml get /v3/holiday-calendars/{id}/export-csv
**Coming soon.** This endpoint will be available by early August 2026.
_Requires the `sequences:read` scope (or a broader one that includes it)._
Exports the holidays of a calendar as a CSV file. The response filename matches the calendar name.
# Get a holiday calendar
Source: https://docs.reply.io/api-reference/holiday-calendars/get-a-holiday-calendar
/api-reference/bundled.yaml get /v3/holiday-calendars/{id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a single holiday calendar by ID, including its holidays.
# List holiday calendars
Source: https://docs.reply.io/api-reference/holiday-calendars/list-holiday-calendars
/api-reference/bundled.yaml get /v3/holiday-calendars
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all holiday calendars available to the authenticated user, including predefined and custom calendars.
# Update a holiday calendar
Source: https://docs.reply.io/api-reference/holiday-calendars/update-a-holiday-calendar
/api-reference/bundled.yaml put /v3/holiday-calendars/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing custom holiday calendar. Only custom calendars can be updated.
# Assign or clear a thread's category
Source: https://docs.reply.io/api-reference/inbox/assign-or-clear-a-threads-category
/api-reference/bundled.yaml put /v3/inbox/threads/{id}/category
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to file a single conversation under an inbox category, or to take it out of the one it is in by sending categoryId as null. A thread sits in at most one category, so setting a new one replaces the previous. Repeating the same call changes nothing. To move many threads at once, use the category thread-links endpoints instead.
# Assign threads to a category
Source: https://docs.reply.io/api-reference/inbox/assign-threads-to-a-category
/api-reference/bundled.yaml post /v3/inbox/threads/categories/{id}/thread-links/bulk
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to file several conversations under this category at once. A thread sits in at most one category, so any earlier category on a supplied thread is replaced. Threads are processed one by one and successful links are kept even when others fail; the response returns only the thread ids that were not linked, each with its error details. A per-item error is notFound when the thread or the category does not exist, invalidInput when the thread id is not usable, or forbidden when you are not allowed to file that thread.
# Bulk-delete inbox threads
Source: https://docs.reply.io/api-reference/inbox/bulk-delete-inbox-threads
/api-reference/bundled.yaml post /v3/inbox/threads/bulk-delete
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to permanently remove several inbox conversations in one call. Threads are processed one by one and successful deletions are kept even when others fail; the response returns only the thread ids that were not deleted, each with its error details, so an empty object means everything succeeded. A per-item error is notFound when the thread does not exist, or forbidden when you are not allowed to delete it.
# Create inbox category
Source: https://docs.reply.io/api-reference/inbox/create-inbox-category
/api-reference/bundled.yaml post /v3/inbox/threads/categories
_Requires the `inbox:write` scope (or a broader one that includes it)._
Use this endpoint when you need a category of your own to sort inbox conversations under. Provide a name — unique within your team, ignoring case — and optionally a color; the created category is returned with its assigned id.
# Delete inbox category
Source: https://docs.reply.io/api-reference/inbox/delete-inbox-category
/api-reference/bundled.yaml delete /v3/inbox/threads/categories/{id}
_Requires the `inbox:write` scope (or a broader one that includes it)._
Use this endpoint when you no longer need one of your own categories. Any conversations filed under it are unlinked and stay in the inbox — only the category is removed. Reply's reserved categories cannot be deleted — those are rejected with 403 and code inboxCategory.cannotModifyDefault.
# Delete inbox thread
Source: https://docs.reply.io/api-reference/inbox/delete-inbox-thread
/api-reference/bundled.yaml delete /v3/inbox/threads/{id}
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to permanently remove a single conversation from the inbox, together with its messages. To remove several conversations in one call, use the bulk-delete endpoint instead.
# Filter inbox threads
Source: https://docs.reply.io/api-reference/inbox/filter-inbox-threads
/api-reference/bundled.yaml post /v3/inbox/threads/filter
_Requires the `inbox:read` scope (or a broader one that includes it)._
Use this endpoint when you need the inbox threads matching specific criteria — a search term, channel, owner, sequence, email or LinkedIn account, contact, category, date range, source, or only those flagged with meeting intent. Every filter field is optional, and an empty body matches the same set as the plain thread listing. Results are paged with the top and skip query parameters.
# Generate an AI draft reply
Source: https://docs.reply.io/api-reference/inbox/generate-an-ai-draft-reply
/api-reference/bundled.yaml post /v3/inbox/threads/{id}/ai-draft/generate
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
Generates an AI-suggested reply for an inbox thread. The same endpoint backs both the initial generate and the "Regenerate" action in the message editor — each call produces a fresh draft. To send the draft (with or without edits), use the existing `POST /v3/inbox/threads/{id}/messages` endpoint.
# Get inbox category
Source: https://docs.reply.io/api-reference/inbox/get-inbox-category
/api-reference/bundled.yaml get /v3/inbox/threads/categories/{id}
_Requires the `inbox:read` scope (or a broader one that includes it)._
Use this endpoint when you need a single inbox category by its id — its name, color, whether it is one of Reply's reserved categories, and its current unread thread count.
# Get inbox thread
Source: https://docs.reply.io/api-reference/inbox/get-inbox-thread
/api-reference/bundled.yaml get /v3/inbox/threads/{id}
_Requires the `inbox:read` scope (or a broader one that includes it)._
Use this endpoint when you need one conversation in full — its thread details together with the message history in the order it happened. When you only need the messages and want to page through a long exchange, use the messages endpoint instead.
# List inbox categories
Source: https://docs.reply.io/api-reference/inbox/list-inbox-categories
/api-reference/bundled.yaml get /v3/inbox/threads/categories
_Requires the `inbox:read` scope (or a broader one that includes it)._
Use this endpoint when you need the inbox categories you can file conversations under, including the reserved ones Reply provides out of the box. Each entry carries its name, color, whether it is reserved, and how many unread threads it currently holds. Pass ownerUserId to see the categories of a specific teammate instead of your own.
# List inbox threads
Source: https://docs.reply.io/api-reference/inbox/list-inbox-threads
/api-reference/bundled.yaml get /v3/inbox/threads
_Requires the `inbox:read` scope (or a broader one that includes it)._
Use this endpoint when you need to browse your inbox — the email and LinkedIn conversations with your contacts, most recently active first. Results are returned as a page controlled by the top and skip query parameters; omit both to get the first page. To narrow the list by channel, sequence, category, date range, or free text, use the filter endpoint instead.
# List messages in an inbox thread
Source: https://docs.reply.io/api-reference/inbox/list-messages-in-an-inbox-thread
/api-reference/bundled.yaml get /v3/inbox/threads/{id}/messages
_Requires the `inbox:read` scope (or a broader one that includes it)._
Use this endpoint when you need to page through the messages of one conversation, oldest first. Messages vary by channel: email messages carry recipient and subject metadata, LinkedIn messages carry only the shared fields. Results are paged with the top and skip query parameters.
# Mark threads as read
Source: https://docs.reply.io/api-reference/inbox/mark-threads-as-read
/api-reference/bundled.yaml post /v3/inbox/threads/mark-as-read
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to clear the unread state on one or more conversations — for example after your own app has shown them to the user. Threads that are already read are unaffected. Threads are processed one by one; the response returns only the thread ids that were not updated, each with its error details, so an empty object means everything succeeded. A per-item error is notFound when the thread does not exist, or forbidden when you are not allowed to access it.
# Mark threads as unread
Source: https://docs.reply.io/api-reference/inbox/mark-threads-as-unread
/api-reference/bundled.yaml post /v3/inbox/threads/mark-as-unread
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to flag one or more conversations as needing attention again by restoring their unread state. Threads are processed one by one; the response returns only the thread ids that were not updated, each with its error details, so an empty object means everything succeeded. A per-item error is notFound when the thread does not exist, or forbidden when you are not allowed to access it.
# Send a reply within a thread
Source: https://docs.reply.io/api-reference/inbox/send-a-reply-within-a-thread
/api-reference/bundled.yaml post /v3/inbox/threads/{id}/messages
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to reply to a contact inside an existing conversation. The channel discriminator in the request body must match the thread's own channel — a mismatch is rejected with code inboxThread.channelMismatch. An email reply may also carry cc and bcc recipients, apply your signature, and target a specific message in the chain, while a LinkedIn reply takes just the message text. Attach files by passing the ids returned when you uploaded them. The sent message is returned.
# Submit feedback on a generated AI draft
Source: https://docs.reply.io/api-reference/inbox/submit-feedback-on-a-generated-ai-draft
/api-reference/bundled.yaml post /v3/inbox/threads/{id}/ai-draft/feedback
**Coming soon.** This endpoint will be available by late August 2026.
Records a thumbs-up / thumbs-down on the AI-generated draft, with optional dislike reasons and a freeform comment. Used as a training signal.
Requires the AI SDR feature on the caller's team.
# Toggle thread meeting-intent
Source: https://docs.reply.io/api-reference/inbox/toggle-thread-meeting-intent
/api-reference/bundled.yaml put /v3/inbox/threads/{id}/meeting-intent
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to mark a conversation as one where the contact wants to book a meeting, or to clear that mark. Send hasMeetingIntent true to set it and false to remove it. The thread filter can then return only the threads carrying this flag.
# Unassign threads from a category
Source: https://docs.reply.io/api-reference/inbox/unassign-threads-from-a-category
/api-reference/bundled.yaml post /v3/inbox/threads/categories/{id}/thread-links/bulk-delete
_Requires the `inbox:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to take several conversations out of this category. A supplied thread filed under a different category is left alone. The conversations themselves are not deleted. Threads are processed one by one; the response returns only the thread ids that were not unlinked, each with its error details. A per-item error is notFound when the thread or the category does not exist, invalidInput when the thread id is not usable, or forbidden when you are not allowed to modify that thread.
# Update inbox category
Source: https://docs.reply.io/api-reference/inbox/update-inbox-category
/api-reference/bundled.yaml put /v3/inbox/threads/categories/{id}
_Requires the `inbox:write` scope (or a broader one that includes it)._
Use this endpoint when you need to rename a category or change its color. Both name and color are replaced by what you send, so include the values you want to keep. The new name must be unique within your team. Reply's reserved categories cannot be edited — those are rejected with 403 and code inboxCategory.cannotModifyDefault.
# Bulk delete LinkedIn accounts
Source: https://docs.reply.io/api-reference/linkedin-accounts/bulk-delete-linkedin-accounts
/api-reference/bundled.yaml post /v3/linkedin-accounts/bulk-delete
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to disconnect several LinkedIn accounts in one call — up to 100 ids per request. Set force to true to also delete accounts that are still in use by sequences. Accounts are processed one by one and successful deletions are kept even when others fail; the response returns only the account ids that were not deleted, each with its error details.
# Create a connection link
Source: https://docs.reply.io/api-reference/linkedin-accounts/create-a-connection-link
/api-reference/bundled.yaml post /v3/linkedin-accounts/connection-link
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to invite a named LinkedIn account to be connected. Give the account a name up front and the response returns a connection link to hand to whoever owns the profile, plus the id of the pending account created for it. The account appears under the pending endpoint until the link is completed, after which it becomes a regular LinkedIn account. To issue a link without naming the account first, use the connect endpoint instead.
# Create a direct connection link
Source: https://docs.reply.io/api-reference/linkedin-accounts/create-a-direct-connection-link
/api-reference/bundled.yaml post /v3/linkedin-accounts/connect
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need a connection link without deciding the account name up front — the name is captured while the link is being completed. No pending account is created until then. To name the account in advance and track it as pending, use the connection-link endpoint instead.
# Delete a LinkedIn account
Source: https://docs.reply.io/api-reference/linkedin-accounts/delete-a-linkedin-account
/api-reference/bundled.yaml delete /v3/linkedin-accounts/{id}
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to disconnect a LinkedIn account and remove it from Reply. An account that is still in use by one or more sequences is rejected; pass force=true to delete it anyway. To remove several accounts in one call, use the bulk-delete endpoint instead.
# Delete a pending LinkedIn account
Source: https://docs.reply.io/api-reference/linkedin-accounts/delete-a-pending-linkedin-account
/api-reference/bundled.yaml delete /v3/linkedin-accounts/pending/{id}
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need to cancel an outstanding LinkedIn connection invitation — for example because its link expired or was sent to the wrong person. This removes the pending entry only; connected accounts are removed with the delete endpoint instead.
# Get a LinkedIn account
Source: https://docs.reply.io/api-reference/linkedin-accounts/get-a-linkedin-account
/api-reference/bundled.yaml get /v3/linkedin-accounts/{id}
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need the full record of a single connected LinkedIn account by its id — profile details, account and cookie status, current usage against its daily automation limits, and its auto-revoke settings.
# List LinkedIn accounts
Source: https://docs.reply.io/api-reference/linkedin-accounts/list-linkedin-accounts
/api-reference/bundled.yaml get /v3/linkedin-accounts
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need the LinkedIn accounts connected to your Reply account, with their connection state and automation limits. The full set is returned in one call — there is no paging. Accounts that have been invited but not yet connected are not included here; list those with the pending endpoint.
# List pending LinkedIn accounts
Source: https://docs.reply.io/api-reference/linkedin-accounts/list-pending-linkedin-accounts
/api-reference/bundled.yaml get /v3/linkedin-accounts/pending
_Requires the `channels:read` scope (or a broader one that includes it)._
Use this endpoint when you need to track LinkedIn accounts that were invited through a connection link but have not completed the connection yet. Each entry carries the name it was invited under, its connection link, when that link expires, and whether it has already expired.
# Reconnect a LinkedIn account
Source: https://docs.reply.io/api-reference/linkedin-accounts/reconnect-a-linkedin-account
/api-reference/bundled.yaml post /v3/linkedin-accounts/{id}/reconnect
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when an already-connected LinkedIn account has stopped working because its session expired — its cookie status is no longer valid. The response returns a fresh connection link for that same account; once it is completed the account resumes with its existing name, limits, and sequence assignments.
# Toggle LinkedIn account status
Source: https://docs.reply.io/api-reference/linkedin-accounts/toggle-linkedin-account-status
/api-reference/bundled.yaml post /v3/linkedin-accounts/{id}/toggle-status
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to pause or resume LinkedIn automation on a single account. The call flips the account between enabled and disabled — there is no separate enable or disable endpoint — and returns the resulting status. An account whose LinkedIn cookie is no longer valid cannot be toggled; reconnect it first.
# Update LinkedIn account limits
Source: https://docs.reply.io/api-reference/linkedin-accounts/update-linkedin-account-limits
/api-reference/bundled.yaml put /v3/linkedin-accounts/{id}/limits
_Requires the `channels:operate` scope (or a broader one that includes it)._
Use this endpoint when you need to cap how much LinkedIn automation an account performs per day — connection requests, messages, InMails, profile views, and the other per-action daily counters. Set limitsMode to Fixed and fill fixedMax to use one hard ceiling per action, or set it to Ranged and fill rangeMin and rangeMax to have Reply pick a daily value inside that range. The whole limits object is replaced, so send every value you want to keep.
# Update LinkedIn account revoke settings
Source: https://docs.reply.io/api-reference/linkedin-accounts/update-linkedin-account-revoke-settings
/api-reference/bundled.yaml put /v3/linkedin-accounts/{id}/revoke-settings
_Requires the `channels:write` scope (or a broader one that includes it)._
Use this endpoint when you need the account to withdraw its own LinkedIn connection requests that go unanswered. Set enabled to true and periodDays to the number of days a request may stay pending before it is withdrawn, or set enabled to false to stop withdrawing them.
# Departments typeahead
Source: https://docs.reply.io/api-reference/live-data/departments-typeahead
/api-reference/bundled.yaml get /v3/live-data/filter-values/departments
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Typeahead values for the `departments` filter on the Live Data / Autopilot sidebar.
# Get a Live Data search
Source: https://docs.reply.io/api-reference/live-data/get-a-live-data-search
/api-reference/bundled.yaml get /v3/live-data/searches/{id}
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a single Live Data search by id, including its status, progress, and the filters it was run with.
# Industries typeahead
Source: https://docs.reply.io/api-reference/live-data/industries-typeahead
/api-reference/bundled.yaml get /v3/live-data/filter-values/industries
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Typeahead values for the `industries` filter on the Live Data / Autopilot sidebar.
# Job titles typeahead
Source: https://docs.reply.io/api-reference/live-data/job-titles-typeahead
/api-reference/bundled.yaml get /v3/live-data/filter-values/job-titles
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Typeahead values for the `jobTitles` filter on the Live Data / Autopilot sidebar.
# List Live Data searches
Source: https://docs.reply.io/api-reference/live-data/list-live-data-searches
/api-reference/bundled.yaml get /v3/live-data/searches
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Returns a paginated list of past Live Data searches for the caller's team, most recent first. Each item includes status and progress for polling.
# Locations typeahead
Source: https://docs.reply.io/api-reference/live-data/locations-typeahead
/api-reference/bundled.yaml get /v3/live-data/filter-values/locations
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Typeahead values for the `locations` filter on the Live Data / Autopilot sidebar. The response mixes countries, regions, and cities (no separate `/countries` endpoint).
# Preview a Live Data search
Source: https://docs.reply.io/api-reference/live-data/preview-a-live-data-search
/api-reference/bundled.yaml post /v3/live-data/searches/preview
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:write` scope (or a broader one that includes it)._
Starts a Live Data preview for the given filters and returns a background job to poll. The job is tracked by `GET /v3/background-jobs/{id}`; on completion its `jsonDataResult` carries `{ contactsCount, companiesCount, sampleContacts }`, where `sampleContacts` is a capped sample of matching contacts. A preview adds no prospects and consumes no credits.
# Seniorities typeahead
Source: https://docs.reply.io/api-reference/live-data/seniorities-typeahead
/api-reference/bundled.yaml get /v3/live-data/filter-values/seniorities
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:read` scope (or a broader one that includes it)._
Typeahead values for the `seniorities` filter on the Live Data / Autopilot sidebar.
# Start a Live Data search
Source: https://docs.reply.io/api-reference/live-data/start-a-live-data-search
/api-reference/bundled.yaml post /v3/live-data/searches
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `contacts:write` scope (or a broader one that includes it)._
Starts a Live Data search that finds contacts matching the filters and adds them as prospects (optionally moving them into a sequence and/or lists). Consumes credits from the Reply credit pool per resulting contact. Returns the created search's id; track its progress via `GET /v3/live-data/searches/{id}`.
# Get calls reporting overview
Source: https://docs.reply.io/api-reference/reports/get-calls-reporting-overview
/api-reference/bundled.yaml post /v3/reporting/calls/overview
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns aggregated call statistics with per-member breakdown.
Includes total calls, average duration, positive/answered counts, and inbound missing calls.
# Get channel efficiency overview
Source: https://docs.reply.io/api-reference/reports/get-channel-efficiency-overview
/api-reference/bundled.yaml post /v3/reporting/channel-efficiency/overview
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns cross-channel efficiency statistics comparing performance across email, calls, LinkedIn, and other channels.
# Get email reporting overview
Source: https://docs.reply.io/api-reference/reports/get-email-reporting-overview
/api-reference/bundled.yaml post /v3/reporting/emails/overview
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns email delivery and engagement metrics with optional trend comparison.
Provides aggregated statistics including delivery rates, opens, replies, bounces, and clicks.
Optionally compare against a previous period by providing the `compareTo` field.
# Get LinkedIn reporting overview
Source: https://docs.reply.io/api-reference/reports/get-linkedin-reporting-overview
/api-reference/bundled.yaml post /v3/reporting/linkedin/overview
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns LinkedIn engagement metrics overview including connection requests, messages, InMails, and profile views.
# Get tasks reporting overview
Source: https://docs.reply.io/api-reference/reports/get-tasks-reporting-overview
/api-reference/bundled.yaml post /v3/reporting/tasks/overview
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns aggregated task statistics broken down by type (call, email, LinkedIn, meetings, SMS, to-do, WhatsApp), with per-member breakdown.
# Get team performance overview
Source: https://docs.reply.io/api-reference/reports/get-team-performance-overview
/api-reference/bundled.yaml post /v3/reporting/team-performance/overview
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns team performance metrics including meetings booked, contacts engaged, conversion rates, touches per contact, and response times, with per-member breakdown.
# List call activity
Source: https://docs.reply.io/api-reference/reports/list-call-activity
/api-reference/bundled.yaml post /v3/reporting/calls
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns a paginated list of individual call records matching the specified filters.
Call recording URLs are included only if the user has permission to listen to call records.
# List email activity
Source: https://docs.reply.io/api-reference/reports/list-email-activity
/api-reference/bundled.yaml post /v3/reporting/emails
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns a paginated list of individual email activity records matching the specified filters.
Use `top` and `skip` query parameters for pagination.
# List LinkedIn activity
Source: https://docs.reply.io/api-reference/reports/list-linkedin-activity
/api-reference/bundled.yaml post /v3/reporting/linkedin
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns a paginated list of individual LinkedIn action records matching the specified filters.
# List meetings
Source: https://docs.reply.io/api-reference/reports/list-meetings
/api-reference/bundled.yaml post /v3/reporting/team-performance/meetings
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns a paginated list of meetings booked through team performance, matching the specified filters.
# List task activity
Source: https://docs.reply.io/api-reference/reports/list-task-activity
/api-reference/bundled.yaml post /v3/reporting/tasks
_Requires the `reporting:read` scope (or a broader one that includes it)._
Returns a paginated list of individual task records matching the specified filters.
# Create a schedule
Source: https://docs.reply.io/api-reference/schedules/create-a-schedule
/api-reference/bundled.yaml post /v3/schedules
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new schedule with timing configuration.
# Delete a schedule
Source: https://docs.reply.io/api-reference/schedules/delete-a-schedule
/api-reference/bundled.yaml delete /v3/schedules/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes a schedule by ID. Cannot delete the default schedule or a schedule in use.
# Get a schedule
Source: https://docs.reply.io/api-reference/schedules/get-a-schedule
/api-reference/bundled.yaml get /v3/schedules/{id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a single schedule by ID.
# Link a holiday calendar to a schedule
Source: https://docs.reply.io/api-reference/schedules/link-a-holiday-calendar-to-a-schedule
/api-reference/bundled.yaml post /v3/schedules/{id}/holiday-calendar-links
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Links a holiday calendar to the specified schedule.
# List linked holiday calendars
Source: https://docs.reply.io/api-reference/schedules/list-linked-holiday-calendars
/api-reference/bundled.yaml get /v3/schedules/{id}/holiday-calendars
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all holiday calendars linked to the specified schedule.
# List schedules
Source: https://docs.reply.io/api-reference/schedules/list-schedules
/api-reference/bundled.yaml get /v3/schedules
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all schedules for the authenticated user.
# Set default schedule
Source: https://docs.reply.io/api-reference/schedules/set-default-schedule
/api-reference/bundled.yaml post /v3/schedules/{id}/set-default
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Sets the specified schedule as the default.
# Unlink a holiday calendar from a schedule
Source: https://docs.reply.io/api-reference/schedules/unlink-a-holiday-calendar-from-a-schedule
/api-reference/bundled.yaml delete /v3/schedules/{id}/holiday-calendar-links/{calendarId}
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Removes the link between a holiday calendar and the specified schedule.
# Update a schedule
Source: https://docs.reply.io/api-reference/schedules/update-a-schedule
/api-reference/bundled.yaml put /v3/schedules/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing schedule.
# Archive a sequence
Source: https://docs.reply.io/api-reference/sequences/archive-a-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/archive
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Archives a sequence. Returns the updated sequence.
# Bulk archive sequences
Source: https://docs.reply.io/api-reference/sequences/bulk-archive-sequences
/api-reference/bundled.yaml post /v3/sequences/archive
_Requires the `sequences:write` scope (or a broader one that includes it)._
Archives multiple sequences at once. Non-atomic — some may succeed while others fail.
# Bulk change sequence owner
Source: https://docs.reply.io/api-reference/sequences/bulk-change-sequence-owner
/api-reference/bundled.yaml post /v3/sequences/batch/owner
_Requires the `sequences:write` scope (or a broader one that includes it)._
Changes the owner of multiple sequences at once. Non-atomic — some may succeed while others fail.
# Bulk delete sequences
Source: https://docs.reply.io/api-reference/sequences/bulk-delete-sequences
/api-reference/bundled.yaml post /v3/sequences/bulk-delete
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes multiple sequences at once. Non-atomic — some may succeed while others fail.
# Bulk pause sequences
Source: https://docs.reply.io/api-reference/sequences/bulk-pause-sequences
/api-reference/bundled.yaml post /v3/sequences/pause
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Pauses multiple sequences at once. Non-atomic — some may succeed while others fail.
# Bulk start sequences
Source: https://docs.reply.io/api-reference/sequences/bulk-start-sequences
/api-reference/bundled.yaml post /v3/sequences/start
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Starts multiple sequences at once. Non-atomic — some may succeed while others fail.
# Bulk unarchive sequences
Source: https://docs.reply.io/api-reference/sequences/bulk-unarchive-sequences
/api-reference/bundled.yaml post /v3/sequences/unarchive
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:write` scope (or a broader one that includes it)._
Unarchives multiple sequences at once. Non-atomic — some may succeed while others fail.
# Change sequence owner
Source: https://docs.reply.io/api-reference/sequences/change-sequence-owner
/api-reference/bundled.yaml put /v3/sequences/{id}/owner
_Requires the `sequences:write` scope (or a broader one that includes it)._
Changes the owner of a single sequence to a different team member
# Count sequences
Source: https://docs.reply.io/api-reference/sequences/count-sequences
/api-reference/bundled.yaml get /v3/sequences/count
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns the total number of sequences matching the given filters. Accepts the same filter parameters as `GET /v3/sequences` (except pagination and sorting).
# Create a sequence
Source: https://docs.reply.io/api-reference/sequences/create-a-sequence
/api-reference/bundled.yaml post /v3/sequences
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new email sequence
# Delete a sequence
Source: https://docs.reply.io/api-reference/sequences/delete-a-sequence
/api-reference/bundled.yaml delete /v3/sequences/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes an existing sequence
# Get a sequence
Source: https://docs.reply.io/api-reference/sequences/get-a-sequence
/api-reference/bundled.yaml get /v3/sequences/{id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns sequence details by Id
# List all sequences
Source: https://docs.reply.io/api-reference/sequences/list-all-sequences
/api-reference/bundled.yaml get /v3/sequences
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a paginated list of all sequences in your account.
Results are ordered by creation date, newest first, unless overridden with `sortBy` / `sortDirection`.
# Pause a sequence
Source: https://docs.reply.io/api-reference/sequences/pause-a-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/pause
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Pauses an active sequence. Returns the updated sequence.
This operation is **idempotent**: calling Pause on an already paused sequence returns `200` with the current sequence state. No error is raised.
# Start a sequence
Source: https://docs.reply.io/api-reference/sequences/start-a-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/start
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Activates a new or paused sequence. Returns the updated sequence.
This operation is **idempotent**: calling Start on an already active sequence returns `200` with the current sequence state. No error is raised.
# Unarchive a sequence
Source: https://docs.reply.io/api-reference/sequences/unarchive-a-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/unarchive
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Restores an archived sequence to its previous state. Returns the updated sequence.
# Update a sequence
Source: https://docs.reply.io/api-reference/sequences/update-a-sequence
/api-reference/bundled.yaml patch /v3/sequences/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing sequence
# Machine-readable indexes
Source: https://docs.reply.io/agents/machine-readable
llms.txt, OpenAPI, catalog.json, and runtime discovery — how agents read Reply.io documentation programmatically.
Every layer of Reply.io documentation is available in a machine-readable form. Use these instead of
scraping HTML.
## This site (docs.reply.io)
| Resource | URL | What it is |
| ------------- | ------------------------------------------------------- | -------------------------------------------------------------------------- |
| llms.txt | [`/llms.txt`](https://docs.reply.io/llms.txt) | Index of all documentation pages with descriptions |
| llms-full.txt | [`/llms-full.txt`](https://docs.reply.io/llms-full.txt) | The full documentation content in one file |
| OpenAPI spec | see [OpenAPI & Postman](/openapi-postman) | The complete v3 REST API specification (also drives the API Reference tab) |
Mintlify also serves any docs page as markdown — append `.md` to a page URL for the raw source.
## The agents portal (agents.reply.io)
The [agent discovery portal](https://agents.reply.io) publishes its own machine surface, useful as
a compact capability index before diving into full references here:
| Resource | What it is |
| ------------------------------ | ---------------------------------------------------------------------------------------- |
| `/catalog.json` | Machine-readable module index: modules, workflows, auth model, rate limits, MCP endpoint |
| `/llms.txt` · `/llms-full.txt` | Portal content index and full text |
| `.md` twins | Every portal page is also plain markdown — append `.md` to its URL |
| Per-module OpenAPI | Small, module-scoped OpenAPI subsets — lighter than the full spec for narrow tasks |
The portal is a discovery layer; canonical technical facts live here on docs.reply.io.
## Runtime discovery
Two sources are always current because they come from the running systems:
* **MCP `tools/list`** — the live tool catalog with full JSON schemas and safety annotations.
This is the runtime truth for MCP; regenerate the reference any time — see
[Tool reference](/mcp/tools#regenerate-this-reference).
* **CLI `--help`** — `reply --help` and `reply --help` print flags and examples, matching
the installed version exactly.
## Recommended agent reading order
1. `docs.reply.io/llms.txt` — find the relevant pages.
2. The specific page as `.md` — read only what the task needs.
3. For MCP work: `tools/list` on the live server for exact schemas.
4. For REST work: the [OpenAPI spec](/openapi-postman) for exact request/response shapes.
# Schemas
Source: https://docs.reply.io/api-reference/schemas
Object models used across the Reply API v3, grouped by resource domain.
Reference for the objects the Reply API v3 accepts and returns. Each entry links to a dedicated page with the full field list, types, and descriptions.
## Contacts & lists
| Schema | Description |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| [Account Filter Item](/api-reference/schemas/contact-account-filter-item) | A single filter condition for contact account queries |
| [Contact](/api-reference/schemas/contact) | Contact response model |
| [Contact Account](/api-reference/schemas/contact-account) | A contact account representing a company or organization |
| [Contact Account Contact](/api-reference/schemas/contact-account-contact) | A contact linked to a contact account |
| [Contact Account Details](/api-reference/schemas/contact-account-details) | A single contact account, including the resolved email provider |
| [Contact Account List](/api-reference/schemas/contact-account-list) | Account list entity |
| [Contact Account Stage](/api-reference/schemas/contact-account-stage) | Stage assigned to a contact account |
| [Contact Inbox Thread](/api-reference/schemas/contact-inbox-thread) | Inbox thread summary as it appears on a single contact's timeline. |
| [Contact List](/api-reference/schemas/contact-list) | Contact list entity |
| [Contact List Reference](/api-reference/schemas/contact-list-ref) | Lightweight contact list reference |
## Sequences
| Schema | Description |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [AI SDR sequence settings](/api-reference/schemas/sequence) | AI SDR-specific settings of a sequence. |
| [Sequence](/api-reference/schemas/sequence-response) | Full representation of a sequence, including its schedule and the email and LinkedIn accounts used to send from it. |
| [Sequence Contact](/api-reference/schemas/sequence-contact-item) | A contact enrolled in a sequence with sequence-specific metadata |
| [Sequence Contacts List](/api-reference/schemas/sequence-contact) | Response model for listing contacts in a sequence with pagination |
| [Sequence Contacts List with Extended State](/api-reference/schemas/sequence-contact-state) | Response model for listing contacts in a sequence with pagination, including extended state columns |
| [Sequence Email Account](/api-reference/schemas/sequence-email-account) | An email account linked to a sequence for sending outbound emails. |
| [Sequence Folder](/api-reference/schemas/sequence-folder) | A folder for organizing sequences |
| [Sequence LinkedIn Account](/api-reference/schemas/sequence-linkedin-account) | A LinkedIn account linked to a sequence |
| [Sequence Step Info](/api-reference/schemas/sequence-step-info) | Position of the contact within a sequence's step graph. |
| [Sequence Template](/api-reference/schemas/sequence-template-base) | A sequence template |
| [Sequence Templates List](/api-reference/schemas/sequence-templates-list) | Response model for listing sequence templates grouped by scope |
| [Step Variant](/api-reference/schemas/variant) | A text variant for a sequence step |
## Email accounts
| Schema | Description |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| [Email Account Detail](/api-reference/schemas/email-account-detail) | Full detailed representation of an email account, including all configuration sections. |
| [Email Account Stats](/api-reference/schemas/email-account-stats) | |
| [Email Account Tag](/api-reference/schemas/email-account-tag) | An email account tag used for organizing and filtering accounts. |
## Email templates
| Schema | Description |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| [Email Template](/api-reference/schemas/email-template) | Full representation of an email template including timestamps. |
| [Email Template Attachment](/api-reference/schemas/email-template-attachment) | Attachment associated with an email template. |
| [Email Template Folder](/api-reference/schemas/email-template-folder) | An email template folder for organizing templates. |
| [Email Template Variables](/api-reference/schemas/email-template-variables) | Available template variables grouped by category. |
| [Render Error](/api-reference/schemas/email-template-render-error) | An error encountered during template rendering. |
| [Render Template Result](/api-reference/schemas/email-template-render-result) | Result of rendering an email template with variable substitution. |
| [Variable Info](/api-reference/schemas/email-template-variable-info) | Information about a single template variable. |
## Inbox
| Schema | Description |
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| [Inbox Category](/api-reference/schemas/category) | Inbox thread category. |
| [Inbox Thread](/api-reference/schemas/thread) | Full inbox thread with the contact, channel-specific account ids, sequence, category, meeting-intent flag, and the ordered list of messages. |
| [Inbox Thread AI Draft](/api-reference/schemas/thread-ai-draft) | The thread's pending AI-drafted reply, awaiting review. |
| [Inbox Thread Category Reference](/api-reference/schemas/thread-category) | Minimal category reference embedded in thread responses. |
| [Inbox Thread Contact](/api-reference/schemas/thread-contact) | Contact party on the other end of an inbox thread. |
| [Inbox Thread Message Status](/api-reference/schemas/thread-message-status) | Per-message status. |
| [Inbox Thread Sequence](/api-reference/schemas/thread-sequence) | Minimal sequence reference attached to an inbox thread. |
| [Inbox Thread Status](/api-reference/schemas/thread-status) | Aggregated thread state, derived from the latest message. |
## Tasks
| Schema | Description |
| -------------------------------------------- | --------------------------------------------------------------------- |
| [Task](/api-reference/schemas/task-response) | Full task detail returned by GET by ID, Create, and Update endpoints. |
## LinkedIn
| Schema | Description |
| ------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| [Connection Link](/api-reference/schemas/connection-link) | A link for connecting a LinkedIn account via browser extension. |
| [Connection Link With Pending](/api-reference/schemas/connection-link-with-pending) | A connection link with the associated pending account ID. |
| [LinkedIn Account](/api-reference/schemas/linkedin-account) | Full representation of a LinkedIn account. |
| [LinkedIn Account Limits](/api-reference/schemas/linkedin-account-limits) | Daily limits configuration for a LinkedIn account. |
| [LinkedIn Account Limits Values](/api-reference/schemas/linkedin-account-limits-values) | Individual daily limit values for LinkedIn automation actions. |
| [LinkedIn Account Revoke Settings](/api-reference/schemas/linkedin-account-revoke-settings) | Connection revocation settings for a LinkedIn account. |
| [LinkedIn Account Toggle Status](/api-reference/schemas/linkedin-account-toggle-status) | Response after toggling a LinkedIn account status. |
| [LinkedIn Account Usage](/api-reference/schemas/linkedin-account-usage) | Daily usage statistics for a LinkedIn account. |
| [LinkedIn Account Voice Profile](/api-reference/schemas/linkedin-account-voice-profile) | Voice profile associated with a LinkedIn account. |
| [LinkedIn Action Item](/api-reference/schemas/linkedin-action-item) | Individual LinkedIn action record in a reporting list |
| [Pending LinkedIn Account](/api-reference/schemas/pending-linkedin-account) | A LinkedIn account that is pending connection. |
## Schedules & holidays
| Schema | Description |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| [Holiday](/api-reference/schemas/holiday) | A single holiday entry within a calendar. |
| [Holiday Calendar](/api-reference/schemas/holiday-calendar) | Full representation of a holiday calendar including individual holidays. |
| [Holiday Input](/api-reference/schemas/holiday-input) | A holiday entry for creating or updating a calendar. |
| [Schedule](/api-reference/schemas/schedule) | Full representation of a schedule with timing configuration. |
| [Schedule Time](/api-reference/schemas/schedule-time) | A time of day represented as hour and minute. |
| [Schedule Timing](/api-reference/schemas/schedule-timing) | Timing configuration for a single day of the week. |
| [Schedule Timing Time Range](/api-reference/schemas/schedule-timing-time-range) | A time range within a day. |
## Blocklist rules
| Schema | Description |
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| [Domain Blacklist Rule](/api-reference/schemas/domain-rule) | A blacklist rule that blocks a domain pattern |
| [Email Blacklist Rule](/api-reference/schemas/email-rule) | A blacklist rule that blocks an email pattern |
| [Email Exception Blacklist Rule](/api-reference/schemas/email-exception-rule) | An exception rule that allows an email pattern even if it matches a blacklist rule |
## AI SDR
| Schema | Description |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [Knowledge Base](/api-reference/schemas/knowledge-base) | Detailed representation of a knowledge base. |
| [Knowledge Base Document](/api-reference/schemas/knowledge-base-document) | A file document attached to a knowledge base. |
| [Knowledge Base Link](/api-reference/schemas/knowledge-base-link) | A web link attached to a knowledge base. |
| [Knowledge Base Media](/api-reference/schemas/knowledge-base-media) | A media file attached to a reply handler or reengagement card. |
| [Offer](/api-reference/schemas/offer) | Detailed representation of an offer — the bundle of company-context inputs the AI SDR uses to personalize outreach. |
| [Playbook](/api-reference/schemas/playbook) | Detailed representation of a playbook, returned by get/create/update responses. |
| [Playbook Style File](/api-reference/schemas/playbook-style-file) | A single style file attached to a playbook. |
| [Reengagement Card](/api-reference/schemas/reengagement-card) | Detailed representation of a reengagement card. |
| [Reply Handler](/api-reference/schemas/reply-handler) | Detailed representation of a reply handler. |
## Reports & analytics
| Schema | Description |
| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| [Calls Overview Response](/api-reference/schemas/calls-overview-response) | Aggregated call statistics with per-member breakdown |
| [Channel Efficiency Overview Response](/api-reference/schemas/channel-efficiency-overview-response) | Cross-channel efficiency statistics |
| [Emails Overview Response](/api-reference/schemas/emails-overview-response) | Email delivery and engagement metrics |
| [LinkedIn Overview Response](/api-reference/schemas/linkedin-overview-response) | LinkedIn engagement metrics overview |
| [Sequence Stats Response](/api-reference/schemas/sequence-stats-response) | Aggregated email and LinkedIn stats for a single sequence |
| [Tasks Overview Response](/api-reference/schemas/tasks-overview-response) | Aggregated task statistics broken down by type, with per-member breakdown |
| [Team Performance Overview Response](/api-reference/schemas/team-performance-overview-response) | Team performance metrics including meetings, contacts, and engagement rates |
## Live data
| Schema | Description |
| ----------------------------------------------------------------- | ----------------------------------------------------------------- |
| [Live Data search](/api-reference/schemas/search) | Summary of a Live Data search. |
| [Live Data search details](/api-reference/schemas/search-details) | A single Live Data search, including the filters it was run with. |
## Attachments
| Schema | Description |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| [Attachment](/api-reference/schemas/attachment) | Uploaded file attachment usable across email templates, sequence steps, and direct emails. |
| [Voice Attachment](/api-reference/schemas/voice-attachment) | Uploaded voice message attachment. |
## Background jobs
| Schema | Description |
| ------------------------------------------------------- | ------------------------------------------------------- |
| [Background Job](/api-reference/schemas/background-job) | A long-running background operation tracked by the API. |
## Webhooks
| Schema | Description |
| ------------------------------------------------------- | -------------------------------------------------------------------- |
| [Webhook](/api-reference/schemas/webhook) | A webhook subscription. |
| [Webhook Events](/api-reference/schemas/webhook-events) | Catalog of event types that a webhook subscription can subscribe to. |
| [Webhook Log](/api-reference/schemas/webhook-log) | One delivery-attempt record for a webhook subscription. |
## Common
| Schema | Description |
| ----------------------------------------------------------------------------- | ------------------------------------------------------ |
| [Create Item Result](/api-reference/schemas/create-item-result) | Per-item result for non-atomic bulk create operations. |
| [Not Processed Item Result](/api-reference/schemas/not-processed-item-result) | Per-item error for non-atomic bulk operations. |
| [Validation Error](/api-reference/schemas/validation-error) | A single field-level validation error. |
| [Validation Problem](/api-reference/schemas/validation-problem) | Input-validation error response at 400. |
## Other
| Schema | Description |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| [AI Prompt](/api-reference/schemas/ai-prompt) | An AI prompt available for configuring a sequence step. |
| [Business Problem](/api-reference/schemas/business-problem) | Domain error response carrying a stable, namespaced error slug. |
| [Count Result](/api-reference/schemas/count-result) | |
| [Custom Field](/api-reference/schemas/custom-field) | A user-defined field for storing additional data on contacts. |
| [Email Validation Estimate](/api-reference/schemas/email-validation-estimate-response) | Synchronous probe result returned by `/v3/email-validations/estimate`. |
| [Email Validation Schedule](/api-reference/schemas/email-validation-schedule-response) | Acknowledgement returned by `/v3/email-validations/schedule`. |
| [Intent Signal Industry](/api-reference/schemas/intent-industry-item) | A single industry entry from the typeahead list, used in intent-signal configuration. |
| [Intent Signal Technology](/api-reference/schemas/intent-technology-item) | A single technology entry from the typeahead list, used in intent-signal configuration. |
| [Problem Details](/api-reference/schemas/problem-details) | Bare RFC 9457 problem-details envelope. |
| [Settings](/api-reference/schemas/settings) | Team and user settings. |
# Bulk add contacts to sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/bulk-add-contacts-to-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/contact-links/bulk
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Add multiple contacts to the sequence at once
# Bulk remove contacts from sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/bulk-remove-contacts-from-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/contact-links/bulk-delete
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Remove multiple contacts from the sequence at once
# Count contacts in sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/count-contacts-in-sequence
/api-reference/bundled.yaml get /v3/sequences/{id}/contacts/count
**Coming soon.** This endpoint will be available by late August 2026.
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns the total number of contacts enrolled in the sequence.
# Get a contact in a sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/get-a-contact-in-a-sequence
/api-reference/bundled.yaml get /v3/sequences/{id}/contacts/{contact_id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns details of a specific contact enrolled in the sequence.
# Get sequence preview for a contact
Source: https://docs.reply.io/api-reference/sequence-contacts/get-sequence-preview-for-a-contact
/api-reference/bundled.yaml get /v3/sequences/{sequence_id}/contacts/{contact_id}/preview
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns the per-contact preview of the messages this sequence will send, including any AI-personalized step templates.
# List contacts in sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/list-contacts-in-sequence
/api-reference/bundled.yaml get /v3/sequences/{id}/contacts
_Requires the `sequences:read` scope (or a broader one that includes it)._
Retrieve all contacts enrolled in the sequence. Defaults to most-recently-added first; use `sort_by` and `sort_direction` to override.
# List contacts in sequence with extended state
Source: https://docs.reply.io/api-reference/sequence-contacts/list-contacts-in-sequence-with-extended-state
/api-reference/bundled.yaml get /v3/sequences/{id}/contacts/state
_Requires the `sequences:read` scope (or a broader one that includes it)._
Retrieve contacts enrolled in the sequence with optional additional columns such as current step, last step completed at, and status.
The response always includes the base fields: `contactId`, `email`, `firstName`, `lastName`, `title`, `addedAt`. Additional fields (`currentStep`, `lastStepCompletedAt`, `status`) are **only** included when explicitly requested via the `additionalColumns` query parameter. Omitting `additionalColumns` returns only the base fields.
# Mark or unmark contacts as bounced in this sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/mark-or-unmark-contacts-as-bounced-in-this-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/contacts/set-bounced
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Marks the given contacts as bounced (`isBounced: true`) or clears the bounced flag (`isBounced: false`), scoped to **this sequence only**.
When clearing the flag (`isBounced: false`), `resendEmails` is required: pass `true` to retry the previously bounced step or `false` to leave the schedule untouched. When marking as bounced (`isBounced: true`), `resendEmails` must be omitted.
To apply across every sequence a contact is in, use `POST /v3/contacts/set-bounced` instead.
# Mark or unmark contacts as replied in this sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/mark-or-unmark-contacts-as-replied-in-this-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/contacts/set-replied
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Marks the given contacts as replied (`isReplied: true`) or clears the replied flag (`isReplied: false`), scoped to **this sequence only**.
To apply across every sequence a contact is in, use `POST /v3/contacts/set-replied` instead.
# Remove contact from sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/remove-contact-from-sequence
/api-reference/bundled.yaml delete /v3/sequences/{id}/contact-links/{contact_id}
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Remove a contact from the sequence
# Reset manual edits in a sequence preview
Source: https://docs.reply.io/api-reference/sequence-contacts/reset-manual-edits-in-a-sequence-preview
/api-reference/bundled.yaml post /v3/sequences/{sequence_id}/contacts/{contact_id}/preview/reset
**Beta.** This endpoint is in beta. Behavior, parameters, and response shapes may change without notice.
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Reverts any manual edits the user has made to the preview for a contact and restores the AI-generated templates.
# Set contacts' status in this sequence
Source: https://docs.reply.io/api-reference/sequence-contacts/set-contacts-status-in-this-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/contacts/set-status-in-sequence
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Sets the in-sequence status (`active` / `paused` / `finished` / `outOfOffice`) for the given contacts, scoped to **this sequence only**.
To apply across every sequence a contact is in, use `POST /v3/contacts/set-status-in-sequence` instead.
Replied and Bounced are **not** values of this enum — use `POST /v3/sequences/{id}/contacts/set-replied` or `POST /v3/sequences/{id}/contacts/set-bounced` for those.
`paused` requires the contact to currently be `active` in this sequence — pausing from any other state is reported per-item as `invalidStatusTransition`. The other targets (`active`, `finished`, `outOfOffice`) accept any current state.
# Assign email account to sequence
Source: https://docs.reply.io/api-reference/sequence-email-accounts/assign-email-account-to-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/email-account-links
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Links an email account to the sequence so it can be used for sending outbound emails.
# List email accounts in sequence
Source: https://docs.reply.io/api-reference/sequence-email-accounts/list-email-accounts-in-sequence
/api-reference/bundled.yaml get /v3/sequences/{id}/email-accounts
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns the list of email accounts currently linked to the specified sequence.
# Remove email account from sequence
Source: https://docs.reply.io/api-reference/sequence-email-accounts/remove-email-account-from-sequence
/api-reference/bundled.yaml delete /v3/sequences/{id}/email-account-links/{email_account_id}
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Unlinks an email account from the sequence. The email account itself is not deleted.
# Set sequence email accounts
Source: https://docs.reply.io/api-reference/sequence-email-accounts/set-sequence-email-accounts
/api-reference/bundled.yaml put /v3/sequences/{id}/email-account-links
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Replaces all email account links for the sequence with the provided list.
Any previously linked accounts not in the new list will be unlinked.
# Bulk assign sequences to a folder
Source: https://docs.reply.io/api-reference/sequence-folders/bulk-assign-sequences-to-a-folder
/api-reference/bundled.yaml post /v3/sequence-folders/{id}/sequence-links/bulk
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Assigns multiple sequences to a folder. Returns a map of sequence IDs that could not be processed with their error details.
# Bulk unassign sequences from a folder
Source: https://docs.reply.io/api-reference/sequence-folders/bulk-unassign-sequences-from-a-folder
/api-reference/bundled.yaml post /v3/sequence-folders/{id}/sequence-links/bulk-delete
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Removes multiple sequences from a folder. Returns a map of sequence IDs that could not be processed with their error details.
# Create a sequence folder
Source: https://docs.reply.io/api-reference/sequence-folders/create-a-sequence-folder
/api-reference/bundled.yaml post /v3/sequence-folders
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new sequence folder
# Delete a sequence folder
Source: https://docs.reply.io/api-reference/sequence-folders/delete-a-sequence-folder
/api-reference/bundled.yaml delete /v3/sequence-folders/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes an existing sequence folder
# Get a sequence folder
Source: https://docs.reply.io/api-reference/sequence-folders/get-a-sequence-folder
/api-reference/bundled.yaml get /v3/sequence-folders/{id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a sequence folder by Id
# List all sequence folders
Source: https://docs.reply.io/api-reference/sequence-folders/list-all-sequence-folders
/api-reference/bundled.yaml get /v3/sequence-folders
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a list of all sequence folders for the current user
# List sequences in a folder
Source: https://docs.reply.io/api-reference/sequence-folders/list-sequences-in-a-folder
/api-reference/bundled.yaml get /v3/sequence-folders/{id}/sequences
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a paginated list of sequences in a folder
# Update a sequence folder
Source: https://docs.reply.io/api-reference/sequence-folders/update-a-sequence-folder
/api-reference/bundled.yaml put /v3/sequence-folders/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing sequence folder
# Assign a LinkedIn account to a sequence
Source: https://docs.reply.io/api-reference/sequence-linkedin-accounts/assign-a-linkedin-account-to-a-sequence
/api-reference/bundled.yaml post /v3/sequences/{id}/linkedin-account-links
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Links a LinkedIn account to the specified sequence
# List LinkedIn accounts for a sequence
Source: https://docs.reply.io/api-reference/sequence-linkedin-accounts/list-linkedin-accounts-for-a-sequence
/api-reference/bundled.yaml get /v3/sequences/{id}/linkedin-accounts
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all LinkedIn accounts available for the sequence
# Remove a LinkedIn account from a sequence
Source: https://docs.reply.io/api-reference/sequence-linkedin-accounts/remove-a-linkedin-account-from-a-sequence
/api-reference/bundled.yaml delete /v3/sequences/{id}/linkedin-account-links/{linkedInAccountId}
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Unlinks a LinkedIn account from the specified sequence
# Bulk create sequence steps
Source: https://docs.reply.io/api-reference/sequence-steps/bulk-create-sequence-steps
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/bulk
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates multiple steps in a sequence at once. Returns a list of results for each step. Email-step variants may carry up to **3** `attachmentIds` each; inaccessible attachment IDs surface as a per-item `forbidden` result (upload first via `POST /v3/attachments`).
# Bulk create step variants
Source: https://docs.reply.io/api-reference/sequence-steps/bulk-create-step-variants
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/{step_id}/variants/bulk
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates multiple text variants for a sequence step at once. For email steps, each variant may carry up to **3** `attachmentIds` (upload first via `POST /v3/attachments`); inaccessible attachment IDs surface as a per-item `forbidden` result.
# Bulk delete sequence steps
Source: https://docs.reply.io/api-reference/sequence-steps/bulk-delete-sequence-steps
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/bulk-delete
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes multiple steps from a sequence at once. Returns a map of step IDs that could not be processed with their error details.
# Bulk delete step variants
Source: https://docs.reply.io/api-reference/sequence-steps/bulk-delete-step-variants
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/{step_id}/variants/bulk-delete
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes multiple variants from a sequence step at once. Returns a map of variant IDs that could not be processed with their error details.
# Create a sequence step
Source: https://docs.reply.io/api-reference/sequence-steps/create-a-sequence-step
/api-reference/bundled.yaml post /v3/sequences/{id}/steps
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new step in a sequence. For bulk creation, use the `/steps/bulk` endpoint.
For email steps, each variant may carry up to **3** `attachmentIds` (upload first via `POST /v3/attachments`); attachments that don't belong to the caller (or the caller's team) are rejected with `403 sequenceStep.forbidden`.
# Create a step variant
Source: https://docs.reply.io/api-reference/sequence-steps/create-a-step-variant
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/{step_id}/variants
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new variant for a sequence step. Supports both text variants (`application/json`) and voice message variants (`multipart/form-data`).
For email-step text variants, the request may carry up to **3** `attachmentIds` (upload first via `POST /v3/attachments`). Inaccessible attachment IDs are rejected with `403 sequenceStep.forbidden`.
# Delete a sequence step
Source: https://docs.reply.io/api-reference/sequence-steps/delete-a-sequence-step
/api-reference/bundled.yaml delete /v3/sequences/{id}/steps/{step_id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes a sequence step.
By default only the addressed step is removed (`mode=nodeOnly`), and any children remain attached to the parent. For condition (branch) steps, set `mode` to also remove descendants on one or both branches.
# Delete a step variant
Source: https://docs.reply.io/api-reference/sequence-steps/delete-a-step-variant
/api-reference/bundled.yaml delete /v3/sequences/{id}/steps/{step_id}/variants/{variant_id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes an existing variant from a sequence step
# Disable step variants
Source: https://docs.reply.io/api-reference/sequence-steps/disable-step-variants
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/{step_id}/disable
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Disables the specified variants on a sequence step. Variants are toggled independently — failures are reported per `variantId` in the response without aborting the whole call.
# Enable step variants
Source: https://docs.reply.io/api-reference/sequence-steps/enable-step-variants
/api-reference/bundled.yaml post /v3/sequences/{id}/steps/{step_id}/enable
_Requires the `sequences:operate` scope (or a broader one that includes it)._
Enables the specified variants on a sequence step. Variants are toggled independently — failures are reported per `variantId` in the response without aborting the whole call.
# Get a sequence step
Source: https://docs.reply.io/api-reference/sequence-steps/get-a-sequence-step
/api-reference/bundled.yaml get /v3/sequences/{id}/steps/{step_id}
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns details of a specific sequence step
# List all sequence steps
Source: https://docs.reply.io/api-reference/sequence-steps/list-all-sequence-steps
/api-reference/bundled.yaml get /v3/sequences/{id}/steps
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns a list of all steps in a sequence
# List contact filter properties
Source: https://docs.reply.io/api-reference/sequence-steps/list-contact-filter-properties
/api-reference/bundled.yaml get /v3/sequences/steps/contact-filter-properties
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all available contact filter properties that can be used in sequence step conditions.
Each property includes its supported operators and, for enum-like properties, the allowed values.
This is a static discovery endpoint — the response does not depend on user data or any specific sequence.
# List step variants
Source: https://docs.reply.io/api-reference/sequence-steps/list-step-variants
/api-reference/bundled.yaml get /v3/sequences/{id}/steps/{step_id}/variants
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all variants for a sequence step
# Update a sequence step
Source: https://docs.reply.io/api-reference/sequence-steps/update-a-sequence-step
/api-reference/bundled.yaml put /v3/sequences/{id}/steps/{step_id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing sequence step. Each variant in the request body must include its `id` to identify which variant to update.
For email steps, each variant may carry up to **3** `attachmentIds` (upload first via `POST /v3/attachments`); attachments that don't belong to the caller (or the caller's team) are rejected with `403 sequenceStep.forbidden`.
# Update a step variant
Source: https://docs.reply.io/api-reference/sequence-steps/update-a-step-variant
/api-reference/bundled.yaml put /v3/sequences/{id}/steps/{step_id}/variants/{variant_id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Updates an existing variant for a sequence step. Supports both text variants (`application/json`) and voice message variants (`multipart/form-data`).
For email-step text variants, the request may carry up to **3** `attachmentIds` (upload first via `POST /v3/attachments`). Inaccessible attachment IDs are rejected with `403 sequenceStep.forbidden`.
# Delete a sequence template
Source: https://docs.reply.io/api-reference/sequence-templates/delete-a-sequence-template
/api-reference/bundled.yaml delete /v3/sequence-templates/{id}
_Requires the `sequences:write` scope (or a broader one that includes it)._
Deletes an existing sequence template.
- **Global templates** (seeded by the system) cannot be deleted — returns 403.
- **User-scoped templates** are team-shared: any teammate may delete a template visible to them. Out-of-team or non-existent ids return 404.
- **Organization templates** require the `DeleteOrgTemplates` feature scope — callers without it get 403.
# List all sequence templates
Source: https://docs.reply.io/api-reference/sequence-templates/list-all-sequence-templates
/api-reference/bundled.yaml get /v3/sequence-templates
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns all sequence templates grouped by scope (global, organization, team)
# Create a sequence from a template
Source: https://docs.reply.io/api-reference/sequences/create-a-sequence-from-a-template
/api-reference/bundled.yaml post /v3/sequences/create-from-template
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new sequence based on an existing sequence template
# Get sequence stats
Source: https://docs.reply.io/api-reference/sequences/get-sequence-stats
/api-reference/bundled.yaml post /v3/sequences/{id}/stats
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns aggregated email and LinkedIn engagement stats for a single sequence.
# Get stats for all sequences
Source: https://docs.reply.io/api-reference/sequences/get-stats-for-all-sequences
/api-reference/bundled.yaml post /v3/sequences/stats
_Requires the `sequences:read` scope (or a broader one that includes it)._
Returns email and LinkedIn engagement stats per sequence.
**Date range restrictions:**
- `dateRangePreset` must be `lastWeek` or `lastMonth`. `lastYear` and `allTime` are rejected.
- When using `from`/`to`, the span must not exceed 31 days.
- `from` must be within the last 31 days from the current date.
- Defaults to `lastWeek` if no date filters are provided.
# Save a sequence as a template
Source: https://docs.reply.io/api-reference/sequences/save-a-sequence-as-a-template
/api-reference/bundled.yaml post /v3/sequences/{id}/save-as-template
_Requires the `sequences:write` scope (or a broader one that includes it)._
Creates a new sequence template from an existing sequence
# Get settings
Source: https://docs.reply.io/api-reference/settings/get-settings
/api-reference/bundled.yaml get /v3/settings
_Requires the `settings:read` scope (or a broader one that includes it)._
Returns the full settings object for the authenticated user's team, including account profile, email configuration, LinkedIn, calls, contacts, and beta features.
# Update settings
Source: https://docs.reply.io/api-reference/settings/update-settings
/api-reference/bundled.yaml patch /v3/settings
_Requires the `settings:write` scope (or a broader one that includes it)._
Performs a sparse update of settings. Only properties present in the request body are updated — omitted properties are left unchanged.
Nested sections follow the same rule: include only the fields you want to change within each section.
**Contact stages and account stages** are an exception — when the `contactStages` or `accountStages` array is present, all 8 stages must be provided with unique IDs (1–8).
# Batch complete tasks
Source: https://docs.reply.io/api-reference/tasks/batch-complete-tasks
/api-reference/bundled.yaml post /v3/tasks/batch/complete
_Requires the `tasks:operate` scope (or a broader one that includes it)._
Completes multiple tasks without execution. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Batch reassign tasks
Source: https://docs.reply.io/api-reference/tasks/batch-reassign-tasks
/api-reference/bundled.yaml post /v3/tasks/batch/assign
_Requires the `tasks:write` scope (or a broader one that includes it)._
Reassigns multiple tasks to a user. The target user must be a member of the caller's team.
Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Bulk delete tasks
Source: https://docs.reply.io/api-reference/tasks/bulk-delete-tasks
/api-reference/bundled.yaml post /v3/tasks/bulk-delete
_Requires the `tasks:write` scope (or a broader one that includes it)._
Deletes multiple tasks by ID. Non-atomic — some may succeed while others fail.
Items not in the response dictionary succeeded.
# Complete a task
Source: https://docs.reply.io/api-reference/tasks/complete-a-task
/api-reference/bundled.yaml post /v3/tasks/{id}/complete
_Requires the `tasks:operate` scope (or a broader one that includes it)._
Marks a task as completed without execution (no email sent, no SMS sent).
For tasks that require execution (sending), use the execute endpoint instead.
# Create a task
Source: https://docs.reply.io/api-reference/tasks/create-a-task
/api-reference/bundled.yaml post /v3/tasks
_Requires the `tasks:write` scope (or a broader one that includes it)._
Creates a new task. The template body may contain handlebars variables
(e.g. `{{firstName}}`) which are resolved when the task is read or executed.
# Delete a task
Source: https://docs.reply.io/api-reference/tasks/delete-a-task
/api-reference/bundled.yaml delete /v3/tasks/{id}
_Requires the `tasks:write` scope (or a broader one that includes it)._
Deletes a single task by ID. Returns 204 on success.
# Execute and complete a task
Source: https://docs.reply.io/api-reference/tasks/execute-and-complete-a-task
/api-reference/bundled.yaml post /v3/tasks/{id}/execute
_Requires the `tasks:operate` scope (or a broader one that includes it)._
Executes the task action (sends email or SMS) and completes it.
Only ManualEmail (7) and Sms (8) task types support execution.
Other task types return 400 — use the complete endpoint for those.
# Filter tasks
Source: https://docs.reply.io/api-reference/tasks/filter-tasks
/api-reference/bundled.yaml post /v3/tasks/filter
_Requires the `tasks:read` scope (or a broader one that includes it)._
Returns a filtered, paginated list of tasks.
Pagination params (`top`, `skip`) are in query string; filter criteria in request body.
All filter fields are optional — an empty body `{}` returns all tasks.
The request body must be a valid JSON object; a missing or unparseable body returns 400.
# Get a task
Source: https://docs.reply.io/api-reference/tasks/get-a-task
/api-reference/bundled.yaml get /v3/tasks/{id}
_Requires the `tasks:read` scope (or a broader one that includes it)._
Returns full task details by ID.
Use `includeContent=true` to also get the rendered content (variables resolved).
# List all tasks
Source: https://docs.reply.io/api-reference/tasks/list-all-tasks
/api-reference/bundled.yaml get /v3/tasks
_Requires the `tasks:read` scope (or a broader one that includes it)._
Returns a paginated list of all tasks visible to the current user.
For filtered search, use POST /tasks/filter.
# Reassign a task
Source: https://docs.reply.io/api-reference/tasks/reassign-a-task
/api-reference/bundled.yaml put /v3/tasks/{id}/assigned-user
_Requires the `tasks:write` scope (or a broader one that includes it)._
Changes the assigned user for a single task. The target user must be a member of the caller's team.
# Update a task
Source: https://docs.reply.io/api-reference/tasks/update-a-task
/api-reference/bundled.yaml put /v3/tasks/{id}
_Requires the `tasks:write` scope (or a broader one that includes it)._
Full replacement update of a task. Only tasks with status `New` (0) can be updated.
Sequence task type and contact cannot be changed.
# Get current user
Source: https://docs.reply.io/api-reference/user-account/get-current-user
/api-reference/bundled.yaml get /v3/whoami
_No scope required — any valid API key can call this endpoint._
Returns the authenticated user's ID, username, and team ID. Useful for verifying credentials and identifying the current user and their team.
# List team users
Source: https://docs.reply.io/api-reference/user-account/list-team-users
/api-reference/bundled.yaml get /v3/whoami/team-users
_No scope required — any valid API key can call this endpoint._
Returns all users in the authenticated user's team (workspace), including each user's team ID, team name, user ID, display name, and email address.
# Create a webhook subscription
Source: https://docs.reply.io/api-reference/webhooks/create-a-webhook-subscription
/api-reference/bundled.yaml post /v3/webhooks
_Requires the `webhooks:write` scope (or a broader one that includes it)._
Creates a new webhook subscription for the authenticated user.
`scope` defaults to `personal` when omitted. `team`-scope subscriptions are accepted
only when policy allows: any member may create them for public teams or organization
mode; only the team owner may create them for private teams.
Attempting to create an `organization`-scope subscription is rejected with
`code: webHook.organizationScopeNotImplemented` — the value is reserved for future use.
# Delete a webhook subscription
Source: https://docs.reply.io/api-reference/webhooks/delete-a-webhook-subscription
/api-reference/bundled.yaml delete /v3/webhooks/{id}
_Requires the `webhooks:write` scope (or a broader one that includes it)._
Permanently deletes the subscription and its delivery-log payloads.
# Disable a webhook subscription
Source: https://docs.reply.io/api-reference/webhooks/disable-a-webhook-subscription
/api-reference/bundled.yaml post /v3/webhooks/{id}/disable
_Requires the `webhooks:operate` scope (or a broader one that includes it)._
Pauses a subscription without deleting it — no events fire until re-enabled.
Idempotent: calling on an already-disabled subscription still returns `204`.
Use this dedicated endpoint rather than `PUT` to toggle the paused state
(`PUT` does not accept the `enabled` flag).
# Enable a webhook subscription
Source: https://docs.reply.io/api-reference/webhooks/enable-a-webhook-subscription
/api-reference/bundled.yaml post /v3/webhooks/{id}/enable
_Requires the `webhooks:operate` scope (or a broader one that includes it)._
Resumes a paused subscription. Idempotent — calling on an already-enabled
subscription still returns `204`. Use this dedicated endpoint rather than
`PUT` to toggle the paused state (`PUT` does not accept the `enabled` flag).
When the active-subscriptions limit has been reached, returns `409 Conflict`
with `code: webHook.limitReached`.
# Get a webhook subscription
Source: https://docs.reply.io/api-reference/webhooks/get-a-webhook-subscription
/api-reference/bundled.yaml get /v3/webhooks/{id}
_Requires the `webhooks:read` scope (or a broader one that includes it)._
Returns a single webhook subscription owned by the authenticated user.
# Get webhook delivery logs
Source: https://docs.reply.io/api-reference/webhooks/get-webhook-delivery-logs
/api-reference/bundled.yaml get /v3/webhooks/{id}/logs
_Requires the `webhooks:read` scope (or a broader one that includes it)._
Returns a paginated **failure log** for a single subscription — only failed
delivery attempts are recorded here; successful deliveries are not listed. Each
log entry captures the triggering event id, HTTP status (or `null` when the
request could not be completed) and the UTC timestamp of the attempt.
# List supported event types
Source: https://docs.reply.io/api-reference/webhooks/list-supported-event-types
/api-reference/bundled.yaml get /v3/webhooks/events
_Requires the `webhooks:read` scope (or a broader one that includes it)._
Returns the catalog of event type names a subscription may listen to. Use any of
these values in the `eventType` field when creating or updating a subscription.
# List webhook subscriptions
Source: https://docs.reply.io/api-reference/webhooks/list-webhook-subscriptions
/api-reference/bundled.yaml get /v3/webhooks
_Requires the `webhooks:read` scope (or a broader one that includes it)._
Returns a paginated list of the authenticated user's webhook subscriptions, ordered
by descending id. Use `top`/`skip` to page through results and inspect `hasMore` to
detect additional pages.
# Send a test payload
Source: https://docs.reply.io/api-reference/webhooks/send-a-test-payload
/api-reference/bundled.yaml post /v3/webhooks/{id}/test
_Requires the `webhooks:operate` scope (or a broader one that includes it)._
Triggers a synthetic delivery to the subscription's URL so the integrator can
validate wiring. Only enabled subscriptions can be tested.
# Update a webhook subscription
Source: https://docs.reply.io/api-reference/webhooks/update-a-webhook-subscription
/api-reference/bundled.yaml put /v3/webhooks/{id}
_Requires the `webhooks:write` scope (or a broader one that includes it)._
Full replacement of the mutable fields of an existing subscription. `organization`
scope is rejected with `code: webHook.organizationScopeNotImplemented`.
# Using the CLI from agents & scripts
Source: https://docs.reply.io/cli/agents
The reply CLI's machine contract — JSON output, exit codes, the two error shapes, retries, impersonation headers, and composition patterns.
The `reply` CLI is built to be driven by coding agents, shell scripts, and CI. It handles the parts
that are tedious to reimplement — browser login, token refresh, the team header, retries — and
exposes the whole [v3 API](/api-reference/introduction) through
[`reply api`](/cli/overview#raw-api-access). That command is the agent surface today; there are no
dedicated resource commands yet.
## The `--json` contract
Every command accepts `--json` (compact) and `--pretty` (indented). **Data goes to stdout, status
and errors go to stderr** — pipes stay clean:
```bash theme={null}
reply api /v3/sequences --json | jq '.data.items[].id'
reply auth whoami --json | jq -r '.username'
reply team list --json | jq -r '.teams[].team_id'
```
`reply api` always emits JSON, with or without a flag — `{"code": , "data": }`.
`--pretty` only changes the indentation.
## Exit codes
| Code | Meaning |
| ---- | ---------------------------------------------------------------------------------- |
| `0` | Success |
| `1` | API failure (HTTP `>= 400`) or runtime failure (network, corrupt credential store) |
| `2` | Usage error — unknown flag, missing argument, no credential to use |
## The two error shapes
**`reply api` never hides an HTTP response.** Any status, including `4xx` and `5xx`, is printed on
stdout as `{code, data}`, where `data` is the v3 `problem+json` body verbatim. The exit code is `1`
for `>= 400`. Branch on `.code`:
```bash theme={null}
out=$(reply api /v3/contacts/999) || true
echo "$out" | jq -e '.code == 404' >/dev/null && echo "not found"
```
**Every other command** reports failures as a single machine-readable line on **stderr** when
`--json` or `--pretty` is set:
```json theme={null}
{"error":{"status":404,"code":"contact.notFound","title":"Not Found","detail":"Contact with ID 999 not found","hint":"Resource not found."}}
```
Branch on `code`, surface `hint` to the user. Every field except `title` is optional.
## Retries and rate limits
Handled inside the CLI, on both paths:
* `429`, `500`, `502`, `503`, and `504` are retried up to 3 times.
* `Retry-After` is honored when present, capped at 30 seconds; otherwise the backoff is exponential
from 500 ms.
* Network failures are retried on the same schedule, then surface as a runtime error (exit `1`).
Don't wrap your own retry loop around write calls — a retried `POST` can double-create. Verify with
a follow-up `GET` instead. [Rate limits](/api-reference/rate-limits) documents the API-side windows.
## Teams and impersonation
Requests carry the team header from `--team-id` → `REPLY_TEAM_ID` → the profile's pinned team. If a
call needs a team and the credential spans several, the API returns `TEAM_REQUIRED` and the CLI adds
a one-line fix-it hint on stderr. Pin one non-interactively:
```bash theme={null}
reply team use 1045 # persists on the active profile
reply --team-id 1045 api /v3/sequences # or scope a single call
```
With an [organization API key](/api-reference/authentication#special-api-keys), name the acting user
with `--user-id ` or `--user-email ` (which also needs a team id). Pass exactly one.
These are **flag-only** — never read from the environment, never written to disk — so an agent must
supply them per invocation:
```bash theme={null}
reply --api-key "$ORG_KEY" --user-id 12345 api /v3/whoami
reply --api-key "$ORG_KEY" --team-id 1045 --user-email rep@acme.com api /v3/sequences
```
Rejection codes for organization keys are listed under
[Rejection responses](/api-reference/authentication#rejection-responses).
## Safety is the caller's job
The CLI has **no `--dry-run` and no confirmation gates**. `reply api` sends exactly the request you
give it — a `DELETE` deletes, and a `POST /v3/sequences/{id}/start` begins real outreach. The only
interactive prompt in the whole CLI is `reply profile delete`, which `-y` skips.
An agent driving `reply api` owns the guardrails the [platform safety rules](/agents/safety)
describe: confirm high-stakes actions with the user first, echo the exact request you're about to
send, and prefer a read-back over a retry. Two habits that help:
* Resolve ids with a `GET` before mutating; never invent numeric ids.
* Use `--verbose` while developing a call — it prints the full request and response to stderr with
credentials redacted, leaving stdout pipeable.
## Non-interactive environments
CI and agent sandboxes have no browser, so `reply auth login` won't work. Supply a key instead:
```bash theme={null}
export REPLY_API_KEY="$REPLY_KEY" # or pass --api-key per call
reply auth whoami --json # verify before doing work
```
Add `-q/--quiet` to silence progress messages on stderr; warnings and errors are still printed.
`REPLY_CONFIG_DIR` relocates the credential store when the default home directory isn't writable.
## Composition patterns
```bash theme={null}
# every active sequence's id and name, as TSV
reply api '/v3/sequences?status=active' --json \
| jq -r '.data.items[] | [.id, .name] | @tsv'
# pause every active sequence whose reply rate was under 1% last week
reply api /v3/sequences/stats --body '{"filters":{"dateRangePreset":"lastWeek"}}' --json \
| jq -r '.data[] | select(.status == "active" and .emailOverview.repliedPercentage < 1) | .sequenceId' \
| xargs -I{} reply api /v3/sequences/{}/pause --method POST
# fail a CI job when the credential no longer works
reply auth whoami --json >/dev/null || exit 1
```
The CLI composes with cron for scheduled jobs, `jq` for filtering, and the
[Reply Skills](/skills/overview) for procedure — the skill supplies the plan, the CLI executes it.
Install them with `reply skills install`.
## Drop-in prompt
A ready-to-paste system prompt encoding this contract lives at
[Drop-in system prompts → CLI variant](/agents/prompts#cli-variant).
# Reply CLI
Source: https://docs.reply.io/cli/overview
The Reply.io command line — sign in once, then run any v3 API call as yourself from your terminal, your scripts, or CI.
[`reply`](https://www.npmjs.com/package/reply-cli) is the official command-line interface for
Reply.io. Sign in once and every request runs as you. Today it covers **authentication and
identity**, [`reply api`](#raw-api-access) gives you authenticated access to the full
[v3 API](/api-reference/introduction) — no HTTP client, no header wrangling — and
[`reply skills`](#skills) installs Reply's agent skill packs into your AI assistants. Higher-level
commands for sequences, contacts, and the inbox are on the way.
| | |
| ----------- | -------------------------------------------------------------------------- |
| **Package** | [`reply-cli`](https://www.npmjs.com/package/reply-cli) (npm) |
| **Command** | `reply` |
| **Runtime** | Node.js 20 or newer |
| **Auth** | OAuth browser login, or an API key |
| **Output** | Human-readable by default; `--json` / `--pretty` for machines |
| **Source** | [github.com/reply-team/reply-cli](https://github.com/reply-team/reply-cli) |
| **Status** | Pre-1.0 — see [Versioning](#versioning) |
## Install
```bash theme={null}
npm install -g reply-cli # installs the `reply` command (Node 20+)
reply --version
```
Or run it without installing:
```bash theme={null}
npx -y reply-cli auth login
```
## Authenticate
Log in through your browser — no key to copy, no key to rotate:
```bash theme={null}
reply auth login
```
Prefer an API key? Read it from stdin so it never lands in your shell history:
```bash theme={null}
reply auth login --with-token
```
Keys come from Reply → **Settings → API Keys**; scopes and key types are covered in
[Authentication](/api-reference/authentication).
Inspect and manage the active credential:
```bash theme={null}
reply auth status # who you're signed in as, and how — no secrets shown
reply auth whoami # validate the stored credential against the API
reply auth logout # remove the stored credential
```
### Credential precedence
Resolved in strict order, first hit wins:
1. `--api-key ` flag
2. `REPLY_API_KEY` environment variable
3. the stored credential from `reply auth login`
The flag and the environment variable are used for that one invocation and are never written to
disk.
There is no `.env` file lookup — export `REPLY_API_KEY` yourself, or store a credential with
`reply auth login`.
### Where credentials live
Credentials are stored as JSON in `~/.config/reply` (`%APPDATA%\reply` on Windows), written `0600`
inside a `0700` directory — the same plaintext-file model as `gh`, `aws`, and `az`. OAuth tokens
refresh automatically; if a refresh fails the record is cleared and you're asked to log in again.
Tokens and keys are redacted in every command's output, `--verbose` included.
## Profiles — more than one Reply.io user
A profile is a signed-in account. Profiles let you keep several Reply.io users authenticated at
once and switch between them, each with its own stored credential. Most people never need one: the
default profile is used until you create another.
Name them however you like — account emails work well:
```bash theme={null}
reply profile add alice@reply.io
reply profile use alice@reply.io # make it the active profile
reply auth login # signs in alice@reply.io
reply profile list # '*' marks the active profile
reply --profile bob@reply.io auth whoami # override for a single command
```
The active profile resolves as `--profile` → `REPLY_PROFILE` → the profile set with
`profile use` → the built-in default.
Manage them after creation:
```bash theme={null}
reply profile show # the active profile — team, auth method, no secrets
reply profile show alice@reply.io # a specific one
reply profile rename alice@reply.io ally # also moves the stored credential
reply profile delete ally # removes it and its stored credential
```
## Teams
A profile can pin a team (workspace). The pinned team is sent as the `X-TEAM-ID` header, with
precedence `--team-id` → `REPLY_TEAM_ID` → the profile's team. The `team` command reads and writes
the **active profile's** team:
```bash theme={null}
reply team list # teams you can act in ('*' marks the profile's team)
reply team current # the pinned team, plus the effective team from whoami
reply team use 1045 # verify 1045 is one of your teams, then pin it
reply team clear # remove the pin
```
If a call needs a team and you belong to more than one, the API answers with a `TEAM_REQUIRED`
error listing your teams — run `reply team use ` to pin one, or pass `--team-id ` for a
single command.
## Raw API access
`reply api` is a raw, authenticated passthrough to any v3 endpoint — the escape hatch for
everything the CLI has no dedicated command for yet.
Use the path exactly as it appears in the [API reference](/api-reference/introduction), starting
with `/v3`; the query string goes in the path. A `--body` switches the method to `POST` (override
with `--method`), and it accepts inline JSON, `@file`, or `-` for stdin.
```bash theme={null}
reply api /v3/whoami # your identity + team
reply api /v3/sequences # list sequences
reply api /v3/contacts --pretty # list contacts, indented
reply api /v3/sequences/12345 # one sequence by id
reply api /v3/contacts --body @contact.json # create a contact (POST)
echo '' | reply api /v3/contacts --body - # body from stdin
reply api /v3/whoami --verbose # full request/response on stderr
```
It prints `{ "code": , "data": }` and exits non-zero on HTTP `>= 400`. See
[Using the CLI from agents & scripts](/cli/agents) for the full machine contract.
## Skills
Reply's outbound expertise ships as three markdown skill packs in
[reply-skills](https://github.com/reply-team/reply-skills). `reply skills` detects the AI assistants
on your machine and installs the packs into each one, resolving dependencies itself:
```bash theme={null}
reply skills install
```
```
✓ detected Claude Code, Codex
✓ Claude Code · ai-sdr-core, reply-adapter, agentic-runtime installed
✓ Codex · ai-sdr-core, reply-adapter, agentic-runtime installed
Start a new session in each assistant so the skills load.
```
| Pack | Alias | What it gives your agent |
| ----------------- | --------- | -------------------------------------------------------- |
| `ai-sdr-core` | `core` | Vendor-neutral SDR operations, playbooks, and guardrails |
| `reply-adapter` | `adapter` | Executing those operations against Reply.io |
| `agentic-runtime` | `runtime` | Durable multi-session work: plans, checkpoints, reports |
Naming no pack installs all three. Dependencies come along automatically, so `adapter` pulls `core`:
```bash theme={null}
reply skills install core
reply skills install adapter runtime
reply skills install --agent codex # only this assistant
reply skills install --project # into this repository, not your home directory
```
Then manage them:
```bash theme={null}
reply skills list # what's installed where, and what has an update available
reply skills update # bring installed packs to the latest version
reply skills remove runtime # remove one pack
reply skills remove # remove all of them
```
On Claude Code and Codex the packs are installed through the assistant's own plugin mechanism, so
they keep updating through it. Other `SKILL.md` hosts receive the skills as copied files. Add
`--json` for a machine-readable report and `--dry-run` to see the plan without changing anything.
| Assistant | `--agent` id | How it receives the packs | Paths verified |
| -------------- | ---------------- | ----------------------------------------------------- | -------------- |
| Claude Code | `claude-code` | its own plugin CLI | Yes |
| Codex | `codex` | its own plugin CLI (`--project` copies files instead) | Yes |
| Cursor | `cursor` | copied files | Not yet |
| Gemini CLI | `gemini-cli` | copied files | Not yet |
| GitHub Copilot | `github-copilot` | copied files | Not yet |
| Windsurf | `windsurf` | copied files | Not yet |
**"Not yet" means that assistant's skills directory comes from its own documentation** and has not
been confirmed by a verification run — the install works, but Reply cannot promise the assistant
reads from where the files were put. Those hosts are marked `(paths not yet verified)` in the
report and carry `"verified": false` in `--json`.
Removing a pack another installed pack depends on is refused — remove both, or run
`reply skills remove` with no pack to remove everything.
Installing skills is not the same as connecting Reply: `reply-adapter` needs a Reply.io credential
(`reply auth login`) to do anything. See [Reply Skills](/skills/overview).
## Command reference
Run `reply --help` for the exact flags of any command — the installed help always matches
your version.
### `reply auth`
| Command | Description |
| ------------------------------- | ---------------------------------------------------------------------- |
| `reply auth login` | Log in via OAuth in the browser |
| `reply auth login --with-token` | Store an API key read from stdin |
| `reply auth status` | Active credential source, method, user, and OAuth expiry — no secrets |
| `reply auth whoami` | Validate the active credential against the API and print the principal |
| `reply auth logout` | Remove the stored credential |
### `reply profile`
| Command | Description |
| ----------------------------------------- | -------------------------------------------------------------------- |
| `reply profile add ` | Create a profile (`--team-id` optionally pins a team) |
| `reply profile use ` | Make it the active profile, used until changed |
| `reply profile list` | List profiles; `*` marks the active one |
| `reply profile current` | Print the active profile name |
| `reply profile show [name]` | Show a profile's team and authorization — no secrets |
| `reply profile set --team-id ` | Pin a team on a profile |
| `reply profile unset team-id` | Clear the pinned team |
| `reply profile rename ` | Rename a profile and move its stored credential |
| `reply profile delete ` | Delete a profile and its stored credential (`-y` to skip the prompt) |
### `reply team`
| Command | Description |
| --------------------- | ------------------------------------------------------- |
| `reply team list` | Teams you can act in |
| `reply team current` | The active profile's pinned team and the effective team |
| `reply team use ` | Pin a team (verified against your teams) |
| `reply team clear` | Remove the pin |
### `reply api`
| Command | Description |
| ------------------------------------------ | ----------------------------------------------------------------- |
| `reply api ` | Raw authenticated request to a v3 endpoint; prints `{code, data}` |
| `reply api --body ` | Send a body (switches the method to `POST`) |
| `reply api --method ` | Override the HTTP method |
### `reply skills`
| Command | Description |
| --------------------------------- | ---------------------------------------------------------------- |
| `reply skills install [packs...]` | Install packs into every detected assistant (default: all three) |
| `reply skills list [packs...]` | Show which packs are installed in which assistant |
| `reply skills update [packs...]` | Update installed packs to the latest published version |
| `reply skills remove [packs...]` | Remove packs from your assistants (default: all of them) |
Pack names accept aliases: `core`, `adapter`, `runtime`. These flags apply to all four subcommands:
| Flag | Effect |
| --------------------- | ------------------------------------------------------------------ |
| `-a, --agent ` | Target these assistants instead of auto-detecting |
| `--project` | Install into the current repository instead of your user directory |
| `--dry-run` | Show what would happen and change nothing |
### Global flags
| Flag | Effect |
| ---------------------- | -------------------------------------------------------------------------------------------------- |
| `-k, --api-key ` | Use this key for one invocation; overrides the env var and stored credential |
| `-p, --profile ` | Run as this profile for one invocation |
| `--team-id ` | Team/workspace to act in, sent as `X-TEAM-ID` |
| `--user-id ` | Act as this user id — [organization API keys](/api-reference/authentication#special-api-keys) only |
| `--user-email ` | Act as this user email — organization keys only; requires a team id |
| `--json` | Compact JSON on stdout |
| `--pretty` | Indented JSON on stdout |
| `--verbose` | Full request/response on stderr, credentials redacted |
| `-q, --quiet` | Suppress progress messages on stderr; warnings and errors still shown |
| `-v, --version` | Print the CLI version |
### Environment variables
| Variable | Description |
| ------------------ | -------------------------------------------------------------------------- |
| `REPLY_API_KEY` | API key used as the credential for the current invocation |
| `REPLY_PROFILE` | Profile to use (same as `--profile`) |
| `REPLY_TEAM_ID` | Team/workspace id sent as `X-TEAM-ID` (same as `--team-id`) |
| `REPLY_CONFIG_DIR` | Config directory (default `~/.config/reply`; `%APPDATA%\reply` on Windows) |
## Why a CLI when there's REST and MCP?
* **Authentication is the hard part, and it's solved here.** Browser login, token refresh, multiple
accounts, and the team header are handled for you — `reply api` is a `curl` that is already
authenticated as the right user in the right workspace.
* **Coding agents are exceptionally good at shell commands.** They get composability — pipes, `jq`,
cron — without writing an HTTP client. See [Using the CLI from agents](/cli/agents).
* **Scriptable one-offs.** Terminal and CI access to the API for work that doesn't warrant a real
integration.
## What isn't in the CLI yet
There are no dedicated commands for sequences, contacts, the inbox, or reporting yet — reach them
through `reply api` today, or use the interface that fits:
| Need | Where today |
| ------------------------------------- | --------------------------------------------------------- |
| Any v3 endpoint, authenticated | `reply api /v3/…` |
| A tool surface for LLM agents | [Reply MCP](/mcp/overview) |
| Procedure and guardrails for an agent | [Reply Skills](/skills/overview) — `reply skills install` |
| Your own integration | [REST API](/api-reference/introduction) |
| Event-driven automation | [Webhooks](/webhook-events) |
## Versioning
`reply-cli` follows [semantic versioning](https://semver.org), with versions derived from
Conventional Commits.
**Pre-1.0.** While the version is `0.x` the CLI is still stabilizing, so — per semver's 0.x rule —
a minor release (`0.x.0`) may contain breaking changes. Pin an exact version if you need stability
before `1.0.0`.
From `1.0.0` onward, a major bump is required to remove or rename a command, flag, or argument;
change the meaning of an exit code; change the `--json` / `--pretty` output shape; change the config
format or a `REPLY_*` environment variable; or raise the minimum Node.js version. New commands, new
optional flags, and new fields in `--json` output are additive and ship in minor releases.
## Links
* Package: [npmjs.com/package/reply-cli](https://www.npmjs.com/package/reply-cli)
* Source and issues: [github.com/reply-team/reply-cli](https://github.com/reply-team/reply-cli)
* Next: [Using the CLI from agents & scripts](/cli/agents)
# Decide what to scale or fix
Source: https://docs.reply.io/guides/analyze-performance
Turn raw reporting data into decisions — what to scale, pause, or rewrite.
**Outcome:** a performance readout (delivered, opens, replies, interested, meetings) with a
diagnosis and one or two recommended actions.
## Best interface
**CLI for reporting agents** — [`reply api`](/cli/overview#raw-api-access) with `--json` makes the
reporting endpoints pipeable and cron-able. **MCP for Q\&A-style analysis** — "compare Q3 sequences"
resolves to `reply_compare_sequence_performance` in one call.
## Steps
Both endpoints take a `filters` object; `dateRangePreset` accepts `lastWeek`, `lastMonth`,
`lastYear`, or `allTime`.
```bash theme={null}
reply api /v3/reporting/emails/overview \
--body '{"filters":{"dateRangePreset":"lastWeek"}}' --json
reply api /v3/sequences/stats \
--body '{"filters":{"dateRangePreset":"lastWeek"}}' --json
```
```bash theme={null}
reply api /v3/sequences/12345/stats \
--body '{"filters":{"dateRangePreset":"lastMonth"}}' --json
```
MCP: `reply_get_sequence_stats` (one sequence, custom date window) or
`reply_compare_sequence_performance` (up to 25 side by side — align by SequenceId, treat
missing rows as no data).
Benchmark deliverability (bounces), copy (opens vs replies), and targeting (replies vs
interested). Flag stalled or degraded sequences. The
[`performance-analysis` skill](/skills/catalog#performance-analysis) packages this diagnosis.
Pausing sequences or editing copy are production changes: recommend, confirm, then execute
([Manage sequences](/guides/improve-sequences) has the loop).
## Availability notes
* Reports, per-sequence stats, and comparisons: **available today**.
* Stats endpoints can throttle earlier than CRUD endpoints — budget calls accordingly
([Rate limits](/api-reference/rate-limits)).
* Meetings are booked via Reply's in-app scheduler; the API **reports** them (meetings ledger) but
can't book them.
## Related
* [Improve sequences every cycle](/guides/improve-sequences) — acting on the diagnosis
* [Skill: `performance-analysis`](/skills/catalog#performance-analysis)
# Automate outbound end-to-end
Source: https://docs.reply.io/guides/automate-outbound
Event-driven and scheduled automation — webhooks for real-time reactions, CLI + cron for recurring hygiene.
**Outcome:** workflows that run without anyone asking — inbound leads answered in minutes, lists
that stay clean, dormant contacts revived on a schedule.
## Best interface
**API + webhooks** — event-driven automation is REST-only: subscribe once, react in real time.
**CLI + cron** — scheduled jobs like a weekly hygiene sweep are a natural fit for
[`--json` composition](/cli/agents#composition-patterns).
## Pattern 1: Event-driven (inbound lead follow-up)
Speed-to-lead is most of the conversion rate. This loop is event-driven end to end — no polling:
A [webhook event](/webhook-events) (or your product's own signup/demo-request event) tells the
agent a lead arrived. Payload shapes: [Webhook event payloads](/webhook-event-payloads).
Create/update the contact with source context in custom fields
([Create contacts](/guides/import-contacts)); score against your ICP.
Auto-enrichment of the lead is **coming soon** (Enrichment API).
Hot leads → short, direct sequence; colder → nurture. Add the contact and start
([Launch outreach](/guides/launch-outreach)). For an immediate one-off first response outside
a sequence, use the outreach-action endpoints ([API Reference](/api-reference/introduction)).
Watch reply categories and meeting intent ([Process replies](/guides/convert-replies-to-meetings));
meetings and activity land in reporting automatically.
## Pattern 2: Scheduled (list hygiene + re-engagement)
Deliverability is a compounding asset. A weekly cron sweep keeps the base clean, then re-engages
what survives:
```bash theme={null}
reply api '/v3/contacts/filter?top=1000' \
--body '{"rules":[{"property":"","condition":"","value":""}]}' --json
```
Filter by last activity, bounce history, or missing fields. The available `property` and
`condition` names are listed with the Filter contacts endpoint in the
[API Reference](/api-reference/introduction); page with `top` and `skip` until `hasMore` is
`false`.
Schedule email validation across the segment; add bounced domains, opt-outs, and competitor
domains to blacklist rules (Suppression & Blacklist API — see
[API Reference](/api-reference/introduction)).
Bulk-update or remove dead records; move survivors into clean lists.
Run a light-touch sequence (2–3 low-pressure touches) against contacts that finished without
converting. Load reengagement cards into a Jason knowledge base for a fresh angle
([Build an AI SDR](/guides/build-an-ai-sdr)).
Re-enrichment before re-engagement ("did they change jobs?") is **coming soon**.
Compare bounce and reply rates before/after with `POST /v3/reporting/emails/overview` (its
`compareTo` field returns the trend against a previous period); track the revival rate to decide
if the sweep becomes quarterly.
## Availability notes
* Webhooks, contact CRUD, validation, suppression, sequences, and reporting: **available today**.
* Enrichment-dependent steps (lead auto-enrichment, dormant-refresh): **coming soon**.
## Related
* [Webhook events catalog](/webhook-events) · [Payloads](/webhook-event-payloads)
* [Using the CLI from agents & scripts](/cli/agents)
* [Convert replies into meetings](/guides/convert-replies-to-meetings) · [Build a supervised AI SDR](/guides/build-an-ai-sdr)
# Build a supervised AI SDR
Source: https://docs.reply.io/guides/build-an-ai-sdr
Give Jason a goal, knowledge, and guardrails — then supervise through approval gates instead of operating by hand.
**Outcome:** a supervised, self-improving outbound system — Jason (Reply's AI SDR) writes and sends
outreach and handles replies, while your agent supervises through approval queues and performance
data. The highest-altitude pattern: an agent configuring an agent.
## Best interface
**MCP** — the Jason toolset is richest there: 31 of the 70 tools drive knowledge bases, offers,
playbooks, reply handlers, reengagement cards, and the approval queue. The REST API covers the
knowledge/offer/playbook layer.
## Steps
Translate the objective into an offer, a target market, and success metrics. Draft offer
content from a website with MCP `reply_generate_offer_from_website` (review, then
`reply_create_offer`).
`reply_create_knowledge_base` → add URL sources (`reply_add_knowledge_base_source`), reply
handlers (how Jason answers pricing/security/… questions), and reengagement cards (win-back
plays). See [MCP tools → Jason AI SDR](/mcp/tools#jason-ai-sdr).
The offer says *what's being sold and to whom*; the playbook says *how* (tone, structure,
channels). `reply_create_offer`, `reply_create_playbook`, then attach both to the sequence.
Enroll contacts you already have ([Launch outreach](/guides/launch-outreach)).
Intent-signal targeting and Live Data sourcing are **coming soon** — see [Find prospects](/guides/find-ideal-prospects).
`reply_set_sequence_reply_mode` → **Review** (Jason drafts, you approve) is the safe default.
**Autonomous** (Jason sends without approval) is a [high-stakes switch](/agents/safety) —
verify offer/playbook/knowledge base first and confirm explicitly. Recipe:
[Switch Jason to Autonomous](/mcp/recipes#switch-jason-to-autonomous).
`reply_list_pending_approvals` → approve (sends immediately), regenerate with feedback, or
reject (removes the contact from the sequence — irreversible). Recipe:
[Supervise Jason in Review mode](/mcp/recipes#supervise-jason-in-review-mode).
Replies flow into the unified inbox with AI categorization; escalate high-intent threads to a
human. Read the meetings ledger and per-sequence stats; refine the offer, playbook, and
targeting based on what converts ([Analyze performance](/guides/analyze-performance)).
## Availability notes
* Knowledge bases, reply handlers, reengagement cards, offers, playbooks, reply-mode control, and
the approval queue: **available today via MCP** (and the AI SDR REST module).
* **Coming soon:** intent-signal prospect targeting; full autopilot enrollment loops. Everything
labeled available above is callable now.
## Related
* [MCP recipes: Supervise Jason / Switch to Autonomous](/mcp/recipes#supervise-jason-in-review-mode)
* [Convert replies into meetings](/guides/convert-replies-to-meetings) — the human-approved counterpart
* [Agent safety rules](/agents/safety) — why Autonomous is gated
# Convert replies into meetings
Source: https://docs.reply.io/guides/convert-replies-to-meetings
Triage the inbox, surface interested prospects, and send approved replies — event-driven or conversational.
**Outcome:** every reply triaged, interested prospects surfaced, responses sent with explicit
approval — from both email and LinkedIn, in one inbox.
## Best interface
**The [`inbox-triage` skill](/skills/catalog#inbox-triage)** for approved, gated sending.
**Webhooks for real-time** — subscribe to [`email_replied` and `reply_categorized`
events](/webhook-events) instead of polling. **MCP** for conversational triage in a client.
## Steps
Real-time: subscribe to [webhook events](/webhook-events) — `email_replied`,
`reply_categorized` — pushed to your endpoint (see
[payloads](/webhook-event-payloads)). Polling:
`reply api /v3/inbox/threads/filter --body '{"source":"unread"}'` or MCP
`reply_get_inbox_emails`.
Group threads by category and meeting intent. LinkedIn and email replies land in the same
unified inbox with the same categories.
```bash theme={null}
reply api /v3/inbox/threads/categories --json # resolve category ids first
reply api /v3/inbox/threads/filter \
--body '{"categoryIds":[7],"onlyWithMeetingIntent":true}' --json
```
```bash theme={null}
reply api /v3/inbox/threads/58217 --json
reply api /v3/inbox/threads/58217/messages --json
```
Sending reaches a real prospect — a [high-stakes action](/agents/safety). Nothing here prompts
for confirmation, so show the exact text, get approval, then send. The body's `channel` must
match the thread's, or the call is rejected with `inboxThread.channelMismatch`.
```bash theme={null}
reply api /v3/inbox/threads/58217/messages \
--body '{"channel":"email","message":"Thanks — does Tuesday at 10am work?","applySignature":true}' --json
```
MCP: `reply_send_inbox_reply` (channel must match the thread; body only in v1 — no Cc/Bcc,
attachments, or scheduling). See the [MCP recipe](/mcp/recipes#reply-to-an-inbox-thread).
```bash theme={null}
reply api /v3/inbox/threads/58217/category --method PUT --body '{"categoryId":42}' --json
```
Route meeting-intent threads to scheduling; mark contacts as replied to stop further automated
outreach (MCP: `reply_mark_contacts_as_replied`).
## Availability notes
* Inbox triage, replying, categorization, and webhooks: **available today**.
* **Fully automated reply policies (auto-answer) are not in Skill v1** — every send is
human-approved. For AI-drafted replies with an approval queue, see
[Build an AI SDR](/guides/build-an-ai-sdr).
## Related
* [Build a supervised AI SDR](/guides/build-an-ai-sdr) — Jason drafts the replies, you approve
* [Automate outbound end-to-end](/guides/automate-outbound) — the event-driven pattern in full
* [Webhook events catalog](/webhook-events)
# Find your ideal prospects
Source: https://docs.reply.io/guides/find-ideal-prospects
From an ICP definition to a saved, deduplicated prospect list — what works today and what's coming.
**Outcome:** a contact list of prospects matching your ideal customer profile, ready for outreach.
The end-to-end path — searching Reply's 1B+ B2B database from an ICP — depends on the **Live
Data / prospect-search API, which is not generally available yet**. The same applies to
waterfall enrichment. This guide describes what works today and labels the rest honestly.
## Best interface today
**Bring your own list.** Until prospect search ships, source prospects externally and enter the
platform at the import step — [Create and import contacts](/guides/import-contacts) covers the
fully-available path (bulk import with dedupe via the API or Skills).
## Steps
Turn the goal into concrete filters: job titles, seniority, department, industry, company
size, location. An agent can derive this from your best customers' domains and reporting data.
The [`campaign-planning`](/skills/catalog#campaign-planning) skill covers turning a goal into
constraints, and [`user-memory`](/skills/catalog#user-memory) keeps the resulting ICP for later
sessions.
Live Data search over the 1B+ contact database with result preview before committing credits.
Ships as an API module and a `reply` CLI command.
Import selected prospects as contact records organized into a named list — today via CSV:
[Create and import contacts](/guides/import-contacts).
Waterfall enrichment (emails, phones, firmographics) depends on the Enrichment API. Until
then, source enrichment externally before import.
Schedule email validation on the list to protect deliverability — part of
[Automate workflows](/guides/automate-outbound).
## Availability notes
* Prospect search & enrichment: **coming soon** — they'll appear as API modules and CLI commands when
the endpoints ship.
* Everything downstream of a list you already have is fully available today.
## Related
* [Import and clean contacts](/guides/import-contacts) — the available entry point
* [Launch multichannel outreach](/guides/launch-outreach) — the next step once the list exists
* [Skill: `audience-building`](/skills/catalog#audience-building) — shaping a list you already have
# Import and clean contacts
Source: https://docs.reply.io/guides/import-contacts
CSV in, deduplicated contacts out — mapped, verified, and organized into a list ready for outreach.
**Outcome:** clean contact records in your workspace, deduplicated, validated, and organized into a
list ready for sequencing.
## Best interface
**API for bulk import** — one `POST /v3/contacts/import` call handles dedupe, list assignment, and
per-item outcomes. Drive it from your own code, or from the terminal with
[`reply api`](/cli/overview#raw-api-access). **MCP** for conversational, one-at-a-time creation.
## Steps
Import takes JSON, not CSV — convert your spreadsheet first. The body is
`{"items": [...], "options": {...}}`, where each item uses the contact fields and `options`
controls dedupe and list assignment. **Maximum 1000 items per request** — batch anything larger.
```json theme={null}
{
"items": [
{"email": "john@acme.com", "firstName": "John", "lastName": "Doe", "company": "Acme"}
],
"options": {"skipExisting": true, "listIds": [42]}
}
```
```bash theme={null}
reply api /v3/contacts/import --body @import.json --json
```
The response reports added / updated / skipped / failed counts with a per-item outcome — report
exact numbers rather than "done". Nothing is written until you send this call, so validate the
payload locally first: there is no server-side dry-run.
For one-at-a-time or system-to-system sync, use the Contacts endpoints in the
[API Reference](/api-reference/introduction), or `reply_create_contact` /
`reply_update_contact` via [MCP](/mcp/tools#contacts) (needs a name plus email or LinkedIn URL).
Schedule email validation on the list — never send to a dead address. Available today via the
API; part of the [list-hygiene loop](/guides/automate-outbound).
```bash theme={null}
reply api /v3/contact-lists/42 --json
reply api '/v3/contacts/filter?top=100' --body '{"listId":42}' --json | jq '.data.items | length'
```
## Availability notes
* CSV import, contact CRUD, lists, and email validation: **available today**.
* Waterfall **enrichment** of imported records (fill missing emails/phones/firmographics):
**coming soon** — see [Find prospects](/guides/find-ideal-prospects#availability-notes).
## Related
* [Launch multichannel outreach](/guides/launch-outreach) — enroll the list into a sequence
* [Automate outbound end-to-end](/guides/automate-outbound) — keep the list clean on a schedule
* [Skill: `audience-building`](/skills/catalog#audience-building)
# Improve sequences every cycle
Source: https://docs.reply.io/guides/improve-sequences
Operate running sequences — A/B variants, per-step stats, pausing losers, promoting winners.
**Outcome:** sequences that measurably improve every cycle — data-picked copy on every step, losers
paused, winners promoted to templates.
## Best interface
**CLI for ops loops** — [`reply api`](/cli/overview#raw-api-access) against the stats and
lifecycle endpoints composes with `jq` and cron for continuous
optimization. **MCP interactively** — inspect steps and variants, compare sequences, pause/start
conversationally.
## Steps
Openers and first follow-ups move the numbers hardest. Add message variants via the Sequences
API (see [API Reference](/api-reference/introduction)); inspect existing ones via MCP:
`reply_get_sequence_steps` → `reply_get_sequence_step_variants`.
Run the sequence; the platform distributes contacts across enabled variants automatically.
```bash theme={null}
reply api /v3/sequences/12345/stats \
--body '{"filters":{"dateRangePreset":"lastMonth"}}' --json
```
MCP: `reply_get_sequence_stats` for one, `reply_compare_sequence_performance` for up to 25
side by side.
```bash theme={null}
reply api /v3/sequences/stats --body '{"filters":{"dateRangePreset":"lastMonth"}}' --json \
| jq -r '.data[] | select(.status == "active" and .emailOverview.repliedPercentage < 1) | .sequenceId' \
| xargs -I{} reply api /v3/sequences/{}/pause --method POST
```
The list endpoint accepts only `lastWeek` or `lastMonth`. Disable underperforming variants
mid-flight via the API; pause whole sequences as above or via MCP.
Save winning messages as email templates so every future sequence starts from proven copy —
Templates via the [API](/api-reference/introduction).
Confirm the aggregate effect over time with `POST /v3/reporting/emails/overview`, or the
[`performance-analysis` skill](/skills/catalog#performance-analysis).
## Availability notes
* Everything here is **available today**. Per-contact status changes inside a sequence
(pause one person, mark finished) are richest via MCP: `reply_change_status_in_sequence`.
## Related
* [Decide what to scale or fix](/guides/analyze-performance) — the diagnosis layer
* [Launch multichannel outreach](/guides/launch-outreach) — starting new sequences
# Launch multichannel outreach
Source: https://docs.reply.io/guides/launch-outreach
From a contact list to a running multichannel sequence — accounts checked, contacts enrolled, start confirmed.
**Outcome:** a running sequence with assigned mailboxes, a schedule, and enrolled contacts — started
only after explicit confirmation.
## Best interface
**The [`campaign-launch` skill](/skills/catalog#campaign-launch)** runs the whole checklist and
gates the start on your explicit confirmation. **In an MCP client**, follow the
[Launch a sequence safely recipe](/mcp/recipes#launch-a-sequence-safely) — same checklist as MCP
tool calls.
## Steps
Email steps need a connected mailbox; LinkedIn steps need a linked LinkedIn account.
API: `GET /v3/email-accounts`, `GET /v3/linkedin-accounts` (`reply api /v3/email-accounts`).
MCP: `reply_list_email_accounts`, `reply_list_linkedin_accounts`.
API: `POST /v3/sequences` to create, `GET /v3/sequences` to pick an existing one.
MCP: `reply_search_sequences`. For LinkedIn-first cadences, lead with LinkedIn steps and fall
back to email for prospects who don't accept.
MCP: `reply_assign_email_account_to_sequence`, `reply_assign_schedule_to_sequence` — a
sequence without a mailbox fails to start with `NoEmailAccounts`.
Add contacts by id, in bulk. Resolve the ids first — never guess them.
```bash theme={null}
reply api /v3/sequences/12345/contact-links/bulk \
--body '{"contactIds":[101,102],"removeFromExisting":false}' --json
```
Per-contact skips (already in sequence, blacklisted) are reported, not failures — read them
and report exact counts.
Starting begins real outreach — a [high-stakes action](/agents/safety) on every surface. Nothing
below asks for confirmation, so ask the user yourself before you run it.
```bash theme={null}
reply api /v3/sequences/12345/start --method POST --json
```
MCP: confirm with the user, then `reply_start_sequence`.
API: `reply api /v3/sequences/12345 --json` · MCP: `reply_get_sequence_stats` or
`reply_search_sequences` to confirm the status is Active.
## Availability notes
* Everything in this guide is **available today**, including LinkedIn steps (with a linked
LinkedIn account).
* Complex step editing (A/B variants on live sequences, LinkedIn step authoring) is handed off to
the Reply app in Skill v1.
## Related
* [Improve sequences every cycle](/guides/improve-sequences) — optimize what's now running
* [Convert replies into meetings](/guides/convert-replies-to-meetings) — handle the responses
* [MCP recipe: Launch a sequence safely](/mcp/recipes#launch-a-sequence-safely)
# Building agents
Source: https://docs.reply.io/mcp/agent-guide
Design principles, high-stakes gating, and a drop-in system prompt for agents that use Reply MCP.
Reply MCP executes a successful call **immediately** — there is no undo. These principles keep an
agent safe and effective. They pair with the [tool contract](/mcp/tool-contract) (how calls behave)
and the [workflow recipes](/mcp/recipes) (concrete call graphs). For the platform-wide,
surface-independent versions of these rules, see [Agent safety rules](/agents/safety) and
[Drop-in system prompts](/agents/prompts).
## Design principles
### Resolve before you mutate
Almost every mutating tool takes exact numeric IDs (sequence, contact, task, thread, user,
email-account, LinkedIn-account, schedule, offer, playbook, knowledge-base). **Never invent an ID,
and never ask the user for one** — users don't know internal IDs. Search → confirm the match →
act → verify:
```text theme={null}
User: "Add John from Acme to the Q3 outbound sequence."
1. reply_search_contacts(email or LinkedIn URL) — this tool needs an email/LinkedIn,
not a name; if you only have a name, ask the user for an email first.
2. reply_search_sequences(name: "Q3 outbound")
3. If exactly one contact and one sequence match, confirm and call
reply_add_contact_to_sequence(sequenceId, [contactId]).
4. Read NotProcessed in the result; verify with reply_get_contact_activity(contactId).
```
### Gate high-stakes actions
Confirm with the user before anything that sends to a prospect, starts outreach, enrolls contacts,
changes ownership, blacklists, bulk-approves, or switches Jason to Autonomous. Two traps worth
naming:
* **`reply_reject_message` doesn't just discard a draft** — it removes the contact from the
sequence entirely. To merely revise a draft, use `reply_regenerate_message`.
* **`reply_send_inbox_reply` supports only `threadId` + `channel` + `message` in v1** — no Cc/Bcc,
attachments, or scheduling; 32,000-char max; the channel must match the thread's.
### Separate discovery, configuration, and execution
Discover objects and IDs (`search_*` / `list_*` / `get_*`), configure the sequence (mailboxes,
schedules, offers, knowledge bases, playbooks, reply mode), then execute (start, approve, send,
complete). `reply_start_sequence` fails with `NoEmailAccounts` or `NoContacts` if you skip
configuration.
### MCP is the interactive surface; REST is the exhaustive one
These 70 tools cover most day-to-day operations. Drop to the [REST API](/api-reference/introduction)
(`api.reply.io/v3`) for bulk imports and updates, background jobs, deep report exports, and anything
absent from `tools/list`.
## High-stakes tools
Each of these reaches a prospect or changes ownership/state the instant it succeeds. Require
explicit confirmation in almost any agent UX:
| Tool | Why it's high-stakes |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reply_start_sequence` | Begins outreach processing |
| `reply_add_contact_to_sequence` | Enrolls people into outreach |
| `reply_send_inbox_reply` | Sends a real message to a contact |
| `reply_approve_message` / `reply_bulk_approve_messages` | Send Jason's drafts (bulk is atomic, ≤100) |
| `reply_reject_message` | **Irreversible** — discards the draft AND removes the contact from the sequence. Use `reply_regenerate_message` if the user merely dislikes the draft |
| `reply_blacklist_contact` | Suppresses an email (or, with `blockEntireDomain`, a whole company domain — including future contacts) from all outreach |
| `reply_change_contact_owner` | Changes visibility and responsibility; trust only `AffectedContactIds` when reporting what changed |
| `reply_set_sequence_reply_mode` → `Autonomous` | Jason sends without per-message review |
Usually worth confirming too (production-affecting, but not prospect-visible): `reply_pause_sequence`,
`reply_change_status_in_sequence`, the `reply_assign_*` / `reply_attach_*` configuration tools,
`reply_complete_task`, and any create/update/delete on knowledge bases, reply handlers, reengagement
cards, offers, or playbooks. The server's `destructiveHint` annotation marks all 39 mutations — use
it as the machine-readable gate.
## Drop-in system prompt
Add this to an agent's system prompt when it has Reply MCP tools:
```text theme={null}
You have access to Reply.io MCP tools. For every request:
1. Classify it: read-only, configuration, outreach-changing, or prospect-visible send.
The server annotates every tool (readOnlyHint / destructiveHint) — honor them.
2. For any mutation, resolve exact numeric IDs first with search/list/filter tools.
Never guess an ID and never ask the user for one.
3. For high-stakes actions (send, approve, reject, start, enroll, blacklist, change owner,
go Autonomous), summarize the target and side effect, then ask the user to confirm.
Remember: reject_message removes the contact from the sequence — it is not "discard draft".
4. Parse results from result.content[0].text: {"Success":true,"Data":...} or
{"Success":false,"ErrorCode":...,"ErrorMessage":...}. Branch on ErrorCode.
Read per-item results (NotProcessed / AffectedContactIds) and report exact counts.
5. Do not pass unknown arguments (schemas are additionalProperties:false) and never send
empty strings/arrays for required fields.
6. Paginate with top/skip until HasMore is false.
7. On failure, follow the tool's own failure-mode guidance. Do not blindly retry sends,
approvals, starts, or enrollments — a timeout may already have succeeded; verify first.
8. If no tool fits, try reply_get_app_map and reply_search_knowledge_base before saying
you can't; then reply_report_unsupported_request if it is truly missing.
```
Next: [workflow recipes](/mcp/recipes) turn these principles into concrete call graphs.
# Connect
Source: https://docs.reply.io/mcp/connect
Add Reply MCP to Claude, Cursor, or any MCP-compatible client.
Connect Reply MCP to your AI client in two steps: get your API key, then point your client at
`https://mcp.reply.io/`.
## 1) Get your API key
Reply MCP authenticates with your Reply.io **personal API key**. Create or copy one under
**Settings → API Key** in the [Reply dashboard](https://run.reply.io).
A personal API key is the preferred credential because it's **scoped**: you grant it only the
permissions your agent needs, and it can call only the tools those permissions cover. A scope is
a permission such as `contacts:read` or `sequences:operate`, grouped by area (contacts,
sequences, inbox, tasks, channels, AI SDR), so you can give an agent exactly the reach it needs
and nothing more. Set the scopes on the key in **Settings → API Key**; see
[Scopes](/api-reference/authentication#scopes) for the full list.
The MCP connection URL is `https://mcp.reply.io/`. You can also open it in the app under
**Execution → Integrations → Reply MCP Server → Configure**, which is where you set up and
configure the MCP connection.
Treat your API key like a password. Don't share it in public repositories, screenshots, or
chat messages. If it's exposed, generate a new API key in Reply.
## 2) Add it to your client
### Header auth (recommended)
Most modern MCP clients support remote Streamable HTTP servers with header auth. Point the client
at `https://mcp.reply.io/` and pass your key in the `Authorization` header as a bearer token.
```json theme={null}
{
"mcpServers": {
"reply": {
"type": "http",
"url": "https://mcp.reply.io/",
"headers": { "Authorization": "Bearer YOUR_REPLY_API_KEY" }
}
}
}
```
**Claude Code**, one line:
```bash theme={null}
claude mcp add --transport http reply https://mcp.reply.io/ \
--header "Authorization: Bearer YOUR_REPLY_API_KEY"
```
### OAuth (clients without header auth)
Some clients don't let you set an API key or custom headers — typically the **UI / desktop apps**
of Claude, ChatGPT, and Codex, where you add a connector by URL only. For those, use OAuth: add
Reply MCP as a remote/custom connector with the URL `https://mcp.reply.io/` and authorize when
prompted — no key to copy or store.
OAuth grants access to the **full tool catalog** — it's equivalent to an API key with **all
scopes**, and there's no per-scope control over an OAuth connection. So whenever your client does
support header auth, prefer a scoped personal API key (above) to limit what the agent can do.
Some OAuth clients don't support Client ID Metadata Documents (CIMD) and fall back to Dynamic
Client Registration, which is allow-listed on our side. If your client reports an allow-list or
registration error when connecting, let us know in the [community Slack](/slack) and we'll
review it.
### Fallback: `mcp-remote` (clients without native remote MCP)
If your client only supports local (stdio) MCP servers, bridge to the remote server with the
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote) proxy and pass your key in the
`Authorization` header:
```json theme={null}
{
"mcpServers": {
"reply": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://mcp.reply.io/",
"--header",
"Authorization: Bearer YOUR_REPLY_API_KEY"
]
}
}
}
```
Passing credentials in the URL (for example `?api_key=...`) is not supported. Always
authenticate with the `Authorization` bearer header.
## Handshake
Once connected, MCP clients drive the standard flow automatically. If you're building a client by
hand:
1. Open an HTTP connection to `https://mcp.reply.io/`.
2. Send a JSON-RPC `initialize` with your protocol version and client info.
3. Send the `notifications/initialized` notification if your client library requires it.
4. Call `tools/list` to discover tools, their JSON schemas, and their annotations.
5. Call `tools/call` with a tool name and an argument object.
```json initialize theme={null}
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "your-client", "version": "1.0.0" }
}
}
```
No `Mcp-Session-Id` is required — `initialize` and `tools/list` work without a session.
## Authentication details
An unauthenticated call returns `401 Unauthorized` with an OAuth challenge header:
```http theme={null}
WWW-Authenticate: Bearer resource_metadata="https://mcp.reply.io/.well-known/oauth-protected-resource/"
```
The published protected-resource metadata (`GET /.well-known/oauth-protected-resource/`):
```json theme={null}
{
"resource": "https://mcp.reply.io",
"authorization_servers": ["https://oauth.sandbox.replyapp.io"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["reply-web-api", "mcp:tools"]
}
```
For most agents a scoped personal API key is the recommended path — it gives the finest-grained
control over what the agent can do (see [Scopes](/api-reference/authentication#scopes)). The
OAuth metadata exists for clients (e.g. Claude custom connectors) that prefer a discovery-based
flow; OAuth grants the **full tool catalog** (all scopes), so prefer a scoped personal API key
when you want to limit an agent's reach.
### Unsupported keys
Reply MCP is a single-user connection, so **act-on-behalf API keys are not supported** —
including team-level **Team keys** and **Organization API keys**. These are designed to make
requests on behalf of other users, which a single-user connection doesn't provide. Use a
personal API key or OAuth instead.
## Rate limits
The server enforces an hourly window (\~3000 requests/hour). Every response carries the current
budget:
```http theme={null}
X-Rate-Limit-Limit: 1h
X-Rate-Limit-Remaining: 2997
X-Rate-Limit-Reset: 2026-07-05T00:00:00.0000000Z
```
On `429`, back off until the `X-Rate-Limit-Reset` timestamp before retrying.
## Related
* [Reply MCP overview](/mcp/overview) — what the server exposes
* [Tool reference](/mcp/tools) — all 70 tools
* [Connecting Reply MCP to your AI model (Help Center)](https://support.reply.io/en/articles/12955018-connecting-reply-mcp-to-your-ai-model)
# Reply MCP overview
Source: https://docs.reply.io/mcp/overview
The official, live Reply.io MCP server — 70 tools for sequences, contacts, inbox, tasks, and the Jason AI SDR autopilot.
Reply MCP is an official, live, remote [Model Context Protocol](https://modelcontextprotocol.io)
server that exposes Reply.io's sales-engagement, inbox, AI SDR (Jason), task, account, and help
workflows as **70 tools** for any MCP-compatible client — Claude, Cursor, Make, n8n, and custom agents.
Everything in these pages was read off the running server via an authenticated
`initialize` + `tools/list` handshake and safe validation probes. Tool names, signatures,
enum values, annotations, response envelopes, and error codes come from the live server —
not hand-maintained prose. If any other documentation disagrees, trust the server (see
[Regenerate this reference](/mcp/tools#regenerate-this-reference)).
## At a glance
| | |
| ---------------- | -------------------------------------------------------------------------------- |
| **Endpoint** | `https://mcp.reply.io/` |
| **Transport** | Streamable HTTP (responses are SSE `text/event-stream` frames carrying JSON-RPC) |
| **Wire format** | JSON-RPC 2.0 over HTTP |
| **MCP protocol** | `2025-06-18` |
| **Auth** | `Authorization: Bearer ` |
| **Rate limit** | Hourly window, \~3000/hour (`X-Rate-Limit-*` headers on every response) |
| **Sessions** | None required — `initialize` and `tools/list` work without an `Mcp-Session-Id` |
| **Tools** | 70 — 31 annotated `readOnlyHint`, 39 annotated `destructiveHint` |
| **Capabilities** | `tools`, `logging` (no prompts or resources advertised) |
Add Reply MCP to Claude, Cursor, or any MCP client in a couple of minutes.
The full catalog of all 70 tools, grouped by domain.
The rules every tool enforces: schemas, envelopes, error codes, retries.
Design principles, high-stakes gating, and a drop-in system prompt.
Task-based guides — launch outreach, process replies, build an AI SDR.
Not sure MCP is the right surface? Compare MCP, Skills, CLI, and API.
## What's exposed
The 70 tools cover the day-to-day Reply.io surface an agent needs:
| Domain | Tools | What it does |
| ------------------------- | :---: | -------------------------------------------------------------------------------------------------------- |
| **Sequences** | 16 | Discover, inspect, start/pause, enroll contacts, and configure mailboxes, schedules, and AI SDR settings |
| **Contacts** | 8 | Look up, filter, create, update, reassign, blacklist, and read activity |
| **Inbox & conversations** | 3 | List inbox threads, send replies, categorize |
| **Jason AI SDR** | 31 | Approvals & autopilot, knowledge bases, reply handlers, reengagement cards, offers, and playbooks |
| **Tasks** | 3 | List, create, and complete tasks |
| **Workspace resolvers** | 5 | Resolve mailboxes, LinkedIn accounts, schedules, lists, and teammates to their IDs |
| **Help & meta** | 4 | Search the Help Center, read articles, map the app, report unsupported requests |
See the [tool reference](/mcp/tools) for every tool name, its safety annotation, and a one-line purpose.
## MCP vs the REST API
MCP and the [REST API](/api-reference/introduction) are two surfaces over **one product** — same
account, same credit system.
* **MCP** — the interactive surface. 70 curated, annotated tools for agent actions and
natural-language workflows. Best for day-to-day operations.
* **REST** (`https://api.reply.io/v3`) — the exhaustive surface. Use it for bulk imports and
updates, background jobs, deep report exports, webhooks, and template/schedule/mailbox
management not exposed as tools.
Browse the full v3 REST API — the exhaustive surface for bulk and background operations.
## FAQ
Yes — it's an official, live server at `mcp.reply.io`. These pages were generated from an
authenticated `initialize` + `tools/list` handshake against the running server. If the
server ever returns a different tool set, the server is newer — trust it, and
[regenerate the reference](/mcp/tools#regenerate-this-reference) in seconds.
Pass your Reply.io API key as an `Authorization: Bearer` header. A call with
no credentials returns `401` with a `WWW-Authenticate` challenge; OAuth protected-resource
metadata is also published for clients that prefer a discovery-based flow. See
[Connect](/mcp/connect).
Every tool is annotated in `tools/list`: 31 are `readOnlyHint: true` and 39 are
`destructiveHint: true`. Agents can — and should — gate on these annotations before adding
their own confirmation UX for the high-stakes subset. See the
[tool contract](/mcp/tool-contract).
Responses are SSE frames; read the last `data:` frame. A successful `tools/call` returns the
payload as a JSON string in `result.content[0].text`. Tool failures are **not** HTTP errors —
they arrive as HTTP 200 with `result.isError = true`. See
[response envelopes](/mcp/tool-contract#response-envelopes).
Use MCP for interactive, natural-language workflows and everyday operations. Drop to the
REST API for bulk imports/updates, background jobs, report exports, and anything absent from
`tools/list`.
Yes. 31 of the 70 tools drive Jason — the approval queue, knowledge bases, reply handlers,
reengagement cards, offers, playbooks, and the Review/Autonomous reply mode.
# Workflow recipes
Source: https://docs.reply.io/mcp/recipes
Concrete Reply MCP call graphs — resolve IDs, configure, confirm high-stakes steps, verify.
Concrete call graphs an agent can follow. Each one resolves IDs first, configures where needed,
confirms before high-stakes steps, and verifies after. See [Building agents](/mcp/agent-guide) for
the principles behind them.
## Launch a sequence safely
`reply_search_sequences` by name → resolve the exact `sequenceId` (ask the user if ambiguous).
`reply_get_sequence_steps` → see which channels the sequence uses.
`reply_list_email_accounts`, then `reply_assign_email_account_to_sequence` if none is attached.
`reply_list_linkedin_accounts`, then `reply_assign_linkedin_account_to_sequence`; warn if the
account is unhealthy.
`reply_list_schedules`, then `reply_assign_schedule_to_sequence` if none is set.
`reply_attach_offer_to_sequence` / `reply_attach_playbook_to_sequence` /
`reply_attach_knowledge_base_to_sequence`, then `reply_set_sequence_reply_mode`.
Confirm with the user ("Start sequence X? This begins outreach to its contacts."), then
`reply_start_sequence` — expect `NoContacts` / `NoEmailAccounts` / `Archived` if configuration
was skipped.
`reply_get_sequence_stats` or `reply_search_sequences` to confirm it is Active.
## Add a contact to a sequence
`reply_search_contacts` by email or LinkedIn URL (the tool does not accept names — ask the user
for an email if that's all you have).
If missing and the user confirms details, `reply_create_contact` (needs a name plus at least one
of email/LinkedIn URL).
`reply_search_sequences` for the target sequence.
Confirm, then `reply_add_contact_to_sequence` (up to 100 IDs; per-contact skips arrive in
`NotProcessed`, e.g. `ContactAlreadyInSequence`, `ContactInBlackList`).
`reply_get_contact_activity` to verify enrollment.
## Supervise Jason in Review mode
`reply_set_sequence_reply_mode` → `Review` (if not already).
`reply_list_pending_approvals` — filter by sequence, owner, or channel.
Inspect the contact and context, compare against the attached playbook/knowledge base, then:
* `reply_approve_message` (optionally overriding body/subject) — **sends immediately**;
* `reply_regenerate_message` with specific feedback to revise the draft;
* `reply_reject_message` **only** if the contact should leave the sequence entirely — rejection
is irreversible and removes them from the sequence.
`reply_bulk_approve_messages` only after every draft in the batch was reviewed — the batch is
atomic: one stale reference rejects the whole batch and nothing is sent.
## Switch Jason to Autonomous
Verify the sequence has an offer, playbook, and knowledge base attached and reviewed.
Autonomous mode sends replies **without per-message approval**. Get explicit confirmation.
`reply_set_sequence_reply_mode` → `Autonomous`.
Keep watching `reply_get_inbox_emails` and `reply_get_sequence_stats`.
## Reply to an inbox thread
`reply_get_inbox_emails` (pagination only in v1 — no filters; previews only, not full bodies) →
resolve the exact `threadId` and note its channel.
Draft the reply and show it to the user for approval.
`reply_send_inbox_reply` with the matching channel (`Email` or `LinkedIn`) — v1 supports message
body only (max 32,000 chars); no Cc/Bcc, attachments, or scheduling. Expect `ChannelMismatch`,
`ContactOptedOut`, or `ThreadSendFailed` as the named failure modes.
`reply_change_inbox_category` to reflect the new state.
## Handle an unsupported request
Confirm no tool in `tools/list` can do it.
`reply_get_app_map` — the action may be a UI-only setting; return the exact in-app link.
`reply_search_knowledge_base` + `reply_get_knowledge_base_article` — answer a how-to and cite
the source.
Only if genuinely missing, `reply_report_unsupported_request` with a stable kebab-case
`requestKey` and a `category` from its enum — then tell the user plainly you cannot do it. Offer
the [REST API](/api-reference/introduction) if it covers the gap.
## Related guides
These recipes power the task-based guides, which add the cross-surface view (CLI, Skills, API):
* [Launch multichannel outreach](/guides/launch-outreach) · [Convert replies into meetings](/guides/convert-replies-to-meetings) ·
[Build a supervised AI SDR](/guides/build-an-ai-sdr) · [Improve sequences every cycle](/guides/improve-sequences)
# Tool contract & errors
Source: https://docs.reply.io/mcp/tool-contract
The rules every Reply MCP tool enforces — schemas, pagination, response envelopes, error codes, and retries.
These rules apply to **every one of the 70 tools**, verified against the served JSON schemas and
live validation probes. An agent that follows them will rarely hit an avoidable error.
## Contract rules the schemas enforce
* **Unknown arguments are invalid.** Every `inputSchema` sets `additionalProperties: false`. Don't
pass extra keys "just in case" — the call fails validation.
* **Required means non-empty.** Required string and array fields reject `null`, `""`, and `[]`
alike. In the server's own words: *"null, empty strings, and empty arrays are not accepted for
required fields."*
* **Every tool is annotated.** `readOnlyHint: true` (31 tools) or `destructiveHint: true` (39
tools) arrives in `tools/list`. Gate on these before adding your own confirmation UX.
* **Pagination is uniform.** List/search tools take `top` (default 20, max 100) and `skip`;
responses return `Data.Items` plus `HasMore`. Iterate with `skip += top` until `HasMore` is
`false`.
* **Patch semantics on updates.** Every `update_*` tool changes only the fields you pass;
omitted/null fields keep their current value. A passed list field replaces the whole list.
* **IDs come from resolvers.** Mutating tools require exact numeric IDs from prior
`search_*` / `list_*` / `filter_*` output. The descriptions repeat one rule verbatim: *"Never
invent, estimate, default, or ask the user."*
* **Approvals are addressed by pair.** Approve/reject/regenerate identify a Jason draft by
`sequenceId` + `contactId` — there is no separate draft/message ID.
* **Batches are bounded and explicit.** Bulk tools cap at 100 items. `reply_bulk_approve_messages`
is atomic (any stale reference rejects the whole batch; nothing is sent). Contact batches return
per-item results (`Affected` / `AffectedContactIds` / `NotProcessed`) — the authoritative record
of what actually happened; report exact counts ("reassigned 48 of 50").
## Enum quick reference
Every closed value set the server declares, in one place. All are case-insensitive unless noted.
| Field | Values | Used by |
| --------------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `channel` | `Email`, `LinkedIn` | `send_inbox_reply`, `list_pending_approvals` filter |
| Per-sequence contact status | `Active`, `Paused`, `Finished`, `Inactive`, `OutOfOffice` | `change_status_in_sequence` |
| Sequence search status | `New`, `Active`, `Paused` | `search_sequences` |
| Reply mode | `Review`, `Autonomous` (+ `sendExistingDrafts` boolean) | `set_sequence_reply_mode` |
| `toneOfVoice` | `Confident`, `Persuasive`, `Witty`, `Straightforward`, `Empathetic` | reply handlers, reengagement cards |
| `responseLength` | `SuperShort`, `Short`, `Medium`, `Long` | reply handlers, reengagement cards |
| `taskType` | `ToDo`, `Call`, `Meeting`, `LinkedIn`, `ManualEmail`, `Sms`, `WhatsApp` | `create_task`, `list_my_tasks` |
| `linkedInTaskType` | `Message`, `Connect`, `InMail`, `ViewProfile` | `create_task` (required when `taskType=LinkedIn`) |
| `callResolution` | `Positive`, `ToCall`, `Negative` | `complete_task` (recommended for Call tasks) |
| Playbook visibility | `Team` (default), `Organization` | `create_playbook`, `duplicate_playbook` |
| `category` | `integrations`, `reporting`, `sequences`, `contacts`, `email`, `linkedIn`, `ai`, `other` | `report_unsupported_request` |
| `sortMode` | `NewestFirst` (default), `OldestFirst` | `list_pending_approvals` |
## Response envelopes
**Transport.** Responses are SSE frames (`text/event-stream`) whose `data:` lines carry ordinary
JSON-RPC payloads. Read the last `data:` frame for the result.
**Success.** A successful `tools/call` returns `result.isError = false`, and the actual payload is
a JSON string inside `result.content[0].text`:
```json theme={null}
{ "Success": true, "Data": { "Items": [ ... ], "HasMore": false } }
```
**Error.** Tool failures are **not** HTTP errors — they come back as **HTTP 200**, JSON-RPC
success, with `result.isError = true` and the error as a JSON string in `result.content[0].text`:
```json theme={null}
{ "Success": false, "ErrorCode": "InvalidArguments", "ErrorMessage": "..." }
```
Parse the inner JSON; branch on `Success`, then on `ErrorCode`. Real captured examples:
```json theme={null}
{"Success":false,"ErrorCode":"InvalidArguments","ErrorMessage":"Tool 'reply_get_sequence_steps' arguments failed validation: SequenceId: 'Sequence Id' must be greater than '0'."}
{"Success":false,"ErrorCode":"InvalidArguments","ErrorMessage":"Tool 'reply_send_inbox_reply' is missing required argument(s): message. Supply a real value for each; null, empty strings, and empty arrays are not accepted for required fields."}
{"Success":false,"ErrorCode":"InvalidArguments","ErrorMessage":"Tool 'reply_search_contacts' arguments failed validation: : At least one of Email or LinkedIn must be provided. If the user gave only a name or company, ask them for an email address or LinkedIn URL instead."}
```
## Error model
Handle failures at three layers:
1. **Transport / auth.** `401` (missing/invalid key), `403` (valid key, missing scope or plan
feature), `429` (rate limit — back off using `X-Rate-Limit-Reset`), `5xx` (transient).
2. **Tool-level `ErrorCode`s** (inside the envelope): `InvalidArguments`, `NotFound`, `Forbidden`,
`Conflict`, `InvalidInput`, `InvalidParameter`, `UpstreamFailure`, `ServiceUnavailable`, plus
domain-specific codes named per tool (`NoEmailAccounts`, `NoContacts`, `ContactLimitExceeded`,
`ChannelMismatch`, `ContactOptedOut`, `ThreadSendFailed`, `InvalidStatusTransition`,
`ArticleNotFound`, `PERMISSION_DENIED`, …). Each tool's description states which codes it can
return and what to do about each.
3. **Per-item partial results.** Batch tools report per-contact skips in `NotProcessed` (e.g.
`ContactAlreadyInSequence`, `ContactInBlackList`, `NotFound`) while the call as a whole
succeeds. Read them; never claim an item succeeded unless it appears in the affected list.
## Safe-retry matrix
A timed-out **send** may already have succeeded server-side. Verify with a read before retrying
anything that reaches a prospect.
| When | Tools | Policy |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| Auto-retry once on transient `ServiceUnavailable` / `UpstreamFailure` / timeout | all read tools — `search_*`, `list_*`, `get_*`, `filter_*`, `*_stats`, `compare_*`, `get_app_map`, `search_knowledge_base` | Safe to retry |
| Retry only **after a verifying read** | `create_*`, `update_*`, `add_contact_to_sequence`, `change_status_in_sequence`, `assign_*`, `attach_*`, `complete_task`, `mark_contacts_as_replied` | Read first, then retry |
| **Never** auto-retry — confirm with the user first | `send_inbox_reply`, `approve_message`, `bulk_approve_messages`, `reject_message`, `start_sequence`, `blacklist_contact`, `change_contact_owner`, switching to Autonomous | Verify server state, then decide |
Next: [Building agents](/mcp/agent-guide) for how to sequence these calls, and the
[tool reference](/mcp/tools) for the full catalog.
# Tool reference
Source: https://docs.reply.io/mcp/tools
The full catalog of all 70 Reply MCP tools, grouped by domain, with safety annotations.
All 70 tools, grouped by domain. This is a **map**, not a schema dump — the authoritative,
per-argument JSON schema for every tool is served live by `tools/list`, and an agent should read it
from there. Each tool's served description also states its exact ID rules, enum values, batch
limits, and per-code failure guidance. See the [tool contract](/mcp/tool-contract) for the rules
shared across all of them.
**Legend:** 📖 read-only (`readOnlyHint`) · ✏️ mutation (`destructiveHint`) · ⭐ high-stakes
(prospect-visible or irreversible — always confirm; see [Building agents](/mcp/agent-guide#high-stakes-tools)).
## Sequences
### Discover & inspect
| Tool | | Purpose |
| ------------------------------------ | :-: | ----------------------------------------------------------------------------------- |
| `reply_search_sequences` | 📖 | Find sequences by name / status / archive flag; returns per-sequence summary fields |
| `reply_get_sequence_steps` | 📖 | List a sequence's ordered steps (type, delay, position, variant count) |
| `reply_get_sequence_step_variants` | 📖 | Read a message step's A/B content variants (subject, body) |
| `reply_get_sequence_stats` | 📖 | Email + LinkedIn performance metrics for one sequence |
| `reply_compare_sequence_performance` | 📖 | Side-by-side metrics for up to 25 sequences |
### Control & configuration
| Tool | | Purpose |
| ------------------------------------------- | :-: | -------------------------------------------------- |
| `reply_start_sequence` | ✏️⭐ | Start or resume a sequence so it begins sending |
| `reply_pause_sequence` | ✏️ | Pause a sequence; halt in-flight scheduled steps |
| `reply_add_contact_to_sequence` | ✏️⭐ | Enroll up to 100 existing contacts into a sequence |
| `reply_change_status_in_sequence` | ✏️ | Set one contact's per-sequence status |
| `reply_assign_email_account_to_sequence` | ✏️ | Link a mailbox to a sequence |
| `reply_assign_linkedin_account_to_sequence` | ✏️ | Link a LinkedIn account to a sequence |
| `reply_assign_schedule_to_sequence` | ✏️ | Set the sending schedule (business-hours window) |
| `reply_attach_offer_to_sequence` | ✏️ | Attach an offer to an AI SDR sequence |
| `reply_attach_playbook_to_sequence` | ✏️ | Attach a playbook to an AI SDR sequence |
| `reply_attach_knowledge_base_to_sequence` | ✏️ | Attach a knowledge base to an AI SDR sequence |
| `reply_set_sequence_reply_mode` | ✏️⭐ | Set Jason's Review / Autonomous reply mode |
## Contacts
| Tool | | Purpose |
| -------------------------------- | :-: | ----------------------------------------------------------------- |
| `reply_search_contacts` | 📖 | Look up contacts by exact email or LinkedIn URL |
| `reply_filter_contacts` | 📖 | List contacts by list/sequence membership and/or free-text search |
| `reply_create_contact` | ✏️ | Create a contact (needs a name + email or LinkedIn URL) |
| `reply_update_contact` | ✏️ | Patch fields on an existing contact |
| `reply_get_contact_activity` | 📖 | Activity history for one contact (sends, opens, replies, calls) |
| `reply_mark_contacts_as_replied` | ✏️ | Mark up to 100 contacts as replied (or clear the flag) |
| `reply_change_contact_owner` | ✏️⭐ | Reassign up to 100 contacts to another user |
| `reply_blacklist_contact` | ✏️⭐ | Blacklist a contact's email (or whole domain) from outreach |
## Inbox & conversations
| Tool | | Purpose |
| ----------------------------- | :-: | --------------------------------------------------------- |
| `reply_get_inbox_emails` | 📖 | List inbox threads (email + LinkedIn) with short previews |
| `reply_send_inbox_reply` | ✏️⭐ | Send a reply on an existing thread (body only in v1) |
| `reply_change_inbox_category` | ✏️ | Assign or clear a thread's workspace category |
## Jason AI SDR
### Approvals & autopilot
| Tool | | Purpose |
| ------------------------------ | :-: | --------------------------------------------------------------- |
| `reply_list_pending_approvals` | 📖 | The queue of Jason drafts awaiting approval |
| `reply_approve_message` | ✏️⭐ | Approve one draft and send it immediately |
| `reply_bulk_approve_messages` | ✏️⭐ | Approve and send up to 100 drafts (atomic batch) |
| `reply_reject_message` | ✏️⭐ | Reject a draft **and remove the contact from the sequence** |
| `reply_regenerate_message` | ✏️ | Ask Jason to rewrite a pending draft (optionally with feedback) |
### Knowledge bases
| Tool | | Purpose |
| ------------------------------------ | :-: | ------------------------------------------------------------- |
| `reply_list_knowledge_bases` | 📖 | List Jason knowledge bases |
| `reply_get_knowledge_base` | 📖 | Full detail of one knowledge base (instructions, URL sources) |
| `reply_create_knowledge_base` | ✏️ | Create a knowledge base |
| `reply_update_knowledge_base` | ✏️ | Rename or edit a knowledge base's instructions |
| `reply_add_knowledge_base_source` | ✏️ | Add a web-page URL source to a knowledge base |
| `reply_delete_knowledge_base_source` | ✏️ | Remove a URL source from a knowledge base |
### Reply handlers
| Tool | | Purpose |
| ---------------------------- | :-: | -------------------------------------------------- |
| `reply_list_reply_handlers` | 📖 | List reply handlers in a knowledge base |
| `reply_get_reply_handler` | 📖 | Full detail of one reply handler |
| `reply_create_reply_handler` | ✏️ | Add a reply handler (question type → instructions) |
| `reply_update_reply_handler` | ✏️ | Patch a reply handler |
| `reply_delete_reply_handler` | ✏️ | Remove a reply handler |
### Reengagement cards
| Tool | | Purpose |
| -------------------------------- | :-: | ------------------------------------------------------ |
| `reply_list_reengagement_cards` | 📖 | List reengagement (win-back) cards in a knowledge base |
| `reply_get_reengagement_card` | 📖 | Full detail of one reengagement card |
| `reply_create_reengagement_card` | ✏️ | Add a reengagement card (with send-after days) |
| `reply_update_reengagement_card` | ✏️ | Patch a reengagement card |
| `reply_delete_reengagement_card` | ✏️ | Remove a reengagement card |
### Offers
| Tool | | Purpose |
| ----------------------------------- | :-: | --------------------------------------------------------- |
| `reply_list_offers` | 📖 | List Jason offers (id + name) |
| `reply_get_offer` | 📖 | Full offer content (ICP, value props, proof points, CTAs) |
| `reply_create_offer` | ✏️ | Create an offer Jason can pitch |
| `reply_update_offer` | ✏️ | Patch an offer |
| `reply_generate_offer_from_website` | 📖 | Draft offer fields from a company website (does not save) |
### Playbooks
| Tool | | Purpose |
| -------------------------- | :-: | --------------------------------------------------- |
| `reply_list_playbooks` | 📖 | List Jason playbooks (id, name, visibility) |
| `reply_get_playbook` | 📖 | Full playbook, including the instruction body |
| `reply_create_playbook` | ✏️ | Create a playbook (Team or Organization visibility) |
| `reply_update_playbook` | ✏️ | Patch a playbook |
| `reply_duplicate_playbook` | ✏️ | Duplicate a playbook into a new one |
## Tasks
| Tool | | Purpose |
| --------------------- | :-: | ------------------------------------------------------------------- |
| `reply_list_my_tasks` | 📖 | List your tasks, filterable by type / status / due window / contact |
| `reply_create_task` | ✏️ | Create a standalone task (ToDo / Call / Meeting / LinkedIn / …) |
| `reply_complete_task` | ✏️ | Mark a task completed (with optional call resolution) |
## Workspace resolvers & accounts
| Tool | | Purpose |
| ------------------------------ | :-: | ------------------------------------------------------------ |
| `reply_list_email_accounts` | 📖 | List mailboxes with connection status, daily limits, tags |
| `reply_list_linkedin_accounts` | 📖 | List connected LinkedIn accounts with health and tier |
| `reply_list_schedules` | 📖 | List sending schedules (timezone, default flag, slot counts) |
| `reply_search_lists` | 📖 | Resolve a contact list name to its `ListId` |
| `reply_search_team_members` | 📖 | Resolve a teammate name/email to their `UserId` |
## Help & meta
| Tool | | Purpose |
| ---------------------------------- | :-: | ------------------------------------------------------------ |
| `reply_search_knowledge_base` | 📖 | Search the Reply.io Help Center for how-to articles |
| `reply_get_knowledge_base_article` | 📖 | Read a full Help Center article by slug |
| `reply_get_app_map` | 📖 | Catalog of app areas with in-app navigation steps and links |
| `reply_report_unsupported_request` | 📖 | Log a capability Reply.io doesn't have, for the product team |
## Regenerate this reference
This catalog is not hand-maintained trust — regenerate it in seconds by probing the running server:
```bash theme={null}
# 1) initialize
curl -s -X POST https://mcp.reply.io/ \
-H "Authorization: Bearer YOUR_REPLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-06-18","capabilities":{},
"clientInfo":{"name":"probe","version":"0.1"}}}'
# 2) list tools
curl -s -X POST https://mcp.reply.io/ \
-H "Authorization: Bearer YOUR_REPLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
```
Responses are SSE — take the JSON after the last `data:` line. If the returned tool set differs
from this page, the server is newer — **trust the server**.
# Skill catalog
Source: https://docs.reply.io/skills/catalog
All 18 Reply skills by pack — what each one does, when it triggers, its maturity, and the skills it builds on.
Every skill in [reply-skills](https://github.com/reply-team/reply-skills), grouped by the pack that
ships it. Install once and you get the whole pack — see [Install](/skills/overview#install).
Each skill's own frontmatter is the source of truth, and
[`INDEX.md`](https://github.com/reply-team/reply-skills/blob/main/INDEX.md) in the repo is generated
from it on every change. Treat the maturity levels below as a snapshot; `INDEX.md` is always current.
**Maturity levels:** `draft` — structurally complete, content plausible · `reviewed` — a maintainer or
domain expert signed off · `validated` — real executions confirmed the guidance · `production` —
safe to follow blindly.
## `ai-sdr-core` — 9 skills
Vendor-neutral SDR expertise: the business-operation contract, outbound strategy and playbooks, and
the guardrails that keep automated outreach safe. **The base pack** — every other pack builds on it,
and it carries no dependencies of its own.
### sdr-operations
*The vendor-neutral contract of atomic SDR business operations.*
Names each operation an SDR actually performs — add a contact, enrol it into a sequence, pause, reply,
classify a conversation, read engagement — in practitioner language with no provider detail. Every
other skill plans against these names, which is what lets a different provider adapter drop in without
rewriting the strategy layer. Ships an `references/operation-catalog.md` with the full operation list,
each one's effect, reversibility, and approval level.
Triggers when planning any outbound work, when writing a plan something else must execute, or before
reaching for a provider-specific execution skill.
**Category** operations · **Maturity** `draft` · **Recommends** `approval-boundaries`, `campaign-planning`
Changing an operation's effect, reversibility, or approval level here is a breaking change — every
guardrail downstream reads those.
### campaign-planning
*Turn a vague outbound goal into an executable plan.*
Clarifies the objective until success is observable, surfaces the constraints that shape it, composes
the work from known operations and playbooks, and decides the checkpoints. This is the entry point
when someone states a business outcome rather than an action.
Triggers on a business goal — book meetings, launch a campaign, grow pipeline — rather than a single
concrete request.
**Category** strategy · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `audience-building`, `campaign-launch`, `inbox-triage`, `performance-analysis`, `sending-guardrails`, `durable-work`
### audience-building
*Turn raw prospect data into a clean, deliberately-shaped audience.*
Decides who belongs, maps and sanity-checks the incoming fields, chooses a duplicate policy, and
organises people into named lists. Handles the messy-data questions explicitly rather than importing
whatever arrives.
Triggers when the user wants to import contacts, build or extend a list, define a segment, or asks
what to do about duplicates.
**Category** strategy · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `campaign-launch`, `sending-guardrails`, `approval-boundaries`
**Guide** [Import and clean contacts](/guides/import-contacts)
### campaign-launch
*Prepare and launch a sequence with every precondition checked and the send explicitly approved.*
Covers sending capability, step content, schedule, enrolment, and the go-live gate. Nothing starts
until the user has approved what will actually be sent.
Triggers when the user wants to create or start a sequence, launch a campaign, add contacts to a
sequence, or begin sending.
**Category** strategy · **Maturity** `draft` · **Depends on** `sdr-operations`, `approval-boundaries` · **Recommends** `audience-building`, `sending-guardrails`, `linkedin-guardrails`, `performance-analysis`
**Guide** [Launch multichannel outreach](/guides/launch-outreach)
### inbox-triage
*Work the inbound replies — surface what matters, draft together, send only what was approved.*
Orders conversations by what deserves attention first, gives compact thread context, drafts responses
with the user, sends only explicitly approved text, and records each conversation's outcome.
Triggers when the user asks about replies, inbox triage, responding to prospects, or finding the
interested leads.
**Category** strategy · **Maturity** `draft` · **Depends on** `sdr-operations`, `approval-boundaries` · **Recommends** `performance-analysis`, `campaign-launch`
**Guide** [Convert replies into meetings](/guides/convert-replies-to-meetings)
### performance-analysis
*An honest read on outreach performance, with a diagnosis that separates the causes.*
Account and per-campaign figures over a stated window, then a diagnosis that distinguishes
deliverability from copy from targeting — followed by one or two high-impact recommendations rather
than a list of everything observable.
Triggers when the user asks how campaigns are performing, what is underperforming, what to fix or
pause, or wants outreach analytics.
**Category** strategy · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `sending-guardrails`, `campaign-launch`, `inbox-triage`, `approval-boundaries`
**Guides** [Decide what to scale or fix](/guides/analyze-performance) · [Improve sequences every cycle](/guides/improve-sequences)
### approval-boundaries
*Where an agent must stop and ask — and the one case where acting first is correct.*
Defines what a valid confirmation looks like, when an earlier approval still applies, and how bulk
writes, sending to real people, unattended runs, retries, and anomaly stop-rules are gated. Every
other skill defers to this one rather than restating the rules, so they cannot drift.
Triggers before any operation touching a real prospect, when deciding whether an approval still
applies, or when configuring an agent to run unattended.
**Category** protection · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `sending-guardrails`, `linkedin-guardrails`
**See also** [Agent safety rules](/agents/safety)
### sending-guardrails
*Protect sender reputation and inbox placement.*
Domain authentication, warm-up, volume pacing, bounce interpretation, and recovery when deliverability
degrades.
Triggers before launching significant email volume, when bounce rates rise, when replies collapse, or
when a sending account or domain is new.
**Category** protection · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `campaign-launch`, `performance-analysis`, `approval-boundaries`
Ships as an honest skeleton with `TODO(expert)` markers — the structure and safety posture are
final; the specific thresholds await expert validation.
### linkedin-guardrails
*Keep social accounts safe under automation.*
Invitation pacing, messaging cadence, daily limits, and recovery when the platform flags activity.
Triggers before any LinkedIn outreach volume, when configuring account limits, or when an account
shows a warning.
**Category** protection · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `campaign-launch`, `approval-boundaries`
Also an honest skeleton with `TODO(expert)` markers.
## `reply-adapter` — 5 skills
Executes the core operation contract against Reply.io — the endpoints, auth, call ordering, and error
translation that turn a planned operation into a real one. **Requires `ai-sdr-core`** and a Reply.io
account.
### reply-cli
*Operate Reply.io from the terminal with the `reply` CLI.*
Sign in via OAuth or an API key, manage profiles and team context, and call any v3 endpoint through
`reply api`. This is the skill the others lean on when they need commands actually executed.
Triggers when executing any Reply.io operation from a shell, setting up authentication, or when
another Reply skill needs commands run.
**Category** execution · **Maturity** `reviewed` · **Recommends** `reply-api`, `reply-auth`, `reply-operations-mapping`
**Docs** [Reply CLI](/cli/overview) · [Using the CLI from agents](/cli/agents)
### reply-api
*Work with Reply.io API v3 correctly.*
Discover endpoints through the machine-readable docs, respect scopes and rate limits, and handle
`problem+json` errors and background jobs. Reading this before choosing an endpoint is what stops an
agent inventing paths.
Triggers before calling any non-trivial v3 endpoint, when choosing an endpoint for a task, or when a
call fails and needs interpretation.
**Category** execution · **Maturity** `reviewed` · **Depends on** `reply-cli` · **Recommends** `reply-auth`, `reply-operations-mapping`
**Docs** [API introduction](/api-reference/introduction) · [Rate limits](/api-reference/rate-limits) · [Machine-readable](/agents/machine-readable)
### reply-auth
*Reply.io credentials in depth.*
API key types (personal, Team, Organization), the `domain:verb` scopes model, acting-user headers, and
the team/organization resolution errors — `TEAM_REQUIRED`, `USER_REQUIRED` and friends.
Triggers when choosing or creating a key for an agent, resolving `401`/`403` errors, or working across
multiple teams.
**Category** execution · **Maturity** `reviewed` · **Recommends** `reply-cli`, `reply-mcp`, `approval-boundaries`
**Docs** [Authentication](/api-reference/authentication)
### reply-mcp
*Connect Reply.io to an MCP-compatible client, and know when to prefer it.*
Wiring `mcp.reply.io` into Claude, ChatGPT, Codex, or Cursor, choosing the right auth for the client,
and deciding when MCP tools beat CLI commands.
Triggers when setting up the Reply MCP connection or choosing between MCP and CLI execution.
**Category** execution · **Maturity** `reviewed` · **Recommends** `reply-cli`, `reply-auth`
**Docs** [Reply MCP](/mcp/overview) · [Connect](/mcp/connect)
### reply-operations-mapping
*The join table: each vendor-neutral operation → its Reply.io execution.*
For every operation named in `sdr-operations`, which v3 endpoint group and doc page serves it, which
scope it needs, what order composite work must run in, and how Reply's errors translate back into
operation outcomes.
Triggers when a plan names an operation and it has to actually run against Reply, or when a Reply
response needs interpreting in business terms.
**Category** execution · **Maturity** `draft` · **Depends on** `reply-api`, `reply-cli`, `sdr-operations` · **Recommends** `reply-auth`, `reply-mcp`
**Covers** contacts · contact lists · sequences · sequence steps · sequence contacts · inbox · reports · email accounts · LinkedIn accounts · schedules · background jobs
## `agentic-runtime` — 4 skills
Durable multi-session work in a plain-markdown workspace the user owns. **Requires `ai-sdr-core`.**
Optional — skip it if your orchestrator already provides durable work.
### durable-work
*How long-running work survives a session ending.*
Goals, plans, work items, checkpoints, approval pauses, recovery, and resumption — all kept as plain
markdown the user can read and edit. Ships `references/workspace-spec.md` plus `templates/plan.md` and
`templates/work-item.md` so the shapes are concrete rather than described.
Triggers when work spans more than one session, when resuming work something else started, or when a
plan needs persisting rather than holding in conversation.
**Category** runtime · **Maturity** `draft` · **Depends on** `sdr-operations` · **Recommends** `execution-reporting`, `user-memory`, `campaign-planning`, `approval-boundaries`
### execution-reporting
*Write execution reports that become organisational memory.*
Structure, evidence, honest deviations, and feeding results back into the next planning pass. Ships
`templates/report.md`.
Triggers after completing meaningful execution, at a plan checkpoint, or when closing a work item.
**Category** runtime · **Maturity** `draft` · **Depends on** `durable-work` · **Recommends** `user-memory`, `performance-analysis`
### orchestrator-integration
*How durable work is driven when nobody is watching.*
Which orchestrator owns scheduling and resumption, how to stop two of them fighting over the same
workspace, and what changes when a run is unattended.
Triggers when setting up background or recurring outbound work, or when more than one orchestration
runtime is available.
**Category** runtime · **Maturity** `draft` · **Depends on** `durable-work` · **Recommends** `approval-boundaries`, `execution-reporting`
**Guide** [Automate outbound end-to-end](/guides/automate-outbound)
### user-memory
*How agents store and reuse knowledge about the user.*
Preferences, ICP definitions, playbooks, and other durable context, kept in the workspace so a fresh
session does not have to ask again.
Triggers when learning something about the user worth keeping — tone, constraints, ICP — or before
drafting content that should reflect their preferences.
**Category** user-knowledge · **Maturity** `draft` · **Depends on** `durable-work` · **Recommends** `inbox-triage`, `campaign-planning`, `audience-building`
## Related
* [Install and manage the packs](/skills/overview)
* [Choose your interface](/agents/choose-your-interface) — Skills vs MCP vs CLI vs API
* [Agent safety rules](/agents/safety)
* [`INDEX.md`](https://github.com/reply-team/reply-skills/blob/main/INDEX.md) — the generated, always-current catalog
* [`docs/skill-contract.md`](https://github.com/reply-team/reply-skills/blob/main/docs/skill-contract.md) — the contract every skill follows
# Reply Skills
Source: https://docs.reply.io/skills/overview
Outbound expertise as markdown skills — three installable packs that teach your agent what to run, in what order, and with which guardrails.
[reply-skills](https://github.com/reply-team/reply-skills) packages Reply.io's outbound expertise as
plain-markdown **Agent Skills**: 18 skills across three independently installable packs. A skill is a
folder containing a `SKILL.md` — a description your agent's router matches on, plus the guidance it
loads when the description matches. No runtime, no code, nothing to host.
Skills use the open [Agent Skills](https://agentskills.io) format, so they load in Claude Code,
Codex, and any other `SKILL.md`-compatible host.
**MCP gives your agent tools. Skills give it judgement.** MCP and the CLI are *execution*
surfaces — individual operations your agent can call. Skills are *procedure*: which operations to
compose, in what order, what to verify, and where to stop and ask. See
[Choose your interface](/agents/choose-your-interface).
## The three packs
The packs split along a clean line: the SDR methodology, the Reply.io execution layer, and the
durable-work runtime each install on their own, so you take the pieces your setup calls for.
| Pack | Alias | Skills | What your agent gains | Requires |
| --------------------- | --------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
| **`ai-sdr-core`** | `core` | 9 | What an SDR operation *is*, outbound strategy and playbooks, and the guardrails — sending limits, channel limits, approval boundaries | — |
| **`reply-adapter`** | `adapter` | 5 | Executing those operations against Reply.io: the CLI, API v3, MCP, auth and scopes, error translation | `ai-sdr-core` + a Reply.io account |
| **`agentic-runtime`** | `runtime` | 4 | Durable multi-session work: goals, plans, work items, checkpoints, resumability, reports, user memory | `ai-sdr-core` |
```
┌───────────────┐
│ ai-sdr-core │ the base pack · SDR methodology
└───────┬───────┘
┌─────────┴─────────┐
┌─────────▼──────┐ ┌────────▼─────────┐
│ reply-adapter │ │ agentic-runtime │
│ Reply.io │ │ durable work │
└────────────────┘ └──────────────────┘
```
`ai-sdr-core` has no dependencies of its own, and `reply-adapter` and `agentic-runtime` never depend
on each other — either can be absent. Guardrails and approval boundaries ship **inside
`ai-sdr-core`**, so no selective install can leave safe operation out.
`reply-adapter` is the pack that connects to Reply.io — install it when you want your agent
operating the platform. The other two carry the methodology and the runtime, and depend only on
`ai-sdr-core`.
Every skill, with its pack, category, and maturity: [Skill catalog](/skills/catalog).
## Install
### `reply skills install` — recommended
The [Reply CLI](/cli/overview#skills) detects the assistants on your machine and installs all three
packs into each one, resolving dependencies itself:
```bash theme={null}
npm install -g reply-cli # needs Node.js 20 or newer
reply skills install
```
```
✓ detected Claude Code, Codex
✓ Claude Code · ai-sdr-core, reply-adapter, agentic-runtime installed
✓ Codex · ai-sdr-core, reply-adapter, agentic-runtime installed
Start a new session in each assistant so the skills load.
```
Install a subset — dependencies come along, so `adapter` pulls `core`:
```bash theme={null}
reply skills install core
reply skills install adapter runtime
reply skills install --agent codex # only this assistant
reply skills install --project # into this repository, not your home directory
reply skills install --dry-run # show the plan, change nothing
```
Full command surface and flags: [Reply CLI → Skills](/cli/overview#skills).
### Claude Code — plugin marketplace
```bash theme={null}
claude plugin marketplace add reply-team/reply-skills
claude plugin install reply-adapter@reply-skills
claude plugin install agentic-runtime@reply-skills
```
`ai-sdr-core` is resolved automatically as a dependency of either pack — Claude Code reports
`(+ 1 dependency: ai-sdr-core)`. Install it alone if you want the vendor-neutral expertise only:
```bash theme={null}
claude plugin install ai-sdr-core@reply-skills
```
Skills arrive namespaced per pack — `ai-sdr-core:campaign-launch`, `reply-adapter:reply-cli` — so they
never collide with skills you wrote yourself.
### Codex — plugin marketplace
```bash theme={null}
codex plugin marketplace add reply-team/reply-skills
# ai-sdr-core FIRST — Codex resolves no dependencies
codex plugin add ai-sdr-core@reply-skills
codex plugin add reply-adapter@reply-skills
codex plugin add agentic-runtime@reply-skills
```
**Install order matters on Codex.** The Codex plugin format has no dependency field, so nothing
installs `ai-sdr-core` for you. Adding `reply-adapter` on its own gives you five skills whose
guidance is missing. Install the core first, or install all three.
Skills are namespaced per pack here too. Re-running `codex plugin add` upgrades in place rather than
duplicating the entry.
### Any host — the skills ecosystem CLI
[`npx skills`](https://github.com/vercel-labs/skills) is the ecosystem package manager for Agent
Skills, with a directory entry at
[skills.sh/reply-team/reply-skills](https://www.skills.sh/reply-team/reply-skills). Use it to reach
hosts Reply does not package for individually.
```bash theme={null}
npx skills add reply-team/reply-skills # asks where to install
npx skills add reply-team/reply-skills --skill '*' --agent claude-code -y # no prompts
npx skills add reply-team/reply-skills --list # look, install nothing
```
**This channel has no pack model.** It installs individual skills by name — no packs, no
namespacing, no dependency resolution. **Install all 18.** A `--skill` subset is your own
responsibility: the CLI will install `reply-operations-mapping` or `durable-work` without
`sdr-operations` and say nothing about it, leaving a skill whose declared dependency is absent.
Provenance is recorded in `skills-lock.json`, so `npx skills update` works later.
### Cursor, Windsurf, Gemini CLI, GitHub Copilot — directory copy
These hosts have no plugin mechanism, so a pack is installed by copying its skills directory. That
works because a pack is just a directory of skills.
```bash theme={null}
git clone https://github.com/reply-team/reply-skills /tmp/reply-skills
# ai-sdr-core is required by the others — copy it first
cp -r /tmp/reply-skills/plugins/ai-sdr-core/skills/* /
cp -r /tmp/reply-skills/plugins/reply-adapter/skills/* /
cp -r /tmp/reply-skills/plugins/agentic-runtime/skills/* /
```
`reply skills install` does this copying for you, to the same paths. Never copy `reply-adapter` or
`agentic-runtime` without `ai-sdr-core`, or their skills will reference guidance that is not there.
## Install channels compared
Each row states the version it was verified against. Rows marked *not verified* have instructions
that are a reasonable starting point, not a promise.
| Channel | Installs | Dependency handling | Layout on disk | Verified against |
| ------------------------------------------------------------------------- | ------------------ | -------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------- |
| **`reply skills install`** | Packs | **Resolved by the installer** on every host | Whatever the channel it drives produces | reply CLI 0.4.0 — Claude Code and Codex; other hosts receive files, unconfirmed |
| Claude Code plugin marketplace | Packs | **Resolved by the host** — `reply-adapter` pulls `ai-sdr-core` | Namespaced per pack | Claude Code 2.1.220 |
| Codex plugin marketplace | Packs | **Manual** — install `ai-sdr-core` first | Namespaced per pack | codex-cli 0.146.0-alpha.3.1 |
| `npx skills` / [skills.sh](https://www.skills.sh/reply-team/reply-skills) | Individual skills | **None** — install all 18 yourself | Flat, no namespacing | skills CLI 1.5.21 |
| Cursor · Windsurf · Gemini CLI · GitHub Copilot | Skills, by copying | **Manual** — copy `ai-sdr-core` first | Flat, per host path | Not verified |
Only the first two channels resolve the `ai-sdr-core` dependency for you.
## Where the files land
`reply skills install` writes to your user directory by default, or to the current repository with
`--project`.
| Assistant | How it receives the packs | Project-scoped path | Paths verified |
| -------------- | ----------------------------------------------------- | ------------------- | -------------- |
| Claude Code | its own plugin CLI | `.claude/skills/` | Yes |
| Codex | its own plugin CLI (`--project` copies files instead) | `.agents/skills/` | Yes |
| Cursor | copied files | `.agents/skills/` | Not yet |
| Gemini CLI | copied files | `.agents/skills/` | Not yet |
| GitHub Copilot | copied files | `.agents/skills/` | Not yet |
| Windsurf | copied files | `.windsurf/skills/` | Not yet |
On Claude Code and Codex the packs go through the assistant's own plugin mechanism, so they keep
updating through it. Other `SKILL.md` hosts receive the skills as files.
**"Not yet" means the skills directory came from that assistant's documentation** and has not been
confirmed by a verification run. The install works; what is unconfirmed is whether the assistant
reads from where the files were put. Those hosts are marked `(paths not yet verified)` in the report
and carry `"verified": false` in `--json` output.
## Verify the install
```bash theme={null}
reply skills list # what's installed where, and what has an update available
reply skills list --json # machine-readable, including per-host `verified`
```
On Claude Code you can also ask the host directly:
```bash theme={null}
claude plugin list
claude plugin details ai-sdr-core # its skills and their token cost
```
**Start a new session** in each assistant — newly installed skills are picked up at session start,
not mid-conversation.
## Connect an execution surface
**Installing skills is a separate step from connecting Reply.io.** `reply-adapter` is the pack that
calls the platform, so it needs at least one Reply.io execution surface configured:
| Surface | When to use it | Start |
| ------------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------ |
| [Reply CLI](/cli/overview) | Your agent has a shell. `reply api` reaches every v3 endpoint. | `reply auth login` |
| [Reply MCP](/mcp/overview) | Your client speaks MCP and has no shell — desktop apps, hosted assistants. | [Connect](/mcp/connect) |
| [REST API v3](/api-reference/introduction) | You are driving the API directly. | [Get a key](/api-reference/authentication) |
If the CLI is installed, this confirms the adapter has something to drive:
```bash theme={null}
reply auth whoami
```
Agents driving the API directly should start at
[llms.txt](https://docs.reply.io/llms.txt) — see [Machine-readable](/agents/machine-readable).
## Run your first skill
Once installed, ask your agent in plain language — the host routes to a skill by matching its
description:
* *"Import this CSV as a new list and tell me about the duplicates"* → `audience-building`
* *"Plan an outbound campaign to book ten meetings with fintech founders"* → `campaign-planning`
* *"What sending limits should I respect on a brand-new domain?"* → `sending-guardrails`
* *"Show me the interested replies from this week"* → `inbox-triage`
* *"Which campaigns are underperforming, and what should I fix?"* → `performance-analysis`
* *"Turn this goal into a plan I can resume tomorrow"* → `durable-work`
## Safety model
Skills never let an agent start a sequence, send a message, or delete data without your explicit
confirmation in the conversation. Anything that sends content requires you to approve the **literal
text** first. Bulk operations show their plan and their counts before running.
Protective actions are the one exception: pausing a campaign that is burning your domain happens
first and tells you immediately, because waiting is the destructive choice.
The rules live in one skill — `approval-boundaries` — and ship inside `ai-sdr-core`, so no selective
install can omit them. Bound them mechanically as well, with
[scoped API keys](/api-reference/authentication): grant an agent only the scopes its work needs. See
[Agent safety rules](/agents/safety).
## Manage installed packs
```bash theme={null}
reply skills list # what's installed where
reply skills update # bring installed packs to the latest version
reply skills remove runtime # remove one pack
reply skills remove # remove all of them
```
Add `--dry-run` to any of them to see the plan without changing anything. Removing a pack that
another installed pack depends on is refused — remove both, or run `reply skills remove` with no pack.
Through Claude Code's own plugin mechanism:
```bash theme={null}
claude plugin update reply-adapter # restart to apply
claude plugin uninstall reply-adapter
claude plugin prune # drop dependencies nothing needs any more
```
## Troubleshooting
Skills load at session start. Start a **new** session in the assistant, then ask something that
matches a skill description — hosts route on the `description` field, so a vague prompt may match
nothing. Confirm the install first with `reply skills list`.
A pack was installed without `ai-sdr-core`. Only `reply skills install` and the Claude Code
marketplace resolve that dependency; on Codex, `npx skills`, and directory copies it is your job.
Install the core and start a new session.
The `codex` binary is not on `PATH` — it ships with the desktop app at
`%LOCALAPPDATA%\OpenAI\Codex\bin\\codex.exe`. Call it by full path. Tooling that detects
Codex with `which codex` will wrongly report it as absent.
`reply-adapter` needs a working Reply.io credential. Run `reply auth whoami`; if it fails, run
`reply auth login`. For `401`/`403` on a specific call, check the key's scopes against
[Authentication](/api-reference/authentication).
Expected for Cursor, Windsurf, Gemini CLI, and GitHub Copilot. The files were written to the path
that host documents, but Reply has not confirmed the host reads them. Check the host's own skills
directory and consult its documentation.
## Maturity and honest gaps
Each skill carries a maturity level — `draft`, `reviewed`, `validated`, or `production`. Some business
and protection content ships as **honest skeletons** marked `TODO(expert)`: the structure and safety
posture are final, the specific numbers await validation by domain experts. A visible gap is
deliberate; invented expertise would be worse. Per-skill maturity is in the
[Skill catalog](/skills/catalog).
## Links
* Repo: [github.com/reply-team/reply-skills](https://github.com/reply-team/reply-skills) (MIT)
* Live catalog, always current: [`INDEX.md`](https://github.com/reply-team/reply-skills/blob/main/INDEX.md)
* The contract every skill follows: [`docs/skill-contract.md`](https://github.com/reply-team/reply-skills/blob/main/docs/skill-contract.md)
* Pack architecture and invariants: [`docs/packs.md`](https://github.com/reply-team/reply-skills/blob/main/docs/packs.md)
* Execution layer: [Reply CLI](/cli/overview) · [Reply MCP](/mcp/overview) · [API v3](/api-reference/introduction)
* Next: [Skill catalog](/skills/catalog)
# Webhook event payloads
Source: https://docs.reply.io/webhook-event-payloads
Example payloads for every webhook event.
When a webhook subscription fires, Reply sends a JSON payload to your configured URL.
This page documents the payload for each supported event type. The examples match the structure of real webhook deliveries.
## Event envelope
Every webhook delivery includes the same top-level event envelope:
```json theme={null}
{
"event": {
"date": "2026-04-21T09:30:00.000Z",
"id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1",
"type": "email_replied",
"user_id": 1234,
"team_id": 100,
"subscription_id": 42
},
// event-specific fields follow, flat at the top level
}
```
| Field | Type | Notes |
| ----------------------- | ------------ | ----------------------------------------------------------------------- |
| `event.date` | ISO-8601 UTC | Time the event occurred in Reply. |
| `event.id` | UUID | Unique event identifier. Use it for idempotency when processing events. |
| `event.type` | string | Event type. Matches the subscription's `eventType`. |
| `event.user_id` | int | ID of the Reply user associated with the subscription. |
| `event.team_id` | int | ID of the team associated with the event. |
| `event.subscription_id` | int | ID of the subscription that triggered the event. |
## Shared conventions
* Property names use `snake_case`, for example `event_type`, `sequence_fields`, and `reply_message_url`.
* Enum values are PascalCase strings, for example `"Personal"`, `"EmailDetected"`, and `"Hard"`.
* Timestamps use ISO-8601 UTC format with millisecond precision.
* Events associated with a contact include a `contact_fields` object. Use `contact_fields.id` as the stable contact identifier.
* If the subscription was created with `includeProspectCustomFields: true`, a `contact_custom_fields` object is also included.
* Events associated with a sequence include a `sequence_fields` object.
* If `SequenceId` is `0`, the event is not associated with a sequence and `sequence_fields` may be empty or omitted.
* Email-related events respect the subscription's `includeEmailUrl` and `includeEmailText` settings. Disabled fields are omitted from the payload.
## Reusable blocks
The following objects appear in multiple event payloads.
### `contact_fields`
```json theme={null}
{
"id": 12345,
"email": "prospect@example.com",
"first_name": "Test",
"last_name": "Prospect",
"full_name": "Test Prospect",
"title": "Test Title",
"company": "Test Company",
"linkedin_profile_url": "https://linkedin.com/in/test",
"city": "Test City",
"state": "Test State",
"country": "Test Country",
"time_zone_id": "America/Los_Angeles",
"phone": "+1234567890",
"domain": "example.com",
"company_size": "51-200 employees",
"industry": "Software",
"linkedin_sales_navigator": "https://www.linkedin.com/sales/people/test",
"linkedin_recruiter": "https://www.linkedin.com/recruiter/people/test"
}
```
### `contact_custom_fields`
Only included when the subscription was created with `payloadConfig.includeProspectCustomFields: true`.
Keys match the custom field names configured on the contact.
```json theme={null}
{
"Custom Field 1": "Custom Value 1"
}
```
### `sequence_fields`
```json theme={null}
{
"id": 200,
"name": "Test Sequence",
"step_number": 1
}
```
***
## Event reference
### `email_replied`
**When it fires:** Once per recorded email reply — when Reply detects an incoming reply to a sequence email, or a direct reply that is not tied to a sequence.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_replied", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"sent_email_id": 999,
"reply_date": "2026-04-21T09:30:00.000Z",
"reply_message_id": "test-reply-msg-id",
"email_from": "account@example.com",
"reply_message_url": "https://example.com/test-email-blob",
"email_text": "…",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `email_account_id`
* The Reply email account that received the reply.
* `sent_email_id`
* The sequence email that was replied to. `null` for direct replies that are not tied to a sequence email.
* `email_from`
* Present when the replying email account is known.
* `reply_message_url`
* Included when `includeEmailUrl: true`.
* `email_text`
* Included when `includeEmailText: true`.
* `sequence_fields`
* Omitted for direct replies that have no sequence context.
* `contact_custom_fields`
* Included when `includeProspectCustomFields: true`.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `reply_categorized`
**When it fires:** An email reply is categorized automatically by Reply or manually by a user.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "reply_categorized", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sent_email_id": 999,
"reply_date": "2026-04-21T09:30:00.000Z",
"reply_message_id": "test-reply-msg-id",
"reply_inbox_category_name": "Interested",
"reply_message_url": "https://example.com/test-email-blob",
"email_text": "…",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `reply_inbox_category_name`
* Human-readable category name.
* Built-in values include:
* `"No categories"`
* `"Interested"`
* `"Not interested"`
* `"Do not contact"`
* `"Not now"`
* `"Forwarded"`
* `"Meeting intent"`
* Teams can create custom categories. Treat this field as a free-form string.
* `reply_message_id`
* May be `null` for older replies where the original message ID was not captured.
* `reply_date`
* May be `null` for older replies where the original timestamp is unavailable.
* `reply_message_url`
* Included when `includeEmailUrl: true` and a stored reply is available.
* `email_text`
* Included when `includeEmailText: true` and the reply body can be extracted.
* `contact_custom_fields`
* Included when `includeProspectCustomFields: true`.
* `sequence_fields` includes `id`, `name`, and `step_number`. Omitted if the reply is not associated with a sequence.
### `email_sent`
**When:** Reply sends an email from a sequence step.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_sent", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"sent_email_id": 999,
"sent_email_message_id": "test-message-id",
"sent_email_date": "2026-04-21T09:30:00.000Z",
"sent_message_url": "https://example.com/test-email-blob",
"email_text": "…",
"email_from": "account@example.com",
"sent_email_variant": "A",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `sent_email_variant` identifies the A/B variant that was sent. Omitted when the step has only one variant.
* `sent_message_url` is included when `includeEmailUrl` is enabled and the message blob is available.
* `email_text` is included when `includeEmailText` is enabled and the message body can be parsed.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `email_opened`
**When:** A recipient opens a tracked email.
Opens from IP addresses in the user's exclusion list are ignored.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_opened", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"sent_email_id": 999,
"sent_email_message_id": "test-message-id",
"email_open_date": "2026-04-21T09:30:00.000Z",
"opens_count": 1,
"email_from": "account@example.com",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `opens_count` is the total number of opens for the email. It increases each time the email is opened.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `email_link_clicked`
**When:** A recipient clicks a tracked link in an email.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_link_clicked", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_link_url": "https://example.com/test-link",
"email_from": "account@example.com",
"contact_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### `email_bounced`
**When:** A sent email bounces or is rejected by the recipient's mail server.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_bounced", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"sent_email_id": 999,
"bounce_date": "2026-04-21T09:30:00.000Z",
"bounce_type": "Hard",
"bounce_message_id": "test-bounce-id",
"sent_message_url": "https://example.com/test-email-blob",
"email_text": "…",
"email_from": "account@example.com",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `bounce_type` can be:
* `Unknown`
* `Soft`
* `Hard`
* `GmailApiLimitWarning`
* `Office365LimitWarning`
* `MailboxFull`
* `AuthenticationFailure`
* `SpamRejection`
* `PolicyViolation`
* `sent_message_url` is included when `includeEmailUrl` is enabled and the message blob is available.
* `email_text` is included when `includeEmailText` is enabled and the message body can be parsed.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `email_auto_reply`
**When:** Reply identifies an incoming message as an auto-reply or out-of-office message.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_auto_reply", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"sent_email_id": 999,
"reply_date": "2026-04-21T09:30:00.000Z",
"reply_message_id": "test-reply-id",
"reply_subject": "Out of Office: Test Subject",
"reply_type": "AutoReply",
"reply_message_url": "https://example.com/test-email-blob",
"email_text": "…",
"email_from": "account@example.com",
"user_name": "Test User",
"user_email": "user@example.com",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `reply_type` is:
* `AutoReply` for automatic responder messages
* `OutOfOffice` for out-of-office messages
* `email_account_id` may be `null`.
* `email_from` is included only when the email account is known.
* `reply_message_url` is included when `includeEmailUrl` is enabled.
* `email_text` is included when `includeEmailText` is enabled and the message body can be parsed.
* `user_name` and `user_email` identify the Reply user who owns the sequence when the event is generated.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `email_account_connection_lost`
**When:** A connected mailbox can no longer authenticate, such as after an OAuth revocation or password change.
This is an account-level event and does not include contact or sequence information.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_account_connection_lost", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"email_account_address": "account@example.com"
}
```
### `email_account_error`
**When:** A connected mailbox reports an IMAP or SMTP error, such as an authentication failure, send failure, health check failure, reputation issue, or sending limit.
The event is generated each time the underlying error changes to a non-empty value. Clearing the error does not generate an event.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "email_account_error", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"email_account_id": 1,
"email_account_error": "Sending: SMTP authentication failed",
"email_account_address": "account@example.com"
}
```
### Field notes
* `email_account_error` is a free-form error message.
* Error messages always begin with either:
* `Receiving:` for IMAP-related errors
* `Sending:` for SMTP-related errors
* `email_account_address` may be omitted if the email account can no longer be loaded.
### `contact_finished`
**When:** A contact's participation in a sequence ends.
This can happen when the contact completes the sequence, reaches a terminal status, or is manually finished.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "contact_finished", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"finish_reason": "Replied",
"finished_date": "2026-04-21T09:30:00.000Z",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence" }
}
```
### Field notes
`finish_reason` can be one of:
| Value | Description |
| :------------------- | :--------------------------------------------------- |
| `Replied` | The contact replied to a sequence email. |
| `Bounced` | An email bounced and the contact was removed. |
| `Manual` | A user manually finished the contact. |
| `FinishedAfterXdays` | The contact reached the sequence's maximum duration. |
| `None` | No specific reason was recorded. |
| `Called` | The contact was finished after a phone call. |
| `OptOut` | The contact opted out. |
| `MeetingBooked` | A meeting was booked with the contact. |
| `ForbiddenStep` | The next step was blocked by content guardrails. |
Additional fields:
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id` and `name`. The whole sequence has ended, so this event is not tied to a single step and `step_number` is not included.
### `contact_opted_out`
**When:** A contact opts out of future outreach.
This event is generated when a contact:
* Clicks an unsubscribe link
* Is manually opted out by a user
* Is marked as **Do Not Contact**
All three scenarios generate the same event and payload. The event does not indicate which action caused the opt-out.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "contact_opted_out", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"opt_out_date": "2026-04-21T09:30:00.000Z",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field otes
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `contact_called`
**When:** A call with a contact reaches a final state, such as answered, missed, busy, timed out, or logged manually.
The event is generated after call details have been collected and are ready to deliver.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "contact_called", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"from_number": "+1234567890",
"duration": 60,
"disposition": "Answered",
"resolution": "Positive",
"notes": "Spoke with prospect, scheduled a follow-up.",
"recording_url": "https://example.com/test-call-recording",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `from_number` is the phone number used to place the call.
* `duration` is the call length in seconds. It is `0` for manually logged calls and calls that never connected.
* `disposition` can be:
* `Answered`
* `LoggedManually`
* `NoAnswer`
* `Busy`
* `Fail`
* `resolution` can be one of Reply's built-in values (`Positive`, `ToCall`, `Negative`) or a custom resolution defined by the team. Treat this field as a free-form string.
* `notes` contains any notes entered by the user. It is `null` when no notes were saved.
* `recording_url` contains a recording URL when a recording is available. It is `null` when the call was not recorded.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`. `step_number` is `0` for calls not tied to a sequence step.
### `linkedin_connection_request_sent`
**When:** Reply sends a LinkedIn connection request as part of a sequence step.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_connection_request_sent", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sender_linkedin_account_id": 50,
"initial_message": "Test connection request",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence" }
}
```
### Field notes
* `sender_linkedin_account_id` is the Reply LinkedIn account ID. It is not a LinkedIn public identifier.
* `initial_message` contains the connection request note that was sent. It may be `null` if no note was included.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id` and `name`. `step_number` is not included for this event.
### `linkedin_connection_request_accepted`
**When:** A LinkedIn connection request previously sent through Reply is accepted.
Only connection requests sent through Reply generate this event.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_connection_request_accepted", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sender_linkedin_account_id": 50,
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `linkedin_message_sent`
**When:** Reply sends a LinkedIn message or InMail.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_message_sent", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sender_linkedin_account_id": 50,
"initial_message": "Test message",
"is_inmail": false,
"is_voice_attached": false,
"linkedin_message_id": "test-msg-id",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `linkedin_message_id` is a Reply-generated message identifier. It is not a LinkedIn message ID.
* `is_inmail` is `true` for InMail messages and `false` for regular LinkedIn messages.
* `is_voice_attached` is `true` when a voice attachment was included.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number`.
### `linkedin_message_replied`
**When:** A contact replies to a LinkedIn conversation associated with a sequence contact.
Only replies linked to known contacts in Reply generate this event.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_message_replied", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sender_linkedin_account_id": 50,
"linkedin_message_id": "01234567-89ab-cdef-0123-456789abcdef",
"linkedin_message": "Test reply",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `linkedin_message_id` is a Reply-generated identifier. It is not a LinkedIn message ID.
* `linkedin_message` contains the reply text.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `sequence_fields.name` and `sequence_fields.step_number` are always included.
* `sequence_fields.id` is included only when the contact is currently associated with a sequence.
### `linkedin_reply_categorized`
**When:** Reply assigns a category to a LinkedIn conversation.
A separate event is generated for each affected conversation.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_reply_categorized", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"linkedin_thread_id": 1,
"reply_inbox_category_name": "Interested",
"last_reply_date": "2026-04-21T09:30:00.000Z",
"linkedin_message": "Test reply",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `reply_inbox_category_name` can be one of:
* `No categories`
* `Interested`
* `Not interested`
* `Do not contact`
* `Not now`
* `Forwarded`
* `Meeting intent`
* LinkedIn categories do not support custom category names.
* `last_reply_date` is included when available.
* `contact_custom_fields` is included when `includeProspectCustomFields` is enabled.
* `linkedin_message` contains the raw reply text from the contact. It is included only when `includeLinkedInMessageText` is enabled.
* `sequence_fields` includes `id`, `name`, and `step_number` when the conversation is associated with a sequence; it is empty when it is not.
### `autopilot_stopped`
**When:** An evergreen sequence's autopilot is disabled.
This event is generated only for the reasons listed in `autopilot_error_message`.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "autopilot_stopped", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"autopilot_error_message": "the active contacts limit has been reached",
"user_name": "Test User",
"user_email": "user@example.com",
"sequence_owner_name": "Test Owner",
"sequence_owner_email": "owner@example.com",
"sequence_fields": { "id": 200, "name": "Test Sequence" }
}
```
### Field notes
`autopilot_error_message` is one of:
* `the sequence has been deactivated`
* `the active contacts limit has been reached`
* `there are no more contacts/companies matching the filter criteria`
Additional fields:
* `user_name` and `user_email` identify the user who triggered the deactivation.
* `sequence_owner_name` and `sequence_owner_email` identify the sequence owner.
* Owner fields may be omitted if the corresponding user cannot be resolved.
**Team accounts**
If the user who triggered the deactivation is not the sequence owner, Reply generates two events:
* One for the acting user
* One for the sequence owner
Both events contain the same sequence information and error message.
### `linkedin_account_alerts`
**When:** Reply detects a LinkedIn account issue that requires attention.
Examples include a disconnected Sales Navigator account, exhausted InMail credits, or a connection request limit.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_account_alerts", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"linkedin_id": 50,
"linkedin_name": "Test LinkedIn Account",
"linkedin_account_alert": "Sales Navigator is disconnected. Please reconnect your LinkedIn account to continue sending InMail steps.",
"team_name": "Test Workspace",
"linkedin_owner_name": "Test User",
"linkedin_owner_email": "owner@example.com"
}
```
### Field notes
* `linkedin_id` and `linkedin_name` identify the LinkedIn account associated with the alert.
* `linkedin_account_alert` contains a human-readable description of the issue.
* `team_name` may be omitted if the workspace has no name.
* `linkedin_owner_name` and `linkedin_owner_email` may be omitted if the account owner cannot be resolved.
### Alert types
| Alert | Description | Re-fire interval |
| :------------------------------------ | :---------------------------------------- | :------------------------ |
| `SalesNavigatorDisconnected` | Sales Navigator is disconnected. | Up to once every 12 hours |
| `InMailCreditsExhausted` | No InMail credits remain. | Up to once every 24 hours |
| `ConnectionRequestWeeklyLimitReached` | Weekly LinkedIn connection limit reached. | Up to once every 24 hours |
### `contact_replied`
**When it fires:** Each time a sequence contact is recorded as **replied** — Reply detects a reply to a sequence email (`reason: "EmailDetected"`), a sequence LinkedIn message (`reason: "LinkedInDetected"`), or a sequence SMS (`reason: "SmsDetected"`), or a user or the API marks the contact as replied (`reason: "StatusSetManually"`).
This is a channel-agnostic **status** signal: subscribe to it once to learn whenever a contact enters the replied state, regardless of channel, instead of combining `email_replied`, `linkedin_message_replied`, and manual actions yourself. It carries who replied, which sequence/step, when, and why — not the message content. For channel-specific detail (message body, mailbox, sender account) subscribe to the matching per-channel webhook and correlate by `reply_message_id`.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "contact_replied", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"reply_date": "2026-04-21T09:30:00.000Z",
"reply_message_id": "test-reply-msg-id",
"reason": "EmailDetected",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `reason`
* `"EmailDetected"` when Reply detects a reply to a sequence email.
* `"LinkedInDetected"` when Reply detects a reply to a sequence LinkedIn message.
* `"SmsDetected"` when Reply detects a reply to a sequence SMS.
* `"StatusSetManually"` when a user or the API marks the contact as replied.
* `reply_message_id`
* Correlation key to the matching per-channel webhook (`email_replied` / `linkedin_message_replied`): email reply message id, LinkedIn message id, or SMS provider message id. `null` for manually marked replies, which carry no stored message.
* `contact_fields`
* The contact who replied. See the shared [`contact_fields`](#contact_fields) block above for the full field list.
* `sequence_fields`
* Always present; `contact_replied` fires only for contacts in a sequence.
* `contact_custom_fields`
* Included when `includeProspectCustomFields: true`.
### `linkedin_comment_posted`
**When it fires:** Reply posts an AI-generated comment on a contact's most recent LinkedIn post as part of a "Comment on a recent post" sequence step.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_comment_posted", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sender_linkedin_account_id": 50,
"post_url": "https://www.linkedin.com/feed/update/urn:li:activity:1234567890",
"comment_text": "Great point about automation!",
"detected_topic": "ai tools",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `sender_linkedin_account_id`
* ID of the LinkedIn account that posted the comment.
* `post_url`
* URL of the LinkedIn post the comment was posted on.
* `comment_text`
* The text of the posted comment.
* `detected_topic`
* The post's main topic as detected by the AI — a short lowercase phrase, for example `"product launch"`.
* `sequence_fields`
* Always present; the comment step only runs for contacts in a sequence.
* `contact_custom_fields`
* Included when `includeProspectCustomFields: true`.
### `linkedin_comment_skipped`
**When it fires:** A "Comment on a recent post" sequence step skips a contact instead of posting a comment. The step finishes and the contact continues through the sequence; `skip_reason` explains why no comment was posted.
```json theme={null}
{
"event": { "date": "2026-04-21T09:30:00.000Z", "id": "4c1b9e74-5ac3-4b0f-9a60-2a37a9b4a2d1", "type": "linkedin_comment_skipped", "user_id": 1234, "team_id": 100, "subscription_id": 42 },
"sender_linkedin_account_id": 50,
"post_url": "https://www.linkedin.com/feed/update/urn:li:activity:1234567890",
"skip_reason": "AiSkip",
"detected_topic": "politics",
"contact_fields": { "…": "see above" },
"contact_custom_fields": { "…": "see above" },
"sequence_fields": { "id": 200, "name": "Test Sequence", "step_number": 1 }
}
```
### Field notes
* `skip_reason`
* `"NoRecentPosts"` — the contact's latest visible post is older than the step's `skipIfNoPostsInDays` window.
* `"PostsUnavailable"` — no posts could be read for the profile. The person may never have posted, or their posts may not be visible to the LinkedIn account used for the lookup.
* `"ReshareWithoutText"` — the post is a reshare that adds no commentary of its own.
* `"NoPostContent"` — the post has no text and no images, so there is nothing to react to.
* `"CommentingRestricted"` — commenting is not permitted on the post.
* `"AiSkip"` — the AI declined to comment, typically because the post matches one of the step's skip topics.
* `post_url`
* URL of the post the step evaluated. `null` for `"NoRecentPosts"` and `"PostsUnavailable"`, where no post was available.
* `detected_topic`
* The post's main topic as detected by the AI. Populated for `"AiSkip"` (may be empty when no topic was determined); `null` for other reasons.
* `sequence_fields`
* Always present; the comment step only runs for contacts in a sequence.
* `contact_custom_fields`
* Included when `includeProspectCustomFields: true`.
***
## Test webhook deliveries
Once a webhook subscription is configured, you can send a test event to your endpoint:
```text theme={null}
POST /v3/webhooks/{id}/test
```
The test payload matches the event schemas shown on this page and uses predictable placeholder values.
# Webhook events
Source: https://docs.reply.io/webhook-events
All webhook events supported by the Reply API, grouped by category.
This page lists all webhook event types supported by the Reply API. Each event links to its payload schema on the [Webhook event payloads](/webhook-event-payloads) page.
To create or manage webhook subscriptions, see the [Webhooks API](/api-reference/webhooks/list-webhook-subscriptions). To retrieve supported event types programmatically, use the [List supported event types](/api-reference/webhooks/list-supported-event-types) endpoint.
When [creating](/api-reference/webhooks/create-a-webhook-subscription) or [updating](/api-reference/webhooks/update-a-webhook-subscription) a subscription, pass the event name as the `eventType` value.
## Email engagement
| Event | When it fires | Payload |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `email_sent` | Reply sends an email from a sequence step. | [View](/webhook-event-payloads#email_sent) |
| `email_opened` | A recipient opens a tracked email. | [View](/webhook-event-payloads#email_opened) |
| `email_link_clicked` | A recipient clicks a tracked link in an email. | [View](/webhook-event-payloads#email_link_clicked) |
| `email_replied` | Reply detects an incoming reply to a sequence email, or a direct email reply not tied to a sequence. Fires once per detected reply. | [View](/webhook-event-payloads#email_replied) |
| `reply_categorized` | An email reply is assigned a category automatically or by a user. | [View](/webhook-event-payloads#reply_categorized) |
| `email_bounced` | A sent email bounces or is rejected by the recipient's mail server. | [View](/webhook-event-payloads#email_bounced) |
| `email_auto_reply` | An incoming message is identified as an auto-reply or out-of-office message. | [View](/webhook-event-payloads#email_auto_reply) |
## Email account health
| Event | When it fires | Payload |
| ------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `email_account_connection_lost` | A connected mailbox can no longer authenticate, such as after an OAuth revocation or password change. | [View](/webhook-event-payloads#email_account_connection_lost) |
| `email_account_error` | A mailbox reports an IMAP or SMTP error, such as an authentication failure, send failure, or rate limit. | [View](/webhook-event-payloads#email_account_error) |
## LinkedIn
| Event | When it fires | Payload |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `linkedin_connection_request_sent` | Reply sends a LinkedIn connection request on behalf of a user. | [View](/webhook-event-payloads#linkedin_connection_request_sent) |
| `linkedin_connection_request_accepted` | A previously sent LinkedIn connection request is accepted. | [View](/webhook-event-payloads#linkedin_connection_request_accepted) |
| `linkedin_message_sent` | Reply sends a LinkedIn message or InMail. | [View](/webhook-event-payloads#linkedin_message_sent) |
| `linkedin_message_replied` | A contact replies in a LinkedIn conversation. | [View](/webhook-event-payloads#linkedin_message_replied) |
| `linkedin_reply_categorized` | A LinkedIn conversation is assigned a category. | [View](/webhook-event-payloads#linkedin_reply_categorized) |
| `linkedin_account_alerts` | A LinkedIn account requires attention, such as a disconnected Sales Navigator account, usage limits, or low InMail credits. | [View](/webhook-event-payloads#linkedin_account_alerts) |
| `linkedin_comment_posted` | Reply posts an AI-generated comment on a contact's most recent LinkedIn post. | [View](/webhook-event-payloads#linkedin_comment_posted) |
| `linkedin_comment_skipped` | A "Comment on a recent post" step skips a contact instead of posting a comment; the payload carries the reason. | [View](/webhook-event-payloads#linkedin_comment_skipped) |
## Calls
| Event | When it fires | Payload |
| ---------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| `contact_called` | A call with a contact reaches a final status, such as connected, missed, voicemail, or logged. | [View](/webhook-event-payloads#contact_called) |
## Contact lifecycle
| Event | When it fires | Payload |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `contact_replied` | A contact enters the replied state — a reply is detected on any channel (email, LinkedIn, or SMS), or the contact is manually marked as replied. Channel-agnostic status signal. | [View](/webhook-event-payloads#contact_replied) |
| `contact_finished` | A contact completes or exits a sequence. | [View](/webhook-event-payloads#contact_finished) |
| `contact_opted_out` | A contact opts out through an unsubscribe link, manual action, or Do Not Contact setting. | [View](/webhook-event-payloads#contact_opted_out) |
## Sequence automation
| Event | When it fires | Payload |
| ------------------- | ---------------------------------------------- | ------------------------------------------------- |
| `autopilot_stopped` | An evergreen sequence's autopilot is disabled. | [View](/webhook-event-payloads#autopilot_stopped) |