Skip to content

Pipevoy MCP server is live. Read the docs.

API and MCP docs

LinkedIn outreach your agent can call

36 REST endpoints and 36 MCP tools on one API key. Every send still goes through warm-up, limits, working hours and approvals.

OpenAPI 3.1Markdown
Base URL
https://pipevoy.com/api/v1
MCP server
https://pipevoy.com/api/mcp
Auth
Authorization: Bearer pv_…
Access
Growth and higher, and the 14-day trial
On this page

Quickstart: zero to booked meetings, no UI

An agent can do the whole setup itself. Three steps need a person, and we won’t pretend otherwise: confirming the signup email, logging in to LinkedIn once per sender account, and paying when the trial ends.

Quickstart with curl
# 1. Sign up (a person confirms the emailed link)
curl -X POST https://pipevoy.com/api/v1/signup -H "Content-Type: application/json" \
  -d '{"email":"founder@acme.com","company_name":"Acme"}'
# → { "id": "…", "claim_token": "pvc_…" }

# 2. Poll until the key arrives (returned once)
curl https://pipevoy.com/api/v1/signup/$SIGNUP_ID -H "X-Claim-Token: $CLAIM_TOKEN"
export PIPEVOY_API_KEY=pv_…

# 3. Company profile from the website
curl -X POST https://pipevoy.com/api/v1/company/analyze -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" -d '{"website":"https://acme.com"}'

# 4. A LinkedIn sender; a person opens connect_url once and logs in
curl -X POST https://pipevoy.com/api/v1/senders -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Sara Khan","timezone":"America/New_York","booking_url":"https://cal.com/sara/intro"}'
curl https://pipevoy.com/api/v1/senders/$SENDER_ID -H "Authorization: Bearer $PIPEVOY_API_KEY"   # wait for "status": "connected"

# 5. Events to your agent (optional; or poll)
curl -X POST https://pipevoy.com/api/v1/webhooks -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://agent.example.com/hooks","events":["draft.created","reply.received","meeting.booked"]}'

# 6. Draft a sequence, then create and start the campaign
curl -X POST https://pipevoy.com/api/v1/campaigns/draft -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"objective":"Book intro calls with RevOps leaders","cta":"a 20-minute call"}'
curl -X POST https://pipevoy.com/api/v1/campaigns -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" -H "Idempotency-Key: launch-1" \
  -d '{"name":"RevOps East Coast","cta":"a 20-minute call","steps":[…],"sender_ids":["'$SENDER_ID'"]}'

# 7. Enroll prospects
curl -X POST https://pipevoy.com/api/v1/campaigns/$CAMPAIGN_ID/enroll -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"linkedin_urls":["https://www.linkedin.com/in/jane-doe"]}'

# 8. Approve drafts (the engine sends within limits and working hours)
curl https://pipevoy.com/api/v1/approvals -H "Authorization: Bearer $PIPEVOY_API_KEY"
curl -X POST https://pipevoy.com/api/v1/approvals/$DRAFT_ID/approve -H "Authorization: Bearer $PIPEVOY_API_KEY"

# 9. Replies and meetings
curl "https://pipevoy.com/api/v1/conversations?since=2026-10-20T00:00:00Z" -H "Authorization: Bearer $PIPEVOY_API_KEY"
curl -X POST https://pipevoy.com/api/v1/conversations/$CONVERSATION_ID/reply -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" -d '{"body":"Does Tuesday work?"}'
curl https://pipevoy.com/api/v1/meetings -H "Authorization: Bearer $PIPEVOY_API_KEY"

# 10. Before the trial ends: a person pays
curl -X POST https://pipevoy.com/api/v1/billing/checkout -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" -d '{"plan":"growth","interval":"month"}'

The same sequence with MCP tools

  1. signup { email, company_name }: no key needed
  2. signup_status { id, claim_token }: returns api_key once; reconnect with it
  3. analyze_company { website }
  4. create_sender { name, timezone, booking_url }: a person opens connect_url
  5. get_sender { id }: until status is connected
  6. create_webhook { url, events }: optional
  7. draft_campaign { objective, cta }
  8. create_campaign { name, steps, sender_ids }: require_approval defaults to true
  9. enroll_prospects { id, linkedin_urls }
  10. list_pending_approvals → approve_draft { id, body? }
  11. list_conversations → get_conversation → draft_reply { id, body }: then approve_draft
  12. list_meetings → update_meeting { id, status, qualified }
  13. create_checkout_link { plan, interval }: a person pays

Connect over MCP

One server, https://pipevoy.com/api/mcp, with the same API key as REST. Pick your client and paste:

Claude Desktop: claude_desktop_config.json
{
  "mcpServers": {
    "pipevoy": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://pipevoy.com/api/mcp",
               "--header", "Authorization: Bearer ${PIPEVOY_API_KEY}"],
      "env": { "PIPEVOY_API_KEY": "pv_your_key_here" }
    }
  }
}
Claude Code
claude mcp add --transport http pipevoy https://pipevoy.com/api/mcp \
  --header "Authorization: Bearer $PIPEVOY_API_KEY"
Any MCP client (Streamable HTTP)
{
  "type": "streamable-http",
  "url": "https://pipevoy.com/api/mcp",
  "headers": { "Authorization": "Bearer pv_your_key_here" }
}

No key yet? Without one, only signup and signup_status work, so an agent can create its own workspace. Full details in MCP server.

Give this to your agent

A plain-text brief to paste into an agent’s context: the endpoints, the links and the three steps that need a person. It is what COPY AGENT PROMPT copies.

agent-brief.txt
You can run LinkedIn outreach through Pipevoy, a headless AI SDR. It drafts LinkedIn invites, follow-ups and replies for a workspace and sends them through its own engine, which enforces warm-up, per-sender daily and weekly limits, working hours and approvals. Never send on LinkedIn yourself.

REST API  https://pipevoy.com/api/v1
MCP       https://pipevoy.com/api/mcp
Docs      https://pipevoy.com/docs/api
OpenAPI   https://pipevoy.com/api/v1/openapi.json
llms.txt  https://pipevoy.com/llms.txt
Auth      Authorization: Bearer <api key> (same key for REST and MCP)

No key yet: POST /api/v1/signup with {"email", "company_name"}, no auth. Then poll GET /api/v1/signup/{id} with header X-Claim-Token until api_key appears. It is returned once; store it.

Start each session with GET /api/v1/me. Its setup.next_step says what to do next. Approving a draft only queues it; the engine decides when it is safe to send. Every error has error.hint with the next step.

Ask your human for exactly three things:
1. Open the confirmation email Pipevoy sends after signup.
2. Open each sender's connect_url and log in to LinkedIn once.
3. Pay the Stripe Checkout link before the 14-day trial ends.

From zero to the first approved invite

The same setup, one call at a time. Or paste the brief above into your agent and let it work through GET /api/v1/me, which always names the next step.

1. Sign up

No key needed. A person confirms the emailed link; then the key is returned once to whoever holds the claim token.

POST /api/v1/signup
curl -X POST https://pipevoy.com/api/v1/signup -H "Content-Type: application/json" \
  -d '{"email":"founder@acme.com","company_name":"Acme"}'
# { "id": "…", "claim_token": "pvc_…", "status": "pending" }

curl https://pipevoy.com/api/v1/signup/$SIGNUP_ID -H "X-Claim-Token: $CLAIM_TOKEN"
# { "status": "verified", "api_key": "pv_…" }

2. Analyse your company

The AI reads your website and writes value propositions, ideal customer and personas. Adjust them with PUT /api/v1/company.

POST /api/v1/company/analyze
curl -X POST https://pipevoy.com/api/v1/company/analyze -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"website":"https://acme.com"}'

