Documentation

Developer

API Reference

Add leads, manage companies, and create deals programmatically from any server or integration platform.

Developer

API Reference

Use the OutboundHQ API to add leads, manage companies, and create deals from your own server, Zapier, Make, or any HTTP client.

Base URL

https://api.outboundhq.ca/api/v1

Authentication

All CRM endpoints require an API key passed in the x-api-key header. Generate one from Dashboard → Team → API Keys.

curl https://api.outboundhq.ca/api/v1/{teamId}/lead-groups \
  -H "x-api-key: ohq_your_key_here"
Your Team ID appears in the dashboard URL: app.outboundhq.ca/team/{teamId}/dashboard
Auth

API Keys

API keys are scoped to a single team. The secret is shown once at creation — store it immediately.

Create a key

Requires a logged-in Dashboard session (Firebase auth). Cannot be called with an API key.

POST /{teamId}/api-keys
Authorization: Bearer <firebase-token>
Content-Type: application/json

{ "name": "Zapier Integration" }

// Response 201
{
  "key_id": "key_1715000000000_abc12345",
  "name": "Zapier Integration",
  "secret": "ohq_64hexcharactershere...",
  "status": "active",
  "createdAt": "2026-05-14T12:00:00.000Z"
}

List keys

Returns all keys for the team. The secret is never included.

GET /{teamId}/api-keys
Authorization: Bearer <firebase-token>

// Response 200
{
  "api_keys": [
    {
      "key_id": "key_...",
      "name": "Zapier Integration",
      "status": "active",
      "last_used_at": "2026-05-14T13:00:00.000Z",
      "createdAt": "2026-05-14T12:00:00.000Z"
    }
  ]
}

Revoke a key

DELETE /{teamId}/api-keys/{keyId}
Authorization: Bearer <firebase-token>

// Response 204 No Content
CRM

Leads & Lead Groups

Leads are organised into groups (lists). Every lead belongs to at least one group.

List groups

GET /{teamId}/lead-groups
x-api-key: ohq_your_key

// Response 200
{ "groups": [{ "group_id": "grp_...", "name": "Website Signups", "lead_count": 42 }] }

Add a lead to a group

Prefer the dashboard? Open Leads → select a group → + Add to fill in the same fields through the built-in intake form — supports first/last name, email, phone (E.164, multiple entries with type), company (with autocomplete), job title, city, contact owner, and any custom fields your team has defined. No API key needed.
FieldTypeRequiredNotes
emailstringyes
firstNamestringno
lastNamestringno
businessNamestringnoCompany name
phonesarrayno[{ "type": "cell", "number": "+1..." }]
jobTitlestringno
notesstringno
customFieldsobjectno{ "key": "value" }
POST /{teamId}/lead-groups/{groupId}/leads
x-api-key: ohq_your_key
Content-Type: application/json

{
  "email": "jane@example.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "businessName": "Acme Corp"
}

// Response 201 — Lead object

Bulk import (up to 5,000 leads)

Duplicates matched by email within the team are skipped. This endpoint runs on the long-running handler (timeout: 120 s).

POST https://api.outboundhq.ca/api/v1/{teamId}/lead-groups/{groupId}/leads/import
x-api-key: ohq_your_key
Content-Type: application/json

{
  "leads": [
    { "email": "a@example.com", "firstName": "Alice" },
    { "email": "b@example.com", "firstName": "Bob" }
  ]
}

// Response 200
{ "imported": 2, "skipped": 0 }

Update / delete

// Update
PUT /{teamId}/lead-groups/{groupId}/leads/{leadId}
x-api-key: ohq_your_key
Content-Type: application/json
{ "jobTitle": "VP Sales" }

// Remove from group (deletes lead if no other group)
DELETE /{teamId}/lead-groups/{groupId}/leads/{leadId}

// Bulk remove
DELETE /{teamId}/lead-groups/{groupId}/leads
{ "lead_ids": ["lead_aaa", "lead_bbb"] }
// Response: { "deleted": 2 }
CRM

Companies

Organise leads and deals under a company record.

List companies

