Outreach
Run AI-driven link-building outreach from your own Gmail. Your agent finds prospects and writes every email, you approve, and RankParse sends on a schedule.
Outreach turns competitor backlink data into a real link-building campaign. Your AI agent does the research, finds a contact at each domain and writes the copy through the RankParse tools. RankParse stores the campaign, and sends the emails from your connected Gmail account on a schedule with a daily cap, so replies land in your own inbox.
You approve before anything sends. When an agent launches a campaign through the RankParse MCP server, the emails go to your approval inbox instead of being queued. You read every email, remove any you do not like, and approve. Outreach is the one action that can never be set to run automatically.
Setup
- Add the RankParse MCP server to your agent. The outreach tools are on the API-key MCP endpoint only:
https://mcp.rankparse.com/mcpfor Claude Code, Cursor and other HTTP clients, orhttps://mcp.rankparse.com/for clients that need the root path, with your API key. The secure OAuth connector used by Claude and ChatGPT on the web does not include outreach tools. See MCP server for the endpoint choice and client setup. - Connect Gmail at Integrations. Emails are sent from that account.
- Start a campaign from Outreach and choose New campaign. The page gives you a ready-to-paste starter prompt that walks your agent through the whole flow: interview you about your site and competitors, mine competitor gaps for prospects, research a real contact at each domain, draft a short personalized email for each, show you everything, and only create and launch the campaign after you approve.
How a campaign runs
- Research. The agent builds a prospect list with
get_competitor_gapandget_domain_authority(5 and 1 credits per call), then finds one real contact email per domain. - Draft. It writes a short subject and body for each contact and shows you the drafts in chat.
- Create and fill. After you approve the drafts in chat, it calls
outreach_create_campaignandoutreach_add_contacts. This creates a draft campaign and stores the emails. Nothing is sent. - Launch. It calls
outreach_launch_campaign. For agents on the MCP server this creates an approval request and returnsstatus: "awaiting_approval"with areview_url. A shortcut,propose_outreach_send, does steps 3 and 4 in one call. - Approve. You open the
review_url, read each email with its recipient, subject and body, optionally remove some, and approve. You get a daily email when items are waiting. - Send. RankParse queues exactly the emails you approved. Sending is paced by your Gmail schedule and daily cap, so a campaign drains over days, not minutes.
- Track. Follow progress on the campaign page in the dashboard (stats, per-contact status, queued sends) or with
outreach_get_campaign. Campaigns can be paused and resumed. Once an email is queued it can no longer be removed from the approval review.
Credits: creating campaigns, adding contacts, launching and sending cost 0 credits. Only the research calls (competitor gap, domain authority and the like) use credits.
What is held for approval
| Action | By an agent through MCP | From the dashboard |
|---|---|---|
| Launch a campaign (send emails) | Held for approval | Your own click, runs now |
| Raise the daily send limit, or switch auto-send on | Held for approval | Applies now |
| Lower the daily send limit, or switch sending off | Applies immediately | Applies now |
| Create a draft, add contacts, pause, resume | Applies immediately (nothing is sent) | Applies now |
Approval covers agents that use the RankParse MCP server, or that send the header X-RankParse-Client: mcp. A script that calls the REST routes below directly with your API key, without that header, launches immediately. See Where approval is enforced.
Rules for contacts
| Rule | Value |
|---|---|
Contacts per add contacts call | up to 100 |
| Contacts per campaign | up to 500 |
| Contacts per agent approval request | up to 100 |
| Per domain | one contact per domain, and one per email address |
| Subject | up to 500 characters (aim for 2 to 5 words) |
| Body | up to 10,000 characters, plain text |
| Name | up to 200 characters, optional |
| Already emailed | An address you contacted from an earlier campaign is skipped |
| Internationalized domains | Use the punycode (xn--) form |
Contacts that fail a rule are not added and are returned in skipped with a reason: invalid_email, invalid_domain, missing_copy, copy_too_long, duplicate_email, duplicate_domain, campaign_full, or already_contacted. Add contacts to a campaign that has already launched to top it up; the new contacts stay inactive until the campaign is launched (and approved) again.
Sending
- Daily cap.
max_sends_per_dayis between 1 and 100 and applies across all your campaigns. It defaults to 20. - Auto-send is a master switch. When it is off, a launch still queues emails but nothing sends until you turn it on; the launch response includes a warning.
- Pausing one campaign holds its queued emails without losing them. Other campaigns keep sending.
- Estimate. The launch response gives
estimated_days, the queue divided by the daily cap, rounded up.
Tools
Available on the API-key MCP endpoint. Each is 0 credits.
| Tool | What it does | REST route |
|---|---|---|
outreach_create_campaign | Create a draft campaign. | POST /v1/outreach/campaigns |
outreach_add_contacts | Add contacts with copy to a campaign. | POST /v1/outreach/campaigns/:id/contacts |
outreach_launch_campaign | Queue the reviewed contacts (held for approval for agents). | POST /v1/outreach/campaigns/:id/launch |
outreach_get_campaign | Status and delivery counts. | GET /v1/outreach/campaigns/:id |
outreach_list_campaigns | List campaigns, optionally for one website. | GET /v1/outreach/campaigns |
outreach_suggest_prospects | Context for topping up: capacity, queue and domains to exclude. | GET /v1/outreach/campaigns/:id/prospecting |
outreach_sending_status | Gmail pipeline status. | GET /v1/outreach/sending |
outreach_configure_sending | Change the daily cap or auto-send. | PATCH /v1/outreach/sending |
outreach_pause_campaign | Pause a campaign. | POST /v1/outreach/campaigns/:id/pause |
outreach_resume_campaign | Resume a campaign. | POST /v1/outreach/campaigns/:id/resume |
propose_outreach_send | Create or top up a campaign and ask for approval in one call. | POST /v1/proposals with kind: "outreach_send" |
REST API
The same operations are available over REST at https://api.rankparse.com/v1/outreach with your X-API-Key. All routes cost 0 credits, share the 60 requests per minute limit, and need Gmail connected for anything that sends. Errors use the standard { error, code, message } shape.
Create a campaign
POST /v1/outreach/campaigns
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Up to 200 characters. |
your_domain | string | Yes | The site you are building links for. URL prefixes and www. are stripped. |
goal | string | No | A note for the campaign record. |
site_id | string | No | A website id from GET /v1/websites. Omit to leave the campaign unassigned. |
curl -X POST "https://api.rankparse.com/v1/outreach/campaigns" \
-H "X-API-Key: rp_your_key" \
-H "Content-Type: application/json" \
-d '{"name": "SaaS blogs, competitor gap Oct 2026", "your_domain": "example.com", "site_id": "site_abc123"}'import requests
res = requests.post(
"https://api.rankparse.com/v1/outreach/campaigns",
headers={"X-API-Key": "rp_your_key"},
json={
"name": "SaaS blogs, competitor gap Oct 2026",
"your_domain": "example.com",
"site_id": "site_abc123",
},
)
campaign = res.json()
print(campaign["id"], campaign["status"])const res = await fetch("https://api.rankparse.com/v1/outreach/campaigns", {
method: "POST",
headers: { "X-API-Key": "rp_your_key", "Content-Type": "application/json" },
body: JSON.stringify({
name: "SaaS blogs, competitor gap Oct 2026",
your_domain: "example.com",
site_id: "site_abc123",
}),
});
const campaign = await res.json();
console.log(campaign.id, campaign.status);{ "id": "V1StGXR8_Z5jdHi6B-myT", "status": "draft", "max_contacts": 500, "site_id": "site_abc123" }Add contacts
POST /v1/outreach/campaigns/:id/contacts with { "contacts": [ ... ] }. Works on a draft, a ready-to-review campaign, or a launched one (a top-up).
curl -X POST "https://api.rankparse.com/v1/outreach/campaigns/V1StGXR8_Z5jdHi6B-myT/contacts" \
-H "X-API-Key: rp_your_key" \
-H "Content-Type: application/json" \
-d '{
"contacts": [{
"email": "ann@alpha-blog.com",
"name": "Ann Lee",
"domain": "alpha-blog.com",
"subject": "your AI tools guide",
"body": "Hi Ann, I read your guide to AI writing tools and ...",
"domain_authority": 54
}]
}'{
"added": 1,
"total_contacts": 1,
"skipped": []
}On a launched campaign the response also carries next_step, reminding you that the new contacts are inactive until the next launch. Skipped contacts look like { "email": "bob@beta.com", "reason": "already_contacted" }.
Launch
POST /v1/outreach/campaigns/:id/launch (no body). Safe to call again: it only queues contacts that are not already queued or sent.
Direct REST call, without the agent header:
{
"queued": 18,
"suppressed": 2,
"auto_send_enabled": true,
"max_sends_per_day": 20,
"estimated_days": 1
}suppressed counts contacts skipped because you already emailed them from another campaign. A warning field appears when auto-send is off, and paused_warning when the campaign is paused. If every contact was suppressed, the response is queued: 0 with a message.
Agent call (MCP, or the X-RankParse-Client: mcp header) in an account with the inbox enforced:
{
"queued": 0,
"suppressed": 0,
"auto_send_enabled": true,
"max_sends_per_day": 20,
"estimated_days": 0,
"status": "awaiting_approval",
"proposal_id": "prp_K3mP9xQ2vLw8Rt5Yn1BzA",
"review_url": "https://rankparse.com/dashboard/sites/site_abc123/today?proposal=prp_K3mP9xQ2vLw8Rt5Yn1BzA",
"message": "Sending now waits for the user's approval in RankParse (...)."
}If status is approved, a person already approved this exact set and it is running; do not launch again. An approval request carries at most 100 contacts, so a larger campaign needs a further launch after the first is approved. See Proposals.
Read a campaign
GET /v1/outreach/campaigns/:idreturnsid,name,your_domain,goal,status,paused,created_at,site_id,site_hostandcontactswithtotal,awaiting_launch,queued,sending,sentandskipped.GET /v1/outreach/campaignsreturns{ "campaigns": [...] }, newest first, at most 100, each withtotal_contacts,queued_contacts,sent_contactsandpaused. Add?site_id=to filter to one website.GET /v1/outreach/campaigns/:id/prospectingreturns the campaign, itscapacity, its sendqueue,exclude_domains(every domain you have used in any campaign, to filter new prospects against),exclude_domains_truncated,recommended_batch_sizeand anext_stepsentence.
Campaign status is one of draft, ready_to_review, pushing (a launch is running), pushed (launched; can still be topped up) or a legacy wizard state.
Sending settings
GET /v1/outreach/sending returns connected, gmail_email, auto_send_enabled, max_sends_per_day, sent_today, remaining_today, queued_across_campaigns and estimated_days_to_drain. Without Gmail it returns { "connected": false, "message": "..." }.
PATCH /v1/outreach/sending changes the settings. At least one field is required.
| Field | Type | Description |
|---|---|---|
max_sends_per_day | integer | 1 to 100, across all campaigns. |
auto_send_enabled | boolean | false stops all sending, true resumes it. |
A direct call replies { "ok": true }. For an agent, lowering the cap or turning sending off applies immediately, while raising the cap or turning sending on is held for approval and replies 202 with ok: true, status, proposal_id, review_url and message. A mixed request applies the stop now and proposes the rest; the response then includes applied. If another sending change is already approved and running, the increase is refused with 409 change_in_progress.
Pause and resume
POST /v1/outreach/campaigns/:id/pause and /resume return { "ok": true, "paused": true } or { "ok": true, "paused": false }. Repeating either is harmless and adds a message.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_json | The body is not valid JSON. |
| 400 | invalid_name, invalid_domain, invalid_site_id | A required field is missing or malformed. |
| 400 | no_contacts | Nothing to add, or no ready contacts to launch. |
| 400 | too_many_contacts | More than 100 contacts in one call. |
| 400 | gmail_not_connected | Connect Gmail first. |
| 400 | sending_provider_unsupported | Only Gmail campaigns can be launched this way. |
| 400 | invalid_send_cap, invalid_auto_send, nothing_to_update | Bad PATCH /sending body. |
| 403 | capability_missing | Your role in the workspace does not allow this. |
| 404 | campaign_not_found, site_not_found | The campaign or website is not in your account. |
| 409 | campaign_not_editable | The campaign is mid-launch or in a state that cannot take contacts. Retry shortly. |
| 409 | campaign_launch_in_progress | Another launch of this campaign is running. Nothing was lost; retry. |
| 409 | change_in_progress | A different sending change is already approved and running. |
| 429 | rate_limited | More than 60 requests in a minute. |
Agent-origin launches and sending changes can also return the proposal errors listed on Proposals, such as too_many_pending_proposals.
Troubleshooting
Related
Approval inbox
How agent-proposed actions work in RankParse. An agent proposes, a person approves in the dashboard, and only then does anything run.
Sites & Rank Tracking
Connect Google Search Console, Bing and Yandex, attach properties to a website, add queries, turn on weekly alerts and read the results from the dashboard, API or MCP.