3. Create a sender

One LinkedIn account. Its owner opens connect_url once and logs in; nobody sees the password. Poll until it is connected.

POST /api/v1/senders
curl -X POST https://pipevoy.com/api/v1/senders -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Sara Khan","timezone":"America/New_York","booking_url":"https://cal.com/sara/intro"}'
# { "sender": { "status": "pending", … }, "connect_url": "https://account.unipile.com/…" }

4. Create a campaign

Draft the sequence with AI, edit it if you like, then create it. It starts as soon as a sender is connected.

POST /api/v1/campaigns
curl -X POST https://pipevoy.com/api/v1/campaigns/draft -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"objective":"Book intro calls with RevOps leaders","cta":"a 20-minute call"}'

curl -X POST https://pipevoy.com/api/v1/campaigns -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"RevOps East Coast","steps":[…],"sender_ids":["'$SENDER_ID'"],"require_approval":true}'

5. Approve drafts, or subscribe to webhooks

Poll the queue, or get draft.created, reply.received and meeting.booked pushed to you, signed with HMAC-SHA256.

POST /api/v1/approvals/{id}/approve
curl https://pipevoy.com/api/v1/approvals -H "Authorization: Bearer $PIPEVOY_API_KEY"
curl -X POST https://pipevoy.com/api/v1/approvals/$DRAFT_ID/approve -H "Authorization: Bearer $PIPEVOY_API_KEY"

curl -X POST https://pipevoy.com/api/v1/webhooks -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://agent.example.com/hooks","events":["draft.created","reply.received"]}'

What a human still does

Three steps need a person, by design. The API returns the link for each, so your agent knows when to ask.

StepWhyLink comes from
Confirm the signup emailThe workspace and its API key exist only after a person clicks the link.POST /api/v1/signup
Log in to LinkedIn once per senderA hosted sign-in. Neither we nor your agent see the password.connect_url from POST /api/v1/senders
PayBy card on Stripe Checkout, before the 14-day trial ends. Unpaid, sending pauses; nothing is deleted.url from POST /api/v1/billing/checkout

Endpoints an agent uses most

36 REST endpoints under /api/v1, each with a matching MCP tool, JSON Schema and an error hint that says what to do next. These are the ones an agent uses most; every endpoint is documented below.

MethodPathWhat it doesMCP tool
POST/api/v1/signupCreate a workspace without the UI. A person confirms the emailed link; no key needed.signup
POST/api/v1/company/analyzeRead your website and write the company profile every draft is based on.analyze_company
POST/api/v1/sendersAdd a LinkedIn account. Returns connect_url for its owner's one-time login.create_sender
POST/api/v1/campaignsCreate and start a campaign from AI-drafted or your own steps.create_campaign
POST/api/v1/campaigns/{id}/enrollEnroll prospects by id or LinkedIn URL. Unknown URLs are added first.enroll_prospects
GET/api/v1/approvalsList drafts waiting for approval, with the prospect and thread.list_pending_approvals
POST/api/v1/approvals/{id}/approveApprove a draft, optionally edited. It joins the send queue.approve_draft
GET/api/v1/conversationsReplies, already classified by intent, newest first.list_conversations
POST/api/v1/conversations/{id}/replyWrite a reply. It becomes a draft; approve it to send.draft_reply
PATCH/api/v1/meetings/{id}Record held, no-show or qualified. A no-show drafts a rebook note.update_meeting
POST/api/v1/signalsPush buying signals from Clay, your CRM or your own research.push_signal
POST/api/v1/webhooksSubscribe to signed events instead of polling.create_webhook
POST/api/v1/billing/checkoutGet a Stripe Checkout link for a person to pay.create_checkout_link

Designed to compose

  1. Clay or your CRM: A stage changes or a column fills
  2. POST /api/v1/signals: or enroll people directly by LinkedIn URL
  3. Pipevoy engine: Drafts, waits for approval, sends inside the limits
  4. Webhook reply.received: Signed with HMAC-SHA256
  5. Your agent or HubSpot: Answers, books, updates the record

Safety rules the engine enforces

An agent can be talked into anything. These rules are enforced in code on every send, whatever the request says. No endpoint sends to LinkedIn directly.

RuleYour agent on LinkedIn directlyYour agent calling Pipevoy
Warm-up rampA new account sends at full volume on day one.Limits ramp 25%, 50%, 75%, 100% over weeks 1 to 4, from the account's first send.
Daily and weekly limitsWhatever your loop sends.Per sender, in the sender's own day: 20 invites and 40 messages by default. Invites are also capped at 100 over a rolling 7 days. Plan ceilings: 100 invites a week, 50 to 100 messages a day; higher values are refused.
Working hoursSends at 3am if that is when the job runs.Only inside each sender's hours and days (default 09:00 to 18:00, Monday to Friday, in their timezone), 2 to 6 minutes apart and paced across the day, up to 30 minutes between sends.
Approval requiredThe first draft goes straight out.require_approval defaults to true. Drafts wait as pending_approval until a person or your agent approves them.
Outcome unknown, never resentA timeout looks like a failure, so it retries and the prospect gets the message twice.A message or reply that hits a timeout or 5xx is marked failed with “Outcome unknown”, still counts toward the limits, and pauses that sequence until a person checks LinkedIn. Invites can retry, because LinkedIn rejects a duplicate invitation.
RestrictionsKeeps going after LinkedIn pushes back.A restricted sender pauses itself and fires the sender.restricted webhook. No retry loop.
Kill switchStop the process and hope nothing is in flight.PATCH /api/v1/senders/{id} with paused: true stops one account. SENDING_DISABLED=true stops every send on the deployment; drafts still queue. Unpaid trials pause sending too, without deleting data.

Honest caveat: LinkedIn’s User Agreement restricts automated activity, and any tool that acts on an account carries some risk of restriction. These rules reduce that risk; they cannot remove it.

Authentication

Create a key in Settings → API (owners and admins), or let an agent sign up with POST /api/v1/signup. Send it on every request as Authorization: Bearer pv_…. A key is shown once and stored only as a hash, belongs to one workspace, and can be revoked at any time. Approvals made with a key are recorded as the person who created it.

API and MCP access is part of the Growth, Agency, Enterprise and Done-For-You plans and of the free trial. On Starter, requests answer 403 plan_upgrade_required, except the billing endpoints, so an agent can still start an upgrade.

Rate limits, pagination, idempotency, errors

  • Rate limit: 120 requests per minute per key. Over it you get 429 with Retry-After; every response carries X-RateLimit-Remaining.
  • Pagination: list endpoints take ?limit= (1 to 100, default 25) and &cursor=, and return { data, next_cursor }. Pass next_cursor back unchanged; null means the last page.
  • Idempotency: send Idempotency-Key on any POST. The first response is stored for 24 hours and replayed for retries with the same key and body; the same key with a different body answers 422.
  • Errors: { "error": { "code", "message", "hint" } } with a matching status (400 validation, 401 key, 403 plan or limit, 404 not in this workspace, 409 state conflict, 422 can’t do that, 429 slow down). The hint says what to do next.
  • Safety: no endpoint sends to LinkedIn directly. Approving queues a message; the engine sends it inside the sender’s working hours, limits and warm-up, and only while billing allows sending.
Error example
{
  "error": {
    "code": "plan_limit",
    "message": "Your Growth plan includes 3 LinkedIn senders (you have 3). Upgrade to Agency for 25 in Settings → Billing.",
    "hint": "Free up capacity or upgrade: POST /api/v1/billing/checkout {\"plan\":\"agency\",\"interval\":\"month\"} and have a human pay the returned URL.",
    "resource": "senders",
    "limit": 3,
    "used": 3
  }
}

Why a send is held