GET /{teamId}/companies?limit=25&cursor=...&search=acme
x-api-key: ohq_your_key

// Response 200
{
  "companies": [...],
  "nextCursor": "base64cursor",
  "hasMore": true,
  "total": 150
}

Create / update a company

FieldTypeRequiredNotes
namestringyes
industrystringno
websitestringno
citystringno
countrystringno
employee_countnumberno
notesstringno
// Create
POST /{teamId}/companies
x-api-key: ohq_your_key
{ "name": "Acme Corp", "website": "https://acme.com" }

// Update
PUT /{teamId}/companies/{companyId}
x-api-key: ohq_your_key
{ "industry": "SaaS" }

// Delete (nullifies company_id on leads/deals — does not delete them)
DELETE /{teamId}/companies/{companyId}

Bulk import

POST /{teamId}/companies/bulk
x-api-key: ohq_your_key
{ "companies": [{ "name": "Acme Corp" }, { "name": "Beta Inc" }] }

// Response: { "imported": 2, "skipped": 0 }
CRM

Deals

Track opportunities through a configurable pipeline.

List deals

GET /{teamId}/deals
x-api-key: ohq_your_key

// Response 200
{
  "deals": [...],
  "pipeline": {
    "stages": [{ "id": "stage_prospecting", "name": "Prospecting", "order": 0 }]
  }
}

Create / update a deal

FieldTypeRequiredNotes
titlestringyes
company_idstringno
contact_lead_idstringno
valuenumberno
currencystringnoDefault: USD
stagestringnoe.g. stage_prospecting
close_datestringnoISO 8601 date
// Create
POST /{teamId}/deals
x-api-key: ohq_your_key
{ "title": "Acme Corp — Enterprise", "value": 12000, "stage": "stage_prospecting" }

// Update (move stage, change value, etc.)
PUT /{teamId}/deals/{dealId}
x-api-key: ohq_your_key
{ "stage": "stage_qualified" }

Default pipeline stages

stage_prospectingstage_qualifiedstage_proposalstage_negotiationstage_closed_wonstage_closed_lost

Customise stages via PUT /{teamId}/deal-pipeline.

Outreach

Campaigns

Create and manage outreach campaigns — including drip email/SMS sequences and social post campaigns — programmatically.

List / get campaigns

// List all campaigns (paginated)
GET /{teamId}/campaigns?limit=20&lastEvaluatedKey=...
x-api-key: ohq_your_key

// Response 200
{
  "campaigns": [
    {
      "campaign_id": "cmp_...",
      "name": "Q2 Outreach",
      "status": "active",
      "drip_enabled": true,
      "stats": { "totalLeads": 120 }
    }
  ],
  "hasMore": true,
  "lastEvaluatedKey": "base64cursor"
}

// Get a single campaign
GET /{teamId}/campaigns/{campaignId}
x-api-key: ohq_your_key

// Response 200 — full campaign object

Create / update / delete

FieldTypeRequiredNotes
namestringyes
drip_enabledbooleannotrue for drip email/SMS sequences
seed_lead_group_idstringnoLead group ID to attach on creation
seed_lead_idsarraynoIndividual lead IDs to enroll on creation
existing_lead_group_idstringnoUse an existing group as the campaign lead group
connectionsarraynoConnected account IDs for sending
// Create
POST /{teamId}/campaigns
x-api-key: ohq_your_key
Content-Type: application/json
{ "name": "Q2 Outreach", "drip_enabled": true }

// Response 201
{
  "campaign_id": "cmp_...",
  "name": "Q2 Outreach",
  "status": "draft",
  "drip_enabled": true,
  "stats": { "totalLeads": 0 }
}

// Update
PUT /{teamId}/campaigns/{campaignId}
x-api-key: ohq_your_key
{ "name": "Q2 Outreach — Revised" }

// Delete
DELETE /{teamId}/campaigns/{campaignId}
// Response 204 No Content

Duplicate a campaign

Creates a new campaign in draft status with all events copied and stats reset. Useful for reusing a sequence template.

POST /{teamId}/campaigns/{campaignId}/duplicate
x-api-key: ohq_your_key
Content-Type: application/json
{ "name": "Q3 Outreach (copy)" }

