# Outreach Actions API

> One-off sends outside a sequence — a single email, LinkedIn connection request, InMail, message, or voice message, on demand.

- **Category:** Outreach
- **Base URL:** `https://api.reply.io/v3`
- **Official reference:** https://docs.reply.io/api-reference/direct-outreach/send-a-direct-email-to-a-contact
- **In Reply's reference:** called “Direct Outreach”
- **OpenAPI:** https://headless-jason-ai-sdr-7ajjk.ondigitalocean.app/apis/outreach-actions.openapi.yaml
- **HTML version:** https://headless-jason-ai-sdr-7ajjk.ondigitalocean.app/apis/outreach-actions

## What it does

The Outreach Actions API sends a single outreach touch to one contact, outside of any sequence. Six operations, all `POST /v3/contacts/{id}/send-direct-*`, all scoped `contacts:operate`:

- **Direct email** — one-off email from a chosen mailbox (or the default account), HTML body, subject required.
- **LinkedIn connection request** — with an optional personalized note; the contact must have a LinkedIn profile URL set.
- **LinkedIn InMail** — for contacts you are not connected with; requires a Sales Navigator account with InMail credits.
- **LinkedIn message** — direct message to an existing connection.
- **LinkedIn voice message** — pre-recorded audio, uploaded first via `POST /v3/attachments/voice`.
- **AI-generated LinkedIn voice message** — synthesized from a text script (max 1,000 chars) using the account's text-to-speech settings.

Each send executes immediately through the specified email or LinkedIn account and returns a receipt: `status` and `messageId` for email, `actionType` plus `linkedInActivityId` for LinkedIn actions.

## The problem it solves

Sequences are built for campaigns, not moments. When a contact replies with a question, when a champion changes jobs, when a signal fires and one precise touch is the right move — spinning up a one-step sequence, starting it, and archiving it is the wrong tool. The Outreach Actions API is the surgical alternative: one call, one contact, one message, on the channel the moment calls for. It gives an agent the same sending infrastructure a sequence uses — connected mailboxes, LinkedIn accounts, deliverability plumbing — without campaign scaffolding.

## How an agent starts using it

Get an API key with the `contacts:operate` scope. Resolve the contact's ID (via the Contact Data endpoints), pick the sending identity — `emailAccountId` for email, `linkedInAccountId` for any LinkedIn action — and `POST` to the matching `/v3/contacts/{id}/send-direct-*` operation. For voice messages, upload the audio first via `POST /v3/attachments/voice` and pass the returned `voiceAttachmentId`; for AI voice, pass a `script` instead. Handle `400` problem responses with stable `code` slugs (e.g. `contact.contactInBlackList`, `contact.emailAccountNotFound`) for programmatic branching.

## Typical agent tasks

- Send a one-off follow-up email to a contact who just replied, without touching their sequence
- Fire a LinkedIn connection request the moment a contact shows a buying signal
- Send a LinkedIn InMail to a prospect you are not connected with yet
- Message an existing LinkedIn connection directly from the agent loop
- Generate and send an AI voice message on LinkedIn from a text script

## Inputs

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `contactId` | integer | yes | Contact ID in the path of every operation (/v3/contacts/{id}/send-direct-*). |
| `subject` | string | no | Subject line — required for direct email and InMail. |
| `body` | string | no | Message body — HTML for direct email, plain text for InMail (max 1,900 chars). |
| `emailAccountId` | integer | no | Mailbox to send the direct email from. Omit to use the default email account. |
| `linkedInAccountId` | integer | no | Connected LinkedIn account to send from — required for all five LinkedIn operations. |
| `message` | string | no | Optional note on a connection request, or the body of a LinkedIn message (max 8,000 chars). |
| `voiceAttachmentId` | integer | no | Uploaded audio for a voice message — upload first via POST /v3/attachments/voice. |
| `script` | string | no | Text to synthesize for an AI-generated voice message (max 1,000 chars). |

## Outputs

| Name | Type | Description |
|------|------|-------------|
| `status` | string | Direct email sending status (new, sent). |
| `messageId` | string | Message ID of the sent direct email. |
| `actionType` | string | LinkedIn action performed (connect, inMail, message, voice, aiVoice). |
| `linkedInActivityId` | integer | ID of the LinkedIn activity record created for the send. |

## Authentication

API key as Bearer token. Send `Authorization: Bearer <API_KEY>` on every request. Required scopes: `contacts:operate`. Get an API key in the Reply.io app under **Settings → API Key** (https://docs.reply.io/api-reference/authentication).

## Rate limits

100 requests/minute and 3,000 requests/hour per user (shared across all of the user's API clients). On 429, honor the Retry-After header. LinkedIn sends execute through the connected LinkedIn account and are additionally subject to LinkedIn's own sending limits.

## Example

### Request — `POST /v3/contacts/{contactId}/send-direct-email`

```bash
curl -X POST https://api.reply.io/v3/contacts/90514382/send-direct-email \
  -H "Authorization: Bearer $REPLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "emailAccountId": 4821,
    "subject": "Re: your question about SOC 2",
    "body": "<p>Hi Dana — short answer, yes. Full report attached to our trust page.</p>"
  }'
```

### Response — `200`

```json
{
  "status": "sent",
  "messageId": "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809"
}
```

### Error — `403`

```json
{
  "type": "https://docs.reply.io/api-reference/authentication",
  "title": "Forbidden",
  "status": 403,
  "detail": "This API key does not have the required scope: contacts:operate."
}
```

## Related APIs

- [Sequence API](https://headless-jason-ai-sdr-7ajjk.ondigitalocean.app/apis/sequences.md) — Create, populate, and control multichannel outreach sequences — email, LinkedIn, and calls in one campaign object.
- [Conversations & Inbox API](https://headless-jason-ai-sdr-7ajjk.ondigitalocean.app/apis/conversations-inbox.md) — The unified inbox as an API — read threads across email and LinkedIn, classify replies with categories, detect meeting intent, and respond.
- [LinkedIn Accounts API](https://docs.reply.io/api-reference/linkedin-accounts/create-a-connection-link) — Connect LinkedIn accounts, manage daily action limits, and monitor connection status for LinkedIn outreach.
- [Mailbox Management API](https://docs.reply.io/api-reference/email-accounts/connect-gmail-account-via-oauth) — Connect and manage sending mailboxes — Gmail/Office 365 OAuth, IMAP/SMTP tests, health, tags, and sending resume.

## FAQ

**When should an agent use this instead of a sequence?**

A sequence is a campaign — multi-step, scheduled, stop-on-reply. Outreach Actions is a single touch, sent now, with no follow-up machinery. Use it for reply follow-ups, one-off intros, and opportunistic LinkedIn touches; use the Sequence API when the contact should enter a multi-step cadence.

**Which mailbox or LinkedIn account does the send go out from?**

Direct email sends from the mailbox in emailAccountId, or the user's default email account if omitted. Every LinkedIn operation requires linkedInAccountId pointing to an active connected LinkedIn account — InMail additionally needs a Sales Navigator subscription with InMail credits, and message/voice sends require an existing connection with the contact.

**Are direct sends tracked anywhere?**

Yes. A direct email returns a status and messageId; LinkedIn operations return the actionType and a linkedInActivityId for the activity record created. Replies from the contact land in the unified inbox — read them with the Conversations & Inbox API.