Approving queues a message; the engine decides when it goes. The table shows the engine’s own explanation for each state, produced by the same code that fills the Next column in the app (example: a New York sender working Monday to Friday, 09:00 to 18:00 ET, on a Friday afternoon). Over the API you read the inputs: can_send and sending_paused_reason on GET /me, and status, effective_limits and today on GET /senders/{id}.

WhenReasonEngine says
Kill switch on (SENDING_DISABLED=true)sending_disabledSending disabled
Trial ended unpaid, or payment failed past the grace periodbilling_pausedSending paused (billing)
The sender has not finished the LinkedIn loginsender_not_connectedSender not connected
Outside the sender's working hoursoutside_hoursOutside working hours · next send Mon 09:00 America/New_York
Warm-up week 1: 25% of the configured limitswarmup_limitWarm-up limit reached (week 1) · resumes Mon 09:00 America/New_York
Daily invite limit useddaily_invite_limitDaily invite limit reached · resumes Mon 09:00 America/New_York
Rolling 7-day invite limit usedweekly_invite_limitWeekly invite limit reached · resumes Mon 09:00 America/New_York
Daily message limit used (sequence messages keep 20% back for replies)daily_message_limitDaily message limit reached · resumes Mon 09:00 America/New_York
Spacing sends from the same accountpacingPacing between sends · next send Fri 17:18 America/New_York
Nothing holding itreadySending shortly

Workspace and billing

Start every session with GET /me: its setup.next_step says what to do next.

Workspace, plan, limits and setup

GET/api/v1/meMCP tool get_workspace#

Plan, trial, limits and usage, whether sending is allowed right now, and a setup checklist.

Request
curl https://pipevoy.com/api/v1/me -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "workspace": {
    "id": "6f1d2c3b-8a47-4e15-9b0c-3d2e1f4a5b6c",
    "name": "Acme",
    "created_at": "2026-10-20T09:14:00.000Z"
  },
  "plan": "growth",
  "plan_name": "Growth",
  "status": "trialing",
  "trial_ends_at": "2026-11-03T09:14:00.000Z",
  "days_left_in_trial": 14,
  "can_send": true,
  "sending_paused_reason": null,
  "limits": {
    "senders": 3,
    "seats": 5,
    "active_campaigns": 25,
    "signal_watches": 10,
    "monthly_ai_drafts": 5000
  },
  "usage": {
    "senders": 1,
    "seats": 1,
    "active_campaigns": 1,
    "signal_watches": 0,
    "ai_drafts_this_month": 12
  },
  "api_key": {
    "prefix": "pv_8KfQ2mZx1a",
    "name": "Sales agent",
    "rate_limit_per_minute": 120
  },
  "setup": {
    "company_profile": true,
    "senders_created": 1,
    "senders_connected": 1,
    "active_campaigns": 1,
    "next_step": "Add prospects and enroll them, then approve drafts: GET /api/v1/approvals."
  }
}

Billing status

GET/api/v1/billingMCP tool get_billing#

Works on every plan, including Starter.

Request
curl https://pipevoy.com/api/v1/billing -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "plan": "growth",
  "plan_name": "Growth",
  "status": "trialing",
  "interval": null,
  "trial_ends_at": "2026-11-03T09:14:00.000Z",
  "days_left_in_trial": 14,
  "current_period_end": null,
  "grace_until": null,
  "has_subscription": false,
  "can_send": true,
  "sending_paused_reason": null,
  "limits": {
    "senders": 3,
    "seats": 5,
    "active_campaigns": 25,
    "signal_watches": 10,
    "monthly_ai_drafts": 5000
  },
  "usage": {
    "senders": 1,
    "seats": 1,
    "active_campaigns": 1,
    "signal_watches": 0,
    "ai_drafts_this_month": 12
  }
}

Checkout link for a person to pay

POST/api/v1/billing/checkoutMCP tool create_checkout_link#

Returns a Stripe Checkout URL. A person opens it and pays; an agent can't. If the workspace already has a subscription you get the billing portal instead, so nobody pays twice.

Request
curl -X POST https://pipevoy.com/api/v1/billing/checkout -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"plan":"growth","interval":"month"}'
Response
{
  "url": "https://checkout.stripe.com/c/pay/cs_…",
  "plan": "growth",
  "interval": "month",
  "human_step": "A person must open this URL and pay by card. Poll GET /api/v1/billing until status is \"active\"."
}

Signup without the UI

No key needed. Nothing is created until a person confirms the emailed link.

Start a signup

POST/api/v1/signupMCP tool signup#

Emails a verification link to the address. Returns a claim_token, shown once. Rate-limited per network and per email domain.

Request
curl -X POST https://pipevoy.com/api/v1/signup -H "Content-Type: application/json" \
  -d '{"email":"founder@acme.com","company_name":"Acme","website":"https://acme.com"}'
Response
{
  "id": "0f1e2d3c-4b5a-4968-8776-655443322110",
  "status": "pending",
  "email": "founder@acme.com",
  "claim_token": "pvc_…",
  "expires_at": "2026-10-21T09:14:00.000Z",
  "poll": "GET /api/v1/signup/0f1e2d3c-4b5a-4968-8776-655443322110 with header X-Claim-Token: <claim_token>",
  "next": "A verification link was emailed to founder@acme.com. A person must open it and confirm…"
}

Poll for the API key

GET/api/v1/signup/{id}MCP tool signup_status#

Poll every 10 to 30 seconds. After confirmation the first response carries api_key; later responses don't.

Request
curl https://pipevoy.com/api/v1/signup/0f1e2d3c-4b5a-4968-8776-655443322110 -H "X-Claim-Token: $CLAIM_TOKEN"
Response
{
  "id": "0f1e2d3c-4b5a-4968-8776-655443322110",
  "status": "verified",
  "email": "founder@acme.com",
  "workspace_id": "6f1d2c3b-8a47-4e15-9b0c-3d2e1f4a5b6c",
  "api_key": "pv_…",
  "expires_at": "2026-10-21T09:14:00.000Z",
  "next": "Store this key now; it is not shown again…"
}

Company profile

Every draft is written from this profile: value props, ICP, personas.

Build the profile from the website

POST/api/v1/company/analyzeMCP tool analyze_company#

Reads a few pages of the site and writes the profile with AI. Uses one request from the daily AI allowance.