// Response 201 — new campaign object with status "draft" and zeroed stats

Start / pause / resume

A campaign must be in draft or ready status to start. Starting a drip campaign immediately enrolls all leads in the linked groups and begins sending scheduled steps.

// Start — promotes approved/draft events to pending, sets status → active
POST /{teamId}/campaigns/{campaignId}/start
x-api-key: ohq_your_key
// Response 200
{ "success": true, "message": "Campaign started successfully", "status": "active" }

// Pause — halts sending; campaign must be active
POST /{teamId}/campaigns/{campaignId}/pause
x-api-key: ohq_your_key
// Response 200
{ "success": true, "status": "paused" }

// Resume — resumes from where it left off; campaign must be paused
POST /{teamId}/campaigns/{campaignId}/resume
x-api-key: ohq_your_key
// Response 200
{ "success": true, "status": "active" }
draftreadyactivepausedgenerating

Campaign leads

Leads within a campaign are separate from CRM lead groups — they carry campaign-specific fields like status and source.

FieldTypeRequiredNotes
emailstringnoRecommended for outreach campaigns
businessNamestringno
firstNamestringno
lastNamestringno
phonesarrayno[{ "type": "cell", "number": "+1..." }]
websiteUrlstringno
industrystringno
sourcestringnoe.g. manual, import
statusstringnonew | contacted | interested | qualified | rejected
// Add a single lead
POST /{teamId}/campaigns/{campaignId}/leads
x-api-key: ohq_your_key
Content-Type: application/json
{
  "email": "jane@acme.com",
  "firstName": "Jane",
  "businessName": "Acme Corp"
}
// Response 201 — lead object

// List leads (paginated)
GET /{teamId}/campaigns/{campaignId}/leads?limit=50&status=new&lastEvaluatedKey=...
x-api-key: ohq_your_key
// Response 200
{ "leads": [...], "hasMore": true }

// Bulk import (up to 5,000)
POST /{teamId}/campaigns/{campaignId}/import-leads
x-api-key: ohq_your_key
{
  "leads": [
    { "email": "a@example.com", "firstName": "Alice" },
    { "email": "b@example.com", "firstName": "Bob" }
  ]
}
// Response 200
{ "success": true, "imported": 2, "skipped": 0, "errors": [] }

// Update a lead
PUT /{teamId}/campaigns/{campaignId}/leads/{leadId}
x-api-key: ohq_your_key
{ "status": "interested" }

// Delete a lead
DELETE /{teamId}/campaigns/{campaignId}/leads/{leadId}
// Response 204 No Content

Campaign events

Each step in a campaign is an event — an outreach message or social post scheduled for a specific time (or, for drip campaigns, a day offset). Events have a status lifecycle of their own.

FieldTypeRequiredNotes
event_dataobjectyesoutreach or social_media payload (see below)
scheduledForstringnoISO 8601 timestamp — required for non-drip events
is_drip_templatebooleannotrue for drip sequence steps
delay_daysnumbernoDays after enrollment to send (drip only); must be unique per campaign
statusstringnodraft | review | pending | approved | rejected
// List events for a campaign
GET /{teamId}/campaigns/{campaignId}/events
x-api-key: ohq_your_key
// Response 200
{ "events": [...] }

// Create an outreach event (scheduled)
POST /{teamId}/campaigns/{campaignId}/events
x-api-key: ohq_your_key
Content-Type: application/json
{
  "scheduledFor": "2026-06-10T10:00:00Z",
  "event_data": {
    "outreach": {
      "channel": "email",
      "from_email": "you@yourco.com",
      "subject": "Quick intro, {{firstName}}",
      "body": "Hi {{firstName}}, I wanted to reach out..."
    }
  }
}

// Update an event (e.g. reschedule or edit content)
PUT /{teamId}/campaigns/{campaignId}/events/{eventId}
x-api-key: ohq_your_key
{ "scheduledFor": "2026-06-11T10:00:00Z" }

