Programmatic access to OutreachPilot. Import contacts, trigger campaigns, connect native voice agents, chat with PilotPlus AI, and receive event webhooks. Designed for AI agents and automation platforms.
Master the platform with comprehensive video walkthroughs and step-by-step feature guides.
https://www.useoutreachpilot.com/api/v1All endpoint paths below are relative to this base URL. The legacy root domain redirects to the same Vercel app, but clients should use the www.useoutreachpilot.com API base directly.
API access requires a Pro or Scale plan. Go to Settings → Billing.
Go to Settings → Workspace → Developer API. New keys include the native scope for voice agents.
Send requests with Authorization: Bearer op_live_xxx
POST /api/v1/native/outreachpilot is the recommended endpoint for low-latency conversational clients, wearables, mobile assistants, and MCP-style agents that already parse user intent into structured actions.
Use executionMode: "fast" for live voice turns. Fast mode targets sub-second server work, returns Server-Timing, and skips read-action audit writes unless requested. Message-send requests follow the same Pilot mode as the dashboard: off blocks, review saves a draft, and auto can send after confirmation.
Every write should include an idempotencyKey. Dangerous writes require a two-step confirmation flow: first call returns mode: "confirmation_required" and pending_action_id; the second call sends confirmed: true to save or execute the confirmed action.
Voice, wearable, meeting assistant, and agentic clients can use REST endpoints for deterministic sync, or the Native Voice Bridge for sub-second action turns. FramesOS is one supported client pattern, not a separate API surface. Both paths use the same API key format.
| Need | REST Endpoint | Native Action |
|---|---|---|
| Contacts list | GET /api/v1/contacts | lookup_person |
| Signals | GET /api/v1/signals | recent_signals |
| Activity history | GET /api/v1/activity | lookup_person or reply_inbox |
| Campaigns | GET /api/v1/campaigns | add_to_campaign, pause_campaign, resume_campaign |
| Campaign stats | GET /api/v1/campaigns/:id/status | campaign_stats |
| Notes read/create | GET/POST /api/v1/notes | note_on_contact |
All requests require an API key passed as a Bearer token:
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ https://www.useoutreachpilot.com/api/v1/credits
Keep your API key secret. Do not expose it in client-side code or public repositories.
Keys are hashed with SHA-256 and cannot be recovered. If compromised, revoke immediately and create a new one.
Every response follows a consistent format with machine-readable metadata:
{
"ok": true,
"data": { ... },
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-03-08T...",
"credits_used": 1,
"credits_remaining": 4832
}
}{
"ok": false,
"error": {
"code": "INVALID_API_KEY",
"message": "Human-readable message",
"details": "Diagnostic info...",
"docs_url": "/docs#invalid_api_key"
},
"meta": {
"request_id": "req_abc123",
"timestamp": "2026-03-08T..."
}
}Import, export, and manage CRM contacts.
/api/v1/contactsscope: contacts · freeList contacts with pagination and filtering.
Query Parameters
limit (max 200), offset, status, folder_id, searchResponse
{ ok: true, data: Contact[], meta: { total, has_more, limit, offset } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/contacts?limit=50&search=john"
/api/v1/contactsscope: contacts · freeCreate one or many contacts. Supports upsert.
Request Body
{ email, first_name?, last_name?, company?, title?, linkedin_url?, upsert?: true } or { contacts: [...], upsert?: true }Response
{ ok: true, data: { contacts: Contact[], count: number } }Example (cURL)
curl -X POST -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"john@acme.com","first_name":"John","company":"Acme"}' \
"https://www.useoutreachpilot.com/api/v1/contacts"/api/v1/contacts/:idscope: contacts · freeGet a single contact by ID.
Response
{ ok: true, data: Contact }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/contacts/CONTACT_UUID"
/api/v1/contacts/:idscope: contacts · freeUpdate a contact. Only provided fields are changed.
Request Body
{ first_name?, last_name?, email?, company?, title?, status?, ... }Response
{ ok: true, data: Contact }Example (cURL)
curl -X PUT -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"VP of Sales"}' \
"https://www.useoutreachpilot.com/api/v1/contacts/CONTACT_UUID"/api/v1/contacts/:idscope: contacts · freeDelete a contact permanently.
Response
{ ok: true, data: { deleted: true, id: "..." } }Example (cURL)
curl -X DELETE -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/contacts/CONTACT_UUID"
Manage companies (Accounts). Contacts are automatically linked to these when domains match.
/api/v1/companiesscope: contacts · freeList companies with pagination and filtering.
Query Parameters
limit (max 200), offset, search, domainResponse
{ ok: true, data: Company[], meta: { total, has_more, limit, offset } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/companies?domain=acme.com"
/api/v1/companiesscope: contacts · freeCreate or update companies. Any unrecognized fields are stored in "context".
Request Body
{ name?, domain?, industry?, size?, upsert?: true } or { companies: [...], upsert?: true }Response
{ ok: true, data: { companies: Company[], count: number } }Example (cURL)
curl -X POST -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Acme Corp","domain":"acme.com","industry":"Software"}' \
"https://www.useoutreachpilot.com/api/v1/companies"/api/v1/companies/:idscope: contacts · freeGet a single company by ID.
Response
{ ok: true, data: Company }/api/v1/companies/:idscope: contacts · freeUpdate a company. Only provided fields are changed. Custom properties go into context.
Request Body
{ name?, domain?, industry?, size?, ... }Response
{ ok: true, data: Company }/api/v1/companies/:idscope: contacts · freeDelete a company. Linked contacts will have their account_id set to null automatically.
Response
{ ok: true, data: { deleted: true, id: "..." } }Interact with the PilotPlus AI engine. Full tool access: research, draft messages, manage contacts, trigger campaigns.
/api/v1/chatscope: chat · 1 creditSend a message to PilotPlus AI. Non-streaming JSON response. The AI can execute tools (research, drafting, CRM operations) autonomously.
Request Body
{ message: string, thread_id?: string }Response
{ ok: true, data: { response: string, thread_id: string?, tool_calls: [...], model: string, usage: { input_tokens, output_tokens } } }Example (cURL)
curl -X POST -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"message":"Find me 10 VP-level fintech contacts in NYC"}' \
"https://www.useoutreachpilot.com/api/v1/chat"Low-latency command endpoint for voice agents, glasses, and external assistants that need structured access to OutreachPilot.
/api/v1/native/outreachpilotscope: native or chat · freeExecute a validated OutreachPilot action. Defaults to fast mode: reads skip extra audit writes, send_message follows the shared Pilot mode (off, review, or auto), and responses include Server-Timing plus performance metadata.
Request Body
{
"action": {
"type": "lookup_person",
"personName": "Sarah Chen",
"company": "Notion"
},
"transcript": "Who is Sarah Chen at Notion?",
"idempotencyKey": "voice-turn-01",
"executionMode": "fast",
"auditReadActions": false
}
Supported action.type values:
lookup_person, lookup_company, recent_signals, start_research, enrich_contact,
add_to_campaign, remove_from_campaign, pause_campaign, resume_campaign,
draft_message, send_message, campaign_stats, reply_inbox, book_meeting,
mark_status, note_on_contact, undo_last.
Irreversible writes return mode="confirmation_required" unless confirmed=true:
send_message, add_to_campaign, remove_from_campaign, pause_campaign,
resume_campaign, book_meeting, and mark_status when status="disqualified".Response
{
"ok": true,
"data": {
"mode": "result | confirmation_required | queued | duplicate",
"spoken_summary": "Sarah Chen is VP of Engineering at Notion. Status is qualified.",
"data": { ... },
"ids": { "contact_id": "..." },
"pending_action_id": "...",
"idempotency_key": "voice-turn-01",
"audit_id": "...",
"performance": {
"execution_mode": "fast",
"server_elapsed_ms": 42,
"target_ms": 750,
"within_target": true
}
},
"meta": { "request_id": "req_abc123", "server_elapsed_ms": 42 }
}Example (cURL)
curl -X POST -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"action":{"type":"lookup_person","personName":"Sarah Chen","company":"Notion"},"transcript":"Who is Sarah Chen at Notion?","executionMode":"fast"}' \
"https://www.useoutreachpilot.com/api/v1/native/outreachpilot"/api/v1/native/outreachpilotscope: native or chat · freeConfirm a pending native action. Use the pending_action_id returned by the first call and send confirmed=true. Reuse the same idempotency key to prevent duplicate writes.
Request Body
{
"pendingActionId": "PENDING_ACTION_UUID",
"confirmed": true,
"idempotencyKey": "voice-turn-02",
"transcript": "Yes, confirm it.",
"action": {
"type": "add_to_campaign",
"contactRef": "Sarah Chen",
"campaignRef": "Seed founders"
}
}Response
{
"ok": true,
"data": {
"mode": "queued | result | duplicate",
"spoken_summary": "Sarah Chen is now in Seed founders.",
"ids": {
"contact_id": "...",
"campaign_id": "...",
"audience_id": "..."
},
"idempotency_key": "native:add_to_campaign:..."
}
}Check your credit balance and plan information.
/api/v1/creditsscope: credits · freeGet current credit balance, plan tier, and reset date.
Response
{ ok: true, data: { credits_remaining, credits_monthly, plan_tier, credits_reset_at } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/credits"
Read buying signals, interaction history, and notes for briefing/debrief workflows.
/api/v1/signalsscope: contacts or native · freeList recent buying signals. Use this for briefing cards and "what happened since last time" summaries.
Query Parameters
limit (max 100), offset, since, platform, status, contact_id, company, min_intent_scoreResponse
{ ok: true, data: Signal[], meta: { total, has_more, limit, offset } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/signals?since=2026-05-01T00:00:00Z&limit=25"
/api/v1/activityscope: contacts or native · freeList interaction history across contacts and campaigns. Notes are included by default.
Query Parameters
limit (max 100), offset, contact_id, campaign_id, direction, channel, since, include_notes=falseResponse
{ ok: true, data: Interaction[], meta: { total, has_more, limit, offset } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/activity?contact_id=CONTACT_UUID&limit=20"
/api/v1/notesscope: contacts or native · freeRead contact notes. Notes are stored as interactions with channel="note".
Query Parameters
limit (max 100), offset, contact_idResponse
{ ok: true, data: Note[], meta: { total, has_more, limit, offset } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/notes?contact_id=CONTACT_UUID"
/api/v1/notesscope: contacts or native · freeCreate a contact note from a debrief.
Request Body
{ contact_id: string, note: string, idempotency_key?: string }Response
{ ok: true, data: { id, contact_id, content, metadata, created_at } }Example (cURL)
curl -X POST -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"contact_id":"CONTACT_UUID","note":"Met after demo. Follow up Friday.","idempotency_key":"debrief-123"}' \
"https://www.useoutreachpilot.com/api/v1/notes"List, inspect, update, trigger, and check campaign status.
/api/v1/campaignsscope: campaigns · freeList campaigns with pagination and filters.
Query Parameters
limit (max 200), offset, status, typeResponse
{ ok: true, data: Campaign[], meta: { total, has_more, limit, offset } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/campaigns?status=active&limit=25"
/api/v1/campaigns/:idscope: campaigns · freeGet a single campaign including audience_stats.
Response
{ ok: true, data: Campaign & { audience_stats: { total, active, completed, paused, bounced } } }/api/v1/campaigns/:idscope: campaigns · freeUpdate campaign fields such as name, status, type, config, steps, or campaign_mode.
Request Body
{ name?, status?, type?, config?, steps?, campaign_mode? }Response
{ ok: true, data: Campaign }/api/v1/campaigns/:id/triggerscope: campaigns · freeAdd a contact to a campaign. Provide contact_id or contact fields (auto-creates if new).
Request Body
{ contact_id: string } or { email, first_name?, last_name?, company? }Response
{ ok: true, data: { triggered: true, campaign_id, contact_id } }Example (cURL)
curl -X POST -H "Authorization: Bearer op_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"john@acme.com","first_name":"John"}' \
"https://www.useoutreachpilot.com/api/v1/campaigns/CAMPAIGN_UUID/trigger"/api/v1/campaigns/:id/statusscope: campaigns · freeGet campaign status with audience breakdown.
Response
{ ok: true, data: { campaign_id, name, status, audience: { total, active, completed, paused, bounced, failed } } }Example (cURL)
curl -H "Authorization: Bearer op_live_YOUR_KEY" \ "https://www.useoutreachpilot.com/api/v1/campaigns/CAMPAIGN_UUID/status"
Subscribe to real-time event notifications.
/api/v1/webhooksscope: campaigns · freeList active webhook subscriptions.
Response
{ ok: true, data: { webhooks: [...], available_events: [...] } }/api/v1/webhooksscope: campaigns · freeRegister a webhook URL for events.
Request Body
{ url: "https://...", events: ["email.opened", "campaign.completed"], secret?: "hmac_secret" }Response
{ ok: true, data: WebhookSubscription }/api/v1/webhooks?id=xxxscope: campaigns · freeDeactivate a webhook subscription.
Response
{ ok: true, data: { deleted: true } }All errors include a machine-readable code, message, and details for diagnostics.
| Code | HTTP | When |
|---|---|---|
| MISSING_AUTH | 401 | No Authorization header provided |
| INVALID_API_KEY | 401 | Key not found, revoked, or malformed |
| PLAN_REQUIRED | 403 | Organization not on Pro or Scale plan |
| SCOPE_DENIED | 403 | API key missing required scope for this endpoint |
| ORG_NOT_FOUND | 404 | Organization deleted or invalid |
| RESOURCE_NOT_FOUND | 404 | Contact, campaign, or webhook not found |
| VALIDATION_ERROR | 422 | Invalid request body or missing required fields |
| DUPLICATE_RESOURCE | 409 | Resource already exists (e.g. contact email) |
| INSUFFICIENT_CREDITS | 402 | Not enough credits for the operation |
| CAMPAIGN_NOT_ACTIVE | 400 | Campaign is draft or paused |
| INTERNAL_ERROR | 500 | Unexpected server error |
Public API keys are limited to 60 requests per minute per organization. Rate-limited responses return HTTP 429 with X-RateLimit-Remaining and X-RateLimit-Reset headers.
List endpoints use offset pagination with limit, offset, and response metadata: total, has_more, limit, and offset. Contacts, companies, and campaigns allow up to 200 rows per page; signals, activity, and notes allow up to 100.
Credit usage is separate from rate limits. Each operation has a credit cost (most are free). AI Chat costs credits per message depending on the model.
When your credits run out, API calls that require credits will return INSUFFICIENT_CREDITS (402). Check your balance anytime at GET /api/v1/credits.
Credits reset monthly on your billing date. Monitor usage in Settings → Usage.