Request
curl -X POST https://pipevoy.com/api/v1/company/analyze -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"website":"https://acme.com"}'
Response
{
  "company": {
    "website": "https://acme.com",
    "company_name": "Acme",
    "summary": "Acme sells revenue analytics to B2B SaaS teams.",
    "value_props": [
      "Forecasts you can trust in week one"
    ],
    "proof_points": [],
    "icp": {
      "industries": [
        "SaaS"
      ],
      "company_sizes": [
        "51-200"
      ],
      "geographies": [
        "US",
        "Canada"
      ],
      "titles": [
        "VP Sales",
        "Head of RevOps"
      ],
      "seniorities": [
        "VP",
        "Head"
      ],
      "pain_points": [
        "Forecast misses"
      ],
      "exclusions": []
    },
    "personas": [
      {
        "name": "RevOps lead",
        "titles": [
          "Head of RevOps"
        ],
        "goals": [
          "Clean pipeline data"
        ],
        "pains": [
          "Manual reporting"
        ],
        "messaging_angle": "Time back every Monday"
      }
    ],
    "competitors": [],
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Read the profile

GET/api/v1/companyMCP tool get_company#

null until it has been built or written.

Request
curl https://pipevoy.com/api/v1/company -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "company": {
    "website": "https://acme.com",
    "company_name": "Acme",
    "summary": "Acme sells revenue analytics to B2B SaaS teams.",
    "value_props": [
      "Forecasts you can trust in week one"
    ],
    "proof_points": [],
    "icp": {
      "industries": [
        "SaaS"
      ],
      "company_sizes": [
        "51-200"
      ],
      "geographies": [
        "US",
        "Canada"
      ],
      "titles": [
        "VP Sales",
        "Head of RevOps"
      ],
      "seniorities": [
        "VP",
        "Head"
      ],
      "pain_points": [
        "Forecast misses"
      ],
      "exclusions": []
    },
    "personas": [
      {
        "name": "RevOps lead",
        "titles": [
          "Head of RevOps"
        ],
        "goals": [
          "Clean pipeline data"
        ],
        "pains": [
          "Manual reporting"
        ],
        "messaging_angle": "Time back every Monday"
      }
    ],
    "competitors": [],
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Update the profile

PUT/api/v1/companyMCP tool update_company#

Send only the fields to change. Lists replace the stored list; ICP fields merge.

Request
curl -X PUT https://pipevoy.com/api/v1/company -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"icp":{"geographies":["US","Canada"]},"personas":[{"name":"RevOps lead","titles":["Head of RevOps"]}]}'
Response
{
  "company": {
    "website": "https://acme.com",
    "company_name": "Acme",
    "summary": "Acme sells revenue analytics to B2B SaaS teams.",
    "value_props": [
      "Forecasts you can trust in week one"
    ],
    "proof_points": [],
    "icp": {
      "industries": [
        "SaaS"
      ],
      "company_sizes": [
        "51-200"
      ],
      "geographies": [
        "US",
        "Canada"
      ],
      "titles": [
        "VP Sales",
        "Head of RevOps"
      ],
      "seniorities": [
        "VP",
        "Head"
      ],
      "pain_points": [
        "Forecast misses"
      ],
      "exclusions": []
    },
    "personas": [
      {
        "name": "RevOps lead",
        "titles": [
          "Head of RevOps"
        ],
        "goals": [
          "Clean pipeline data"
        ],
        "pains": [
          "Manual reporting"
        ],
        "messaging_angle": "Time back every Monday"
      }
    ],
    "competitors": [],
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Senders

A sender is one LinkedIn account. Its owner logs in once through connect_url; that is the only human step besides paying.

Create a sender

POST/api/v1/sendersMCP tool create_sender#

Returns connect_url, a hosted LinkedIn sign-in (the password is never seen by us or your agent). Limits are capped at your plan's safety maximums; warm-up is on by default.

Request
curl -X POST https://pipevoy.com/api/v1/senders -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"Sara Khan","timezone":"America/New_York","work_start":"09:00","work_end":"18:00","booking_url":"https://cal.com/sara/intro"}'
Response
{
  "sender": {
    "id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
    "name": "Sara Khan",
    "status": "pending",
    "connected": false,
    "linkedin_url": null,
    "timezone": "America/New_York",
    "work_start": "09:00",
    "work_end": "18:00",
    "work_days": [
      1,
      2,
      3,
      4,
      5
    ],
    "limits": {
      "daily_invites": 20,
      "daily_messages": 40,
      "weekly_invites": 100
    },
    "warmup_enabled": true,
    "effective_limits": {
      "daily_invites": 10,
      "daily_messages": 20,
      "weekly_invites": 50,
      "warmup_week": 2
    },
    "health": {
      "score": 86,
      "grade": "good",
      "factors": [
        {
          "label": "Acceptance rate",
          "value": "38%",
          "impact": 0
        }
      ],
      "recommendations": []
    },
    "today": {
      "invites": 3,
      "messages": 5
    },
    "booking_url": "https://cal.com/sara/intro",
    "voice_notes": null,
    "last_error": null,
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  },
  "connect_url": "https://account.unipile.com/…",
  "connect_error": null,
  "human_step": "A person must open connect_url once and log in to LinkedIn…"
}

List senders

GET/api/v1/sendersMCP tool list_senders#

Status, configured and effective (warm-up) limits, today's sends and health score.

Request
curl https://pipevoy.com/api/v1/senders -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
      "name": "Sara Khan",
      "status": "connected",
      "connected": true,
      "linkedin_url": null,
      "timezone": "America/New_York",
      "work_start": "09:00",
      "work_end": "18:00",
      "work_days": [
        1,
        2,
        3,
        4,
        5
      ],
      "limits": {
        "daily_invites": 20,
        "daily_messages": 40,
        "weekly_invites": 100
      },
      "warmup_enabled": true,
      "effective_limits": {
        "daily_invites": 10,
        "daily_messages": 20,
        "weekly_invites": 50,
        "warmup_week": 2
      },
      "health": {
        "score": 86,
        "grade": "good",
        "factors": [
          {
            "label": "Acceptance rate",
            "value": "38%",
            "impact": 0
          }
        ],
        "recommendations": []
      },
      "today": {
        "invites": 3,
        "messages": 5
      },
      "booking_url": "https://cal.com/sara/intro",
      "voice_notes": null,
      "last_error": null,
      "created_at": "2026-10-20T09:14:00.000Z",
      "updated_at": "2026-10-20T09:14:00.000Z"
    }
  ],
  "next_cursor": null
}

Get a sender

GET/api/v1/senders/{id}MCP tool get_sender#

Poll this after sending connect_url to a person; status turns connected.

Request
curl https://pipevoy.com/api/v1/senders/5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9 -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "sender": {
    "id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
    "name": "Sara Khan",
    "status": "connected",
    "connected": true,
    "linkedin_url": null,
    "timezone": "America/New_York",
    "work_start": "09:00",
    "work_end": "18:00",
    "work_days": [
      1,
      2,
      3,
      4,
      5
    ],
    "limits": {
      "daily_invites": 20,
      "daily_messages": 40,
      "weekly_invites": 100
    },
    "warmup_enabled": true,
    "effective_limits": {
      "daily_invites": 10,
      "daily_messages": 20,
      "weekly_invites": 50,
      "warmup_week": 2
    },
    "health": {
      "score": 86,
      "grade": "good",
      "factors": [
        {
          "label": "Acceptance rate",
          "value": "38%",
          "impact": 0
        }
      ],
      "recommendations": []
    },
    "today": {
      "invites": 3,
      "messages": 5
    },
    "booking_url": "https://cal.com/sara/intro",
    "voice_notes": null,
    "last_error": null,
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Update hours, limits, warm-up

PATCH/api/v1/senders/{id}MCP tool update_sender#

paused: true stops all sends from the account; paused: false re-checks the connection and resumes. Limits above your plan's safety maximum are refused with limit_above_safety_maximum.

Request
curl -X PATCH https://pipevoy.com/api/v1/senders/5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9 -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"daily_invite_limit":15,"work_days":[1,2,3,4]}'
Response
{
  "sender": {
    "id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
    "name": "Sara Khan",
    "status": "connected",
    "connected": true,
    "linkedin_url": null,
    "timezone": "America/New_York",
    "work_start": "09:00",
    "work_end": "18:00",
    "work_days": [
      1,
      2,
      3,
      4,
      5
    ],
    "limits": {
      "daily_invites": 20,
      "daily_messages": 40,
      "weekly_invites": 100
    },
    "warmup_enabled": true,
    "effective_limits": {
      "daily_invites": 10,
      "daily_messages": 20,
      "weekly_invites": 50,
      "warmup_week": 2
    },
    "health": {
      "score": 86,
      "grade": "good",
      "factors": [
        {
          "label": "Acceptance rate",
          "value": "38%",
          "impact": 0
        }
      ],
      "recommendations": []
    },
    "today": {
      "invites": 3,
      "messages": 5
    },
    "booking_url": "https://cal.com/sara/intro",
    "voice_notes": null,
    "last_error": null,
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Fresh connect link

POST/api/v1/senders/{id}/connectMCP tool get_connect_link#

Links expire. Use this when a person needs a new one, or when an account needs to reconnect.

Request
curl -X POST https://pipevoy.com/api/v1/senders/5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9/connect -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "connect_url": "https://account.unipile.com/…",
  "connect_error": null,
  "human_step": "A person must open connect_url once and log in to LinkedIn…"
}

Prospects

Deduplicated by LinkedIn profile URL. Adding a prospect contacts nobody.

Add prospects

POST/api/v1/prospectsMCP tool add_prospects#

One object, an array, or { prospects: [...] }; up to 500 per call. Existing prospects are updated and blanks never overwrite.

Request
curl -X POST https://pipevoy.com/api/v1/prospects -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: import-2026-10-20" \
  -d '[{"linkedin_url":"https://www.linkedin.com/in/jane-doe","first_name":"Jane","title":"VP Sales","company":"Northwind"}]'
Response
{
  "created": 1,
  "updated": 0,
  "errors": [],
  "data": [
    {
      "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "linkedin_url": "https://www.linkedin.com/in/jane-doe",
      "first_name": "Jane",
      "last_name": "Doe",
      "headline": null,
      "title": "VP Sales",
      "company": "Northwind",
      "company_domain": "northwind.com",
      "email": null,
      "location": "New York",
      "industry": null,
      "account_id": null,
      "source": "api",
      "research": null,
      "created_at": "2026-10-20T09:14:00.000Z",
      "updated_at": "2026-10-20T09:14:00.000Z"
    }
  ]
}

List prospects

GET/api/v1/prospectsMCP tool list_prospects#

Newest first. search= matches name, company, title, headline, email and URL.

Request
curl "https://pipevoy.com/api/v1/prospects?limit=50&search=northwind" -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "linkedin_url": "https://www.linkedin.com/in/jane-doe",
      "first_name": "Jane",
      "last_name": "Doe",
      "headline": null,
      "title": "VP Sales",
      "company": "Northwind",
      "company_domain": "northwind.com",
      "email": null,
      "location": "New York",
      "industry": null,
      "account_id": null,
      "source": "api",
      "research": null,
      "created_at": "2026-10-20T09:14:00.000Z",
      "updated_at": "2026-10-20T09:14:00.000Z"
    }
  ],
  "next_cursor": "eyJ0Ijoi…",
  "total": 128
}