// Delete an event
DELETE /{teamId}/campaigns/{campaignId}/events/{eventId}
// Response 204 No Content
draftreviewpendingapprovedrejectedpostedcompletedfailed
Outreach events must be scheduled at least 24 hours apart. Drip template events must have unique delay_days values within the same campaign.

Attachments

Email outreach events and campaigns support file attachments (PDF, images, Office documents, etc.). The aggregate size of all attachments plus the email body must be under 10 MB. Executable file types are blocked. Uploading follows a two-step presigned-URL pattern: request an upload URL → PUT the file to S3 → register the s3_key with the backend.

Per-event attachments

// Step 1 — request a presigned upload URL
POST /{teamId}/campaigns/{campaignId}/events/{eventId}/attachments/upload
x-api-key: ohq_your_key
Content-Type: application/json
{ "fileName": "brochure.pdf", "contentType": "application/pdf", "size": 524288 }

// Response 200
{
  "presignedUrl": "https://s3.amazonaws.com/...",
  "s3_key": "attachments/team_.../brochure-uuid.pdf"
}

// Step 2 — upload the file directly to S3
PUT <presignedUrl>
Content-Type: application/pdf
<file bytes>

// Step 3 — register the uploaded file on the event
POST /{teamId}/campaigns/{campaignId}/events/{eventId}/attachments
x-api-key: ohq_your_key
{ "s3_key": "attachments/...", "fileName": "brochure.pdf", "contentType": "application/pdf", "size": 524288 }

// Response 200
{ "success": true, "attachments": [{ "s3_key": "...", "fileName": "brochure.pdf", "contentType": "application/pdf", "size": 524288, "signedUrl": "https://..." }] }

// Delete an attachment by index
DELETE /{teamId}/campaigns/{campaignId}/events/{eventId}/attachments/{index}
x-api-key: ohq_your_key
// Response 200 — updated attachments array

Campaign-level attachments

Campaign-level attachments are appended to every email step in the campaign.

// Step 1 — presigned upload URL
POST /{teamId}/campaigns/{campaignId}/attachments/upload
x-api-key: ohq_your_key
{ "fileName": "one-pager.pdf", "contentType": "application/pdf", "size": 204800 }

// Step 2 — PUT file to S3 (same as above)

// Step 3 — register
POST /{teamId}/campaigns/{campaignId}/attachments
x-api-key: ohq_your_key
{ "s3_key": "...", "fileName": "one-pager.pdf", "contentType": "application/pdf", "size": 204800 }

// Delete by index
DELETE /{teamId}/campaigns/{campaignId}/attachments/{index}
x-api-key: ohq_your_key
signedUrl values returned by GET endpoints expire after 1 hour and are regenerated fresh on every request — never store them; always re-fetch the campaign or event to get a valid URL.

Drip sequences (manual setup)

A drip sequence is built from events with is_drip_template: true and a delay_days offset. Leads enrolled after the campaign starts automatically receive each step at the right interval. Supports {{firstName}}, {{lastName}}, {{businessName}}, and {{email}} template variables.

Typical flow: Create campaign (drip_enabled: true, seed_lead_group_id: "grp_...") → add drip-template events → start campaign. The campaign auto-creates a single lead group; new leads added to that group are automatically enrolled.
// 1. Create the campaign
POST /{teamId}/campaigns
x-api-key: ohq_your_key
{ "name": "Q2 Drip", "drip_enabled": true }

// 2. Add step — Day 0 intro email
POST /{teamId}/campaigns/{campaignId}/events
x-api-key: ohq_your_key
{
  "is_drip_template": true,
  "delay_days": 0,
  "event_data": {
    "outreach": {
      "channel": "email",
      "from_email": "you@yourco.com",
      "subject": "Quick intro, {{firstName}}",
      "body": "Hi {{firstName}},\n\nI wanted to reach out about {{businessName}}..."
    }
  }
}

// 3. Add step — Day 3 follow-up email
POST /{teamId}/campaigns/{campaignId}/events
x-api-key: ohq_your_key
{
  "is_drip_template": true,
  "delay_days": 3,
  "event_data": {
    "outreach": {
      "channel": "email",
      "from_email": "you@yourco.com",
      "subject": "Following up, {{firstName}}",
      "body": "Just checking in — did my last note reach you?"
    }
  }
}