Get a prospect

GET/api/v1/prospects/{id}MCP tool get_prospect#

Request
curl https://pipevoy.com/api/v1/prospects/a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "prospect": {
    "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
    "linkedin_url": "https://www.linkedin.com/in/jane-doe",
    "first_name": "Jane",
    "last_name": "Doe",
    "headline": null,
    "title": "VP Sales",
    "company": "Northwind",
    "company_domain": "northwind.com",
    "email": null,
    "location": "New York",
    "industry": null,
    "account_id": null,
    "source": "api",
    "research": null,
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Campaigns

require_approval defaults to true, so every message waits for an approval your agent can give.

Draft a sequence with AI

POST/api/v1/campaigns/draftMCP tool draft_campaign#

Generates an invite and follow-ups from the company profile. Saves nothing. Optional: tone, language, follow_ups, ab_test, signal_types; or pass your own steps to validate them.

Request
curl -X POST https://pipevoy.com/api/v1/campaigns/draft -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"objective":"Book intro calls with RevOps leaders at Series B SaaS","cta":"a 20-minute call","tone":"direct"}'
Response
{
  "draft": {
    "name": "RevOps leaders, Series B",
    "rationale": "Opens on hiring signals…",
    "steps": [
      {
        "type": "invite",
        "delay_days": 0,
        "guidance": "Short note referencing their recent hiring push. No pitch.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 1,
        "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 4,
        "guidance": "Brief, low-pressure follow-up with one proof point.",
        "template": null
      }
    ],
    "ai": true
  },
  "next": "Review or edit the steps, then POST /api/v1/campaigns…"
}

Create and start a campaign

POST/api/v1/campaignsMCP tool create_campaign#

start defaults to true. If it can't start yet (no connected sender, no company profile, plan limit), it is saved as a draft and activation_error says why.

Request
curl -X POST https://pipevoy.com/api/v1/campaigns -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"RevOps leaders, East Coast","objective":"Book intro calls","cta":"a 20-minute call","steps":[…],"sender_ids":["5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"],"require_approval":true}'
Response
{
  "campaign": {
    "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "name": "RevOps leaders, East Coast",
    "status": "active",
    "kind": "standard",
    "goal": "Book intro calls with RevOps leaders",
    "persona": null,
    "cta": "a 20-minute call",
    "language": "en",
    "require_approval": true,
    "steps": [
      {
        "type": "invite",
        "delay_days": 0,
        "guidance": "Short note referencing their recent hiring push. No pitch.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 1,
        "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 4,
        "guidance": "Brief, low-pressure follow-up with one proof point.",
        "template": null
      }
    ],
    "signal_rules": null,
    "sender_ids": [
      "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"
    ],
    "stats": {
      "enrolled": 42,
      "invited": 30,
      "connected": 12,
      "replied": 4,
      "booked": 1,
      "pending_approval": 3
    },
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  },
  "started": true,
  "activation_error": null,
  "next": "Enroll prospects: POST /api/v1/campaigns/:id/enroll."
}

List campaigns

GET/api/v1/campaignsMCP tool list_campaigns#

Optional status= draft, active, paused or completed.

Request
curl "https://pipevoy.com/api/v1/campaigns?status=active" -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "name": "RevOps leaders, East Coast",
      "status": "active",
      "kind": "standard",
      "goal": "Book intro calls with RevOps leaders",
      "persona": null,
      "cta": "a 20-minute call",
      "language": "en",
      "require_approval": true,
      "steps": [
        {
          "type": "invite",
          "delay_days": 0,
          "guidance": "Short note referencing their recent hiring push. No pitch.",
          "template": null
        },
        {
          "type": "message",
          "delay_days": 1,
          "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
          "template": null
        },
        {
          "type": "message",
          "delay_days": 4,
          "guidance": "Brief, low-pressure follow-up with one proof point.",
          "template": null
        }
      ],
      "signal_rules": null,
      "sender_ids": [
        "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"
      ],
      "stats": {
        "enrolled": 42,
        "invited": 30,
        "connected": 12,
        "replied": 4,
        "booked": 1,
        "pending_approval": 3
      },
      "created_at": "2026-10-20T09:14:00.000Z",
      "updated_at": "2026-10-20T09:14:00.000Z"
    }
  ],
  "next_cursor": null
}

Get a campaign

GET/api/v1/campaigns/{id}MCP tool get_campaign#

Request
curl https://pipevoy.com/api/v1/campaigns/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "campaign": {
    "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "name": "RevOps leaders, East Coast",
    "status": "active",
    "kind": "standard",
    "goal": "Book intro calls with RevOps leaders",
    "persona": null,
    "cta": "a 20-minute call",
    "language": "en",
    "require_approval": true,
    "steps": [
      {
        "type": "invite",
        "delay_days": 0,
        "guidance": "Short note referencing their recent hiring push. No pitch.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 1,
        "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 4,
        "guidance": "Brief, low-pressure follow-up with one proof point.",
        "template": null
      }
    ],
    "signal_rules": null,
    "sender_ids": [
      "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"
    ],
    "stats": {
      "enrolled": 42,
      "invited": 30,
      "connected": 12,
      "replied": 4,
      "booked": 1,
      "pending_approval": 3
    },
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Update a campaign

PATCH/api/v1/campaigns/{id}MCP tool update_campaign#

Name, objective, persona, cta, language, steps, require_approval, sender_ids, signal_rules.

Request
curl -X PATCH https://pipevoy.com/api/v1/campaigns/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"require_approval":false}'
Response
{
  "campaign": {
    "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "name": "RevOps leaders, East Coast",
    "status": "active",
    "kind": "standard",
    "goal": "Book intro calls with RevOps leaders",
    "persona": null,
    "cta": "a 20-minute call",
    "language": "en",
    "require_approval": false,
    "steps": [
      {
        "type": "invite",
        "delay_days": 0,
        "guidance": "Short note referencing their recent hiring push. No pitch.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 1,
        "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 4,
        "guidance": "Brief, low-pressure follow-up with one proof point.",
        "template": null
      }
    ],
    "signal_rules": null,
    "sender_ids": [
      "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"
    ],
    "stats": {
      "enrolled": 42,
      "invited": 30,
      "connected": 12,
      "replied": 4,
      "booked": 1,
      "pending_approval": 3
    },
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Enroll prospects

POST/api/v1/campaigns/{id}/enrollMCP tool enroll_prospects#

By prospect_ids and/or linkedin_urls (unknown URLs are added first). People already in a running campaign, in a conversation or opted out are skipped.

Request
curl -X POST https://pipevoy.com/api/v1/campaigns/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f/enroll -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"linkedin_urls":["https://www.linkedin.com/in/jane-doe"]}'
Response
{
  "enrolled": 1,
  "skipped": 0,
  "created_prospects": 0,
  "invalid_linkedin_urls": []
}

Pause

POST/api/v1/campaigns/{id}/pauseMCP tool pause_campaign#

Nothing more is drafted or sent for it.

Request
curl -X POST https://pipevoy.com/api/v1/campaigns/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f/pause -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "campaign": {
    "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "name": "RevOps leaders, East Coast",
    "status": "paused",
    "kind": "standard",
    "goal": "Book intro calls with RevOps leaders",
    "persona": null,
    "cta": "a 20-minute call",
    "language": "en",
    "require_approval": true,
    "steps": [
      {
        "type": "invite",
        "delay_days": 0,
        "guidance": "Short note referencing their recent hiring push. No pitch.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 1,
        "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 4,
        "guidance": "Brief, low-pressure follow-up with one proof point.",
        "template": null
      }
    ],
    "signal_rules": null,
    "sender_ids": [
      "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"
    ],
    "stats": {
      "enrolled": 42,
      "invited": 30,
      "connected": 12,
      "replied": 4,
      "booked": 1,
      "pending_approval": 3
    },
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Resume or activate

POST/api/v1/campaigns/{id}/resumeMCP tool resume_campaign#

Counts toward the plan's active-campaign limit.

Request
curl -X POST https://pipevoy.com/api/v1/campaigns/c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f/resume -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "campaign": {
    "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "name": "RevOps leaders, East Coast",
    "status": "active",
    "kind": "standard",
    "goal": "Book intro calls with RevOps leaders",
    "persona": null,
    "cta": "a 20-minute call",
    "language": "en",
    "require_approval": true,
    "steps": [
      {
        "type": "invite",
        "delay_days": 0,
        "guidance": "Short note referencing their recent hiring push. No pitch.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 1,
        "guidance": "Thank them for connecting; one line on the problem; ask the CTA.",
        "template": null
      },
      {
        "type": "message",
        "delay_days": 4,
        "guidance": "Brief, low-pressure follow-up with one proof point.",
        "template": null
      }
    ],
    "signal_rules": null,
    "sender_ids": [
      "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9"
    ],
    "stats": {
      "enrolled": 42,
      "invited": 30,
      "connected": 12,
      "replied": 4,
      "booked": 1,
      "pending_approval": 3
    },
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Approvals

Approving through the API is the same as approving in the app. The engine still decides when it is safe to send.

Pending drafts

GET/api/v1/approvalsMCP tool list_pending_approvals#

Oldest first, with the prospect and recent thread. Optional campaign_id=.

Request
curl https://pipevoy.com/api/v1/approvals -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
      "type": "invite",
      "status": "pending_approval",
      "purpose": null,
      "body": "Hi Jane, saw Northwind is hiring three SDRs in Austin. Would be glad to connect.",
      "ai_draft": null,
      "step_index": 0,
      "sender_id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
      "prospect_id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "conversation_id": null,
      "scheduled_for": "2026-10-20T09:14:00.000Z",
      "approved_at": null,
      "sent_at": null,
      "created_at": "2026-10-20T09:14:00.000Z",
      "sender_name": "Sara Khan",
      "campaign_name": "RevOps leaders, East Coast",
      "prospect": {
        "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
        "first_name": "Jane",
        "last_name": "Doe",
        "title": "VP Sales",
        "company": "Northwind",
        "linkedin_url": "https://www.linkedin.com/in/jane-doe"
      },
      "thread": []
    }
  ],
  "next_cursor": null
}

Approve

POST/api/v1/approvals/{id}/approveMCP tool approve_draft#

Optionally send an edited body. will_send is false while billing or SENDING_DISABLED holds sending; the approval stays queued.

Request
curl -X POST https://pipevoy.com/api/v1/approvals/d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a/approve -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"body":"Hi Jane, saw Northwind is hiring in Austin. Glad to connect."}'
Response
{
  "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
  "status": "approved",
  "will_send": true,
  "sending_paused_reason": null,
  "note": "Approved messages are sent by the engine inside the sender's working hours, daily/weekly limits and warm-up, with randomised pacing."
}

Reject

POST/api/v1/approvals/{id}/rejectMCP tool reject_draft#

Never sent. Rejecting a sequence step pauses that prospect's sequence.

Request
curl -X POST https://pipevoy.com/api/v1/approvals/d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a/reject -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
  "status": "rejected"
}

Conversations

Replies are classified automatically and the AI drafts an answer for approval.

List conversations

GET/api/v1/conversationsMCP tool list_conversations#

Most recent activity first. since= (ISO time) and filter= attention, interested or booked.

Request
curl "https://pipevoy.com/api/v1/conversations?since=2026-10-20T00:00:00Z" -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
      "intent": "interested",
      "intent_confidence": 88,
      "needs_attention": true,
      "last_message_at": "2026-10-20T09:14:00.000Z",
      "prospect": {
        "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
        "first_name": "Jane",
        "last_name": "Doe",
        "title": "VP Sales",
        "company": "Northwind",
        "linkedin_url": "https://www.linkedin.com/in/jane-doe"
      },
      "sender": {
        "id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
        "name": "Sara Khan"
      },
      "campaign_name": "RevOps leaders, East Coast",
      "last_message": {
        "direction": "inbound",
        "body": "Sounds interesting, what does it cost?",
        "sent_at": "2026-10-20T09:14:00.000Z"
      },
      "has_pending_draft": true,
      "meeting_at": null
    }
  ],
  "next_cursor": null
}

Get a conversation

GET/api/v1/conversations/{id}MCP tool get_conversation#

All messages, the pending reply draft and queued replies.