// 4. Add step — Day 7 SMS nudge
POST /{teamId}/campaigns/{campaignId}/events
x-api-key: ohq_your_key
{
  "is_drip_template": true,
  "delay_days": 7,
  "event_data": {
    "outreach": {
      "channel": "sms",
      "from_phone": "+15550001234",
      "body": "Hi {{firstName}}, happy to chat about {{businessName}} — just reply here."
    }
  }
}

// 5. Start — lead group was already set at creation
POST /{teamId}/campaigns/{campaignId}/start
x-api-key: ohq_your_key

AI generation

Skip manual event creation and let OutboundHQ's AI write and schedule your entire sequence or social content plan in one call.

Generate an outreach sequence

Pass a prompt describing your goal and OutboundHQ creates all drip steps (subject lines, body copy, timing) automatically.

FieldTypeRequiredNotes
contentPromptstringyesDescribe the campaign goal and tone
channelsarrayyes["email"] or ["email","sms"]
frequencyPromptstringnoe.g. "3 emails over 2 weeks, then weekly"
fromEmailstringno
fromPhonestringno
isDripCampaignbooleannoCreates drip-template events when true
useHtmlEmailbooleannoGenerates rich HTML emails (async)
POST /{teamId}/campaigns/{campaignId}/generate-outreach-sequence
x-api-key: ohq_your_key
Content-Type: application/json
{
  "contentPrompt": "Introduce our SaaS product to SMB owners, highlight ROI, and invite a demo",
  "frequencyPrompt": "3 emails — day 0, day 4, day 10 — then an SMS on day 14",
  "channels": ["email", "sms"],
  "fromEmail": "you@yourco.com",
  "fromPhone": "+15550001234",
  "isDripCampaign": true
}

// Response 200 (plaintext, sync)
{ "success": true, "sequenceSteps": 4, "eventsCreated": 4 }

// Response 200 (HTML email, async — events created in background)
{ "success": true, "sequenceSteps": 4, "async": true }

Generate social media posts

Generates a full content plan and queues individual post-writing tasks. The campaign status changes to generating while tasks run; posts appear as events when ready.

FieldTypeRequiredNotes
promptstringyesTopic, tone, and goal for the content
numberOfPostsnumberyes
platformsarrayyes["instagram","tiktok","reddit", ...]
presetsobjectnoReddit: [{ "subreddit": "...", "kind": "self" }]
brandingAssetIdsarraynoBrand asset IDs to include
POST /{teamId}/campaigns/{campaignId}/generate-social-posts
x-api-key: ohq_your_key
Content-Type: application/json
{
  "prompt": "Promote our new feature launch with engaging, conversational posts",
  "numberOfPosts": 6,
  "platforms": ["instagram", "tiktok"],
  "brandingAssetIds": ["asset_abc"]
}

// Response 200
{
  "success": true,
  "message": "Post generation in progress",
  "tasksQueued": 6,
  "tasksFailed": 0,
  "campaignId": "cmp_...",
  "status": "generating"
}
Poll GET /{teamId}/campaigns/{campaignId} until status leaves generating, then fetch events to see the generated posts.
Public

Contact Form Intake

A public endpoint — no API key required. Accepts HTML form submissions and creates leads automatically. Configure field mapping and auto-reply in Inbox → Auto-Reply → Contact Form.

Intake endpoint

POST https://api.outboundhq.ca/api/v1/intake/{teamId}/contact
Content-Type: application/json   (or application/x-www-form-urlencoded)

{
  "email": "jane@example.com",
  "firstName": "Jane",
  "message": "I'd love a demo."
}

// Response 200
{ "success": true }
Include a hidden _hp field as a honeypot — submissions where it's filled are silently dropped as bot traffic.

HTML form example

<form action="https://api.outboundhq.ca/api/v1/intake/YOUR_TEAM_ID/contact" method="POST">
  <input type="text"  name="firstName" placeholder="First name">
  <input type="email" name="email"     placeholder="Email" required>
  <textarea           name="message"   placeholder="Message"></textarea>
  <input type="text"  name="_hp" style="display:none" tabindex="-1" autocomplete="off">
  <button type="submit">Send</button>