Request
curl https://pipevoy.com/api/v1/conversations/7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "conversation": {
    "id": "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
    "intent": "interested",
    "intent_confidence": 88,
    "intent_reason": "Asks about pricing",
    "needs_attention": true,
    "last_message_at": "2026-10-20T09:14:00.000Z",
    "prospect": {
      "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "linkedin_url": "https://www.linkedin.com/in/jane-doe",
      "first_name": "Jane",
      "last_name": "Doe",
      "headline": null,
      "title": "VP Sales",
      "company": "Northwind",
      "company_domain": "northwind.com",
      "email": null,
      "location": "New York",
      "industry": null,
      "account_id": null,
      "source": "api",
      "research": null,
      "created_at": "2026-10-20T09:14:00.000Z",
      "updated_at": "2026-10-20T09:14:00.000Z"
    },
    "sender": {
      "id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
      "name": "Sara Khan"
    },
    "campaign": {
      "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "name": "RevOps leaders, East Coast"
    },
    "messages": [
      {
        "id": "…",
        "direction": "inbound",
        "body": "Sounds interesting, what does it cost?",
        "sent_at": "2026-10-20T09:14:00.000Z"
      }
    ],
    "pending_draft": {
      "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
      "type": "reply",
      "status": "pending_approval",
      "purpose": "reply",
      "body": "Hi Jane, saw Northwind is hiring three SDRs in Austin. Would be glad to connect.",
      "ai_draft": null,
      "step_index": 0,
      "sender_id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
      "prospect_id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "conversation_id": "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
      "scheduled_for": "2026-10-20T09:14:00.000Z",
      "approved_at": null,
      "sent_at": null,
      "created_at": "2026-10-20T09:14:00.000Z"
    },
    "queued": [],
    "meeting": null
  }
}

Write a reply (as a draft)

POST/api/v1/conversations/{id}/replyMCP tool draft_reply#

Never sends directly. Creates a pending draft that replaces any older one; approve it to send.

Request
curl -X POST https://pipevoy.com/api/v1/conversations/7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e/reply -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"body":"Happy to walk you through it. Does Tuesday work?"}'
Response
{
  "draft": {
    "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
    "type": "reply",
    "status": "pending_approval",
    "purpose": "manual",
    "body": "Happy to walk you through it. Does Tuesday work?",
    "ai_draft": null,
    "step_index": 0,
    "sender_id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
    "prospect_id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
    "conversation_id": "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
    "scheduled_for": "2026-10-20T09:14:00.000Z",
    "approved_at": null,
    "sent_at": null,
    "created_at": "2026-10-20T09:14:00.000Z"
  },
  "next": "Approve it with POST /api/v1/approvals/d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a/approve."
}

Meetings

Booked through each sender's booking link.

List meetings

GET/api/v1/meetingsMCP tool list_meetings#

Latest start time first. Optional status=.

Request
curl https://pipevoy.com/api/v1/meetings -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "9d0e1f2a-3b4c-4d5e-af6b-7c8d9e0f1a2b",
      "status": "booked",
      "start_time": "2026-10-23T19:00:00.000Z",
      "end_time": "2026-10-23T19:20:00.000Z",
      "attendee_name": "Jane Doe",
      "attendee_email": "jane@northwind.com",
      "qualified": null,
      "notes": null,
      "prospect": {
        "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
        "first_name": "Jane",
        "last_name": "Doe",
        "title": "VP Sales",
        "company": "Northwind",
        "linkedin_url": "https://www.linkedin.com/in/jane-doe"
      },
      "sender_id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
      "sender_name": "Sara Khan",
      "campaign_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "campaign_name": "RevOps leaders, East Coast",
      "conversation_id": "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
      "rebook": null,
      "rebook_attempts_left": 2,
      "created_at": "2026-10-20T09:14:00.000Z",
      "updated_at": "2026-10-20T09:14:00.000Z"
    }
  ],
  "next_cursor": null
}

Record the outcome

PATCH/api/v1/meetings/{id}MCP tool update_meeting#

status held, no_show or cancelled (or no_show: true), qualified, notes. A no-show drafts a rebook note for approval, at most twice.

Request
curl -X PATCH https://pipevoy.com/api/v1/meetings/9d0e1f2a-3b4c-4d5e-af6b-7c8d9e0f1a2b -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"status":"held","qualified":true,"notes":"Wants a pilot in Q1"}'
Response
{
  "meeting": {
    "id": "9d0e1f2a-3b4c-4d5e-af6b-7c8d9e0f1a2b",
    "status": "held",
    "start_time": "2026-10-23T19:00:00.000Z",
    "end_time": "2026-10-23T19:20:00.000Z",
    "attendee_name": "Jane Doe",
    "attendee_email": "jane@northwind.com",
    "qualified": true,
    "notes": "Wants a pilot in Q1",
    "prospect": {
      "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "first_name": "Jane",
      "last_name": "Doe",
      "title": "VP Sales",
      "company": "Northwind",
      "linkedin_url": "https://www.linkedin.com/in/jane-doe"
    },
    "sender_id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
    "sender_name": "Sara Khan",
    "campaign_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "campaign_name": "RevOps leaders, East Coast",
    "conversation_id": "7b8c9d0e-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
    "rebook": null,
    "rebook_attempts_left": 2,
    "created_at": "2026-10-20T09:14:00.000Z",
    "updated_at": "2026-10-20T09:14:00.000Z"
  }
}

Signals

Same behaviour as the inbound signal webhook: deduplicated, scored and attached to accounts.

Push buying signals

POST/api/v1/signalsMCP tool push_signal#

Up to 100 per call. Each needs text and one of company, domain or person_linkedin_url. Send type and text to store it as-is, or only text to let the AI decide whether it is a signal. Shares the inbound limits: 60 requests an hour and 1,000 signals a day.

Request
curl -X POST https://pipevoy.com/api/v1/signals -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"signals":[{"company":"Northwind","domain":"northwind.com","type":"funding","text":"Northwind raised a $30M Series B","url":"https://northwind.com/news"}]}'
Response
{
  "created": 1,
  "duplicates": 0,
  "errors": []
}

Webhooks

Get events pushed to you instead of polling.

Subscribe

POST/api/v1/webhooksMCP tool create_webhook#

HTTPS URL plus events. The signing secret is returned once.

Request
curl -X POST https://pipevoy.com/api/v1/webhooks -H "Authorization: Bearer $PIPEVOY_API_KEY" -H "Content-Type: application/json" \
  -d '{"url":"https://agent.example.com/pipevoy","events":["draft.created","reply.received","meeting.booked"]}'
Response
{
  "webhook": {
    "id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
    "url": "https://agent.example.com/pipevoy",
    "events": [
      "draft.created",
      "reply.received",
      "meeting.booked"
    ],
    "secret_preview": "whse…3f9a",
    "active": true,
    "last_delivery_at": null,
    "last_error": null,
    "created_at": "2026-10-20T09:14:00.000Z"
  },
  "secret": "whsec_…",
  "note": "Store the secret now; it is not shown again."
}

List subscriptions

GET/api/v1/webhooksMCP tool list_webhooks#

Request
curl https://pipevoy.com/api/v1/webhooks -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "data": [
    {
      "id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
      "url": "https://agent.example.com/pipevoy",
      "events": [
        "draft.created"
      ],
      "secret_preview": "whse…3f9a",
      "active": true,
      "last_delivery_at": "2026-10-20T09:14:00.000Z",
      "last_error": null,
      "created_at": "2026-10-20T09:14:00.000Z"
    }
  ],
  "available_events": [
    "reply.positive",
    "meeting.booked",
    "meeting.no_show",
    "signal.detected",
    "reply.received",
    "draft.created",
    "invite.accepted",
    "sender.restricted"
  ]
}

Delete

DELETE/api/v1/webhooks/{id}MCP tool delete_webhook#

Request
curl -X DELETE https://pipevoy.com/api/v1/webhooks/2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d -H "Authorization: Bearer $PIPEVOY_API_KEY"
Response
{
  "id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "deleted": true
}

Webhook events

Subscribe with POST /api/v1/webhooks or in Settings → Integrations. Each delivery is a JSON POST with x-event-type, x-event-id (use it to ignore duplicates), x-timestamp (unix seconds) and x-signature: sha256=…, the HMAC-SHA256 of <timestamp>.<raw body> with your secret. Reject deliveries more than 5 minutes old. Failed deliveries retry up to five times, in order.

EventWhenExtra fields
draft.createdA draft enters the queue (invite, follow-up, reply, rebook) or is auto-approveddata.draft: id, type, status, body, conversationId, stepIndex, purpose, scheduledFor; data.autoApproved
reply.receivedA prospect repliesdata.conversationId, data.message (latest inbound text)
reply.positiveA reply is classified interested or asking for a meetingdata.intent, data.conversationId, data.message
invite.acceptedA connection invite is accepteddata.senderId, data.prospectId, data.campaignId
meeting.bookedA meeting is booked through the booking linkdata.meeting: id, status, startTime, endTime, attendee, campaignId, qualified
meeting.no_showA meeting is marked as a no-showdata.meeting
signal.detectedNew buying signals are recordeddata.signals[], data.account
sender.restrictedLinkedIn restricts a sender; it pauses automaticallydata.senderId, data.reason, data.pausedActions

Every payload has id, type, workspaceId, createdAt and data, which includes the prospect when there is one.

Sample payload
{
  "id": "e3c1…",
  "type": "draft.created",
  "workspaceId": "6f1d2c3b-8a47-4e15-9b0c-3d2e1f4a5b6c",
  "createdAt": "2026-10-20T09:14:00.000Z",
  "data": {
    "actionId": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
    "type": "invite",
    "autoApproved": false,
    "prospectId": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
    "senderId": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
    "campaignId": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
    "prospect": {
      "id": "a3b4c5d6-e7f8-4a1b-8c2d-3e4f5a6b7c8d",
      "firstName": "Jane",
      "lastName": "Doe",
      "title": "VP Sales",
      "company": "Northwind",
      "linkedinUrl": "https://www.linkedin.com/in/jane-doe"
    },
    "draft": {
      "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
      "type": "invite",
      "status": "pending_approval",
      "body": "Hi Jane, saw Northwind is hiring three SDRs in Austin. Would be glad to connect.",
      "conversationId": null,
      "stepIndex": 0,
      "purpose": null,
      "scheduledFor": "2026-10-20T09:14:00.000Z"
    }
  }
}
Verify the signature
// Node.js: verify a delivery (use the raw body, before JSON parsing)
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, headers, secret) {
  const ts = Number(headers["x-timestamp"]);
  if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false; // replay window
  const expected = "sha256=" + createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  const got = String(headers["x-signature"] ?? "");
  return got.length === expected.length && timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}

Agent as approver: keep require_approval on, react to draft.created (or poll GET /api/v1/approvals), then approve, edit or reject. If the workspace auto-sends replies to interested prospects (a workspace setting), those drafts arrive already approved; data.autoApproved says so.

MCP server

https://pipevoy.com/api/mcp speaks MCP over Streamable HTTP (JSON responses, no session needed) and authenticates with the same API key in an Authorization: Bearer header. It uses API keys, not OAuth. Without a key only signup and signup_status work, so an agent can create its own workspace. A getting_started prompt and resource walk an agent through the sequence above, and a server card is published at https://pipevoy.com/api/mcp/server-card.

Claude Desktop

Add this to claude_desktop_config.json. It uses mcp-remote to pass the header:

claude_desktop_config.json
{
  "mcpServers": {
    "pipevoy": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://pipevoy.com/api/mcp",
               "--header", "Authorization: Bearer ${PIPEVOY_API_KEY}"],
      "env": { "PIPEVOY_API_KEY": "pv_your_key_here" }
    }
  }
}

Claude Code

Terminal
claude mcp add --transport http pipevoy https://pipevoy.com/api/mcp \
  --header "Authorization: Bearer $PIPEVOY_API_KEY"

Claude custom connector

In Claude, add a custom connector with the server URL above. Sending the key needs the connector’s request-header option, which Anthropic currently offers as a beta to some organizations: set header Authorization to Bearer pv_…. Without it, use the Desktop or Claude Code setup.

Any MCP client

Client config
{
  "type": "streamable-http",
  "url": "https://pipevoy.com/api/mcp",
  "headers": { "Authorization": "Bearer pv_your_key_here" }
}

Or call it directly:

tools/call with curl
curl -X POST https://pipevoy.com/api/mcp \
  -H "Authorization: Bearer $PIPEVOY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_workspace","arguments":{}}}'

Tools

Each tool has a JSON Schema and runs the same code as the matching REST endpoint. Tools marked write change something.

ToolKindWhat it does
signupwriteStarts a Pipevoy signup: emails a verification link to the address.
signup_statusreadNo API key needed.
get_workspacereadWorkspace, plan, trial status, limits and usage, whether sending is currently allowed, and a setup checklist with the next step.
get_billingreadPlan, billing status, trial end, current period end, limits and usage.
create_checkout_linkwriteReturns a Stripe Checkout URL for a plan.
get_companyreadThe company profile the AI writes from: summary, value props, proof points, ICP, personas, competitors.
analyze_companywriteReads the company website (a few pages) and builds the company profile with AI.
update_companywriteUpdates the company profile.
list_prospectsreadProspects, newest first.
get_prospectreadOne prospect by id.
add_prospectswriteAdds or updates up to 500 prospects, deduplicated by LinkedIn profile URL (existing prospects are updated; blank fields never overwrite).
list_sendersreadLinkedIn sender accounts with status, configured and effective (warm-up) limits, today's sends and health score with recommendations.
get_senderreadOne sender: connection status (pending until a person logs in to LinkedIn through connect_url), limits, warm-up and health.
create_senderwriteCreates a LinkedIn sender (one LinkedIn account) and returns connect_url, a hosted LinkedIn sign-in link.
update_senderwriteUpdates a sender's working hours, timezone, limits (capped at the plan's safety maximums), warm-up, booking link or voice notes.
get_connect_linkwriteA fresh hosted LinkedIn sign-in link for a sender (links expire).
list_campaignsreadCampaigns, newest first, with status, approval setting, steps and funnel stats.
get_campaignreadOne campaign with its steps, sender ids and stats.
draft_campaignreadGenerates a sequence (invite + follow-ups) with the AI campaign wizard from the company profile.
create_campaignwriteCreates a campaign from steps (e.g.
update_campaignwriteUpdates a campaign's name, goal, persona, CTA, language, steps, require_approval, senders or signal rules.
enroll_prospectswriteEnrolls prospects into a campaign by prospect id and/or LinkedIn URL (unknown URLs are added as prospects first).
pause_campaignwritePauses a campaign: nothing more is drafted or sent for it until it is resumed.
resume_campaignwriteActivates (or resumes) a campaign.
list_pending_approvalsreadDrafts waiting for approval (invites, follow-ups, replies, rebooks), oldest first, with prospect and recent thread context.
approve_draftwriteApproves a pending draft, optionally with an edited body.
reject_draftwriteRejects a draft so it is never sent.
list_conversationsreadConversations with prospects (replies), most recent activity first.
get_conversationreadOne conversation with all messages, the pending reply draft (if any), queued replies and the meeting.
draft_replywriteWrites a reply DRAFT for a conversation.
list_meetingsreadMeetings booked through the booking link, latest start time first, with outcome, qualification, notes and rebook status.
update_meetingwriteRecords a meeting's outcome: status held / no_show / cancelled (or no_show=true), qualified, notes.
push_signalwritePushes up to 100 buying signals (job change, funding, hiring, a post, anything) about a company or person.
list_webhooksreadOutbound webhook subscriptions (url, events, delivery status).
create_webhookwriteSubscribes an HTTPS URL to events (reply.positive, meeting.booked, meeting.no_show, signal.detected, reply.received, draft.created, invite.accepted, sender.restricted).
delete_webhookwriteDeletes a webhook subscription.