</form>

Auto-reply emails support template variables: {{firstName}}, {{email}}, {{company}}.

AI Assistants

MCP — Use OutboundHQ from any AI assistant

The OutboundHQ MCP server lets AI assistants like Claude and Cursor add leads, create deals, and manage companies on your behalf — just by asking in plain English.

What is MCP?

The Model Context Protocol is an open standard that lets AI assistants call external tools. You run a small server process; the AI talks to it over stdin/stdout and decides which tools to call based on what you ask. No copy-pasting, no manual API calls.

You: "Add john@acme.com to the Inbound leads group"
↓ AI picks tool
MCP server: add_lead_to_group(group_id, email, ...)
↓ HTTP call
OutboundHQ API: 201 Created ✓

Install & configure

The MCP server is published on npm as outboundhq-mcp. You need Node.js 18+ and an API key — no cloning required.

1

Go to Dashboard → Team → API Keys, create a key, and copy the secret.

2

Find your Team ID in the dashboard URL: /team/YOUR_TEAM_ID/dashboard

3

Pick your assistant below and paste in your key and team ID — npx will download and run the server automatically.

Claude Code

Browser login Recommended

One command — a browser opens, you log in, and Claude Code is connected. No API key or team ID needed.

claude mcp add outboundhq -- npx mcp-remote https://api.outboundhq.ca/mcp
After authorizing, try: "List my lead groups" — Claude will call the tool automatically. Manage or disconnect sessions from Dashboard → Team → AI Connections.

API key (manual)

If you prefer to use an API key directly, run this instead:

claude mcp add outboundhq \
  -e OUTBOUNDHQ_API_KEY=ohq_your_secret_here \
  -e OUTBOUNDHQ_TEAM_ID=your_team_id_here \
  -- npx outboundhq-mcp

Claude Desktop

Open claude_desktop_config.json — on Mac it's at ~/Library/Application Support/Claude/, on Windows %APPDATA%\Claude\ — and add:

{
  "mcpServers": {
    "outboundhq": {
      "command": "npx",
      "args": ["outboundhq-mcp"],
      "env": {
        "OUTBOUNDHQ_API_KEY": "ohq_your_secret_here",
        "OUTBOUNDHQ_TEAM_ID": "your_team_id_here"
      }
    }
  }
}

Restart Claude Desktop. You'll see "outboundhq" appear in the MCP tools list.

Cursor

Add to ~/.cursor/mcp.json (create it if it doesn't exist):

{
  "mcpServers": {
    "outboundhq": {
      "command": "npx",
      "args": ["outboundhq-mcp"],
      "env": {
        "OUTBOUNDHQ_API_KEY": "ohq_your_secret_here",
        "OUTBOUNDHQ_TEAM_ID": "your_team_id_here"
      }
    }
  }
}

Available tools

Once connected, you can ask naturally. The AI picks the right tool:

ToolWhat it doesExample prompt
list_lead_groupsList all groups with IDs and counts"What lead groups do I have?"
add_lead_to_groupAdd one lead to a group"Add sarah@acme.com to the Inbound group"
import_leads_to_groupBulk-import up to 200 leads per call"Import these 20 leads into Webinar Signups"
list_companiesList companies with optional search"Show me all companies in the SaaS industry"
create_companyCreate a new company record"Create a company called Acme Corp"
list_dealsList deals, optionally filtered by stage"What deals are in Prospecting?"
create_dealCreate a new deal"Create a deal — Acme Corp Enterprise, $50k, Prospecting"
update_dealUpdate fields on an existing deal"Move the Acme deal to Qualified"
list_campaignsList all campaigns"Show me my active campaigns"
create_campaignCreate a new campaign"Create a drip campaign called Q3 Outreach"
update_campaignRename or attach lead groups"Attach the Inbound group to my Q3 campaign"
start_campaignStart a draft or ready campaign"Start the Q3 Outreach campaign"
pause_campaignPause an active campaign"Pause Q3 Outreach"
resume_campaignResume a paused campaign"Resume Q3 Outreach"
add_campaign_recipientAdd a recipient directly to a campaign"Add john@acme.com to Q3 Outreach"
import_campaign_recipientsBulk-import up to 200 campaign recipients per call"Import these leads into Q3 Outreach"
add_drip_stepAdd a timed email or SMS drip step"Add a day-3 follow-up email to Q3 Outreach"
list_social_accountsList connected social accounts with accountIds"What social accounts do I have connected?"
connect_social_accountGet an OAuth URL to connect a social platform"Give me the link to connect my Reddit account"
create_campaign_postCreate a social media post in a campaign"Post to r/SaaS about our new feature launch"
list_assetsList asset summaries and processing status"Which uploaded assets are ready?"
get_asset_upload_urlCreate a signed direct-to-storage upload"Give me a signed upload URL for interview.mp4"
inspect_assetPage or search an asset's transcript and scene metadata"Find scenes mentioning the product launch"

Claude skill instructions

Want Claude to manage your CRM directly from the terminal? Create a slash command by saving the text below as .claude/commands/crm.md in your project. Once saved, type /crm in Claude Code and describe what you want — Claude will authenticate with your API key, resolve group IDs, confirm before bulk actions, and make the API calls for you directly.

# .claude/commands/crm.md
# Save this file, then use /crm in Claude Code to manage your OutboundHQ CRM.

## Credentials

Base URL:  https://api.outboundhq.ca/api/v1
Auth:      x-api-key header on every request

If $OUTBOUNDHQ_API_KEY or $OUTBOUNDHQ_TEAM_ID are not set, ask the user before
proceeding. Get a key from Dashboard → Team → API Keys. Find the team ID in the
dashboard URL: /team/{teamId}/dashboard

## Before you do anything

Fetch all lead groups so you can resolve names to IDs:

  curl -s https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/lead-groups \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY"

Never guess a group_id — always resolve it first.

## Adding a single lead

Extract: email (required), firstName, lastName, businessName, phone, jobTitle, notes.
Ask which group only if the user hasn't said.

  curl -s -X POST https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/lead-groups/{groupId}/leads \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"email":"jane@example.com","firstName":"Jane","lastName":"Doe"}'

Confirm: "Added [name] to [group name]."

## Importing multiple leads  (up to 5,000 per call)

Parse all entries the user provides (table, CSV, JSON, plain list).
Confirm count and group first: "I'll import 12 leads into 'Webinar Signups'. Go ahead?"

  curl -s -X POST https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/lead-groups/{groupId}/leads/import \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"leads":[{"email":"a@example.com"},{"email":"b@example.com"}]}'

Report: "Imported 12. 0 skipped (duplicates)."

## Adding a company

Extract: name (required), industry, website, city, country, employee_count, notes.

  curl -s -X POST https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/companies \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name":"Acme Corp","website":"https://acme.com"}'

## Adding or updating a deal

Extract: title (required), value, currency, stage, company_id, contact_lead_id, close_date.

Stage IDs: stage_prospecting  stage_qualified  stage_proposal
           stage_negotiation  stage_closed_won  stage_closed_lost

Map stage names to IDs automatically.

  # Create
  curl -s -X POST https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/deals \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"title":"Acme Enterprise","value":12000,"stage":"stage_prospecting"}'

  # Update — fetch deals first to find deal_id
  curl -s https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/deals \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY"

  curl -s -X PUT https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/deals/{dealId} \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"stage":"stage_qualified"}'

## Listing / searching

  # Lead groups
  curl -s https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/lead-groups \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY"

  # Companies (optional ?search=acme)
  curl -s "https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/companies?search=acme" \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY"

  # Deals
  curl -s https://api.outboundhq.ca/api/v1/$OUTBOUNDHQ_TEAM_ID/deals \
    -H "x-api-key: $OUTBOUNDHQ_API_KEY"

Present results as compact tables.

## Tone
Be direct and fast. Confirm actions in one sentence.
Set OUTBOUNDHQ_API_KEY and OUTBOUNDHQ_TEAM_ID as environment variables so Claude never needs to ask for them.