Paying $79 a seat for LinkedIn outreach?See what you'd save with Hollerly →
developers

Hollerly API

Add leads, read replies and move deals from your own code, Zapier, Make, or Claude. The API, webhooks into Zapier and Make, and the MCP server are on the Agency plan. Plain webhooks are on Pro and up.

Base URL: https://hollerlyai.com/api/v1. Every request and response is JSON.

Authentication

Create a key in Settings, Integrations, API keys. It starts with hl_live_ and is shown once. Send it as a Bearer token. A key acts for the whole workspace, so keep it server side and revoke it if it leaks.

curl
curl https://hollerlyai.com/api/v1/me \
  -H "Authorization: Bearer $HOLLERLY_KEY"
response
{ "workspace_id": "6f1c…", "plan": "agency", "email": "you@agency.com" }

Errors

Errors come back as { "error": "message" } with a status code: 400 bad input, 401 missing or revoked key, 403 plan doesn't include the API, 404 not found in this workspace, 409 not allowed right now (for example resuming before samples are approved), 500 our fault.

Endpoints

GET/api/v1/me

The workspace the key belongs to.

curl
curl https://hollerlyai.com/api/v1/me -H "Authorization: Bearer $HOLLERLY_KEY"

GET/api/v1/campaigns

Every campaign with its counts.

curl
curl https://hollerlyai.com/api/v1/campaigns -H "Authorization: Bearer $HOLLERLY_KEY"
response
{
  "campaigns": [
    {
      "id": "0b7e…", "name": "Founders in Austin", "status": "active",
      "samples_approved": 5, "client_id": null, "created_at": "2026-09-14T10:02:11Z",
      "stats": { "leads": 240, "invited": 180, "accepted": 71, "replied": 19,
                 "emailed": 40, "with_email": 96, "meetings": 6, "opened": 22 }
    }
  ]
}

GET/api/v1/campaigns/{id}/leads?status=&limit=

Newest leads first. limit defaults to 50, max 200. status is optional: new, in_sequence, waiting_accept, replied, done, failed or skipped.

curl
curl "https://hollerlyai.com/api/v1/campaigns/$CAMPAIGN_ID/leads?status=replied&limit=50" -H "Authorization: Bearer $HOLLERLY_KEY"

POST/api/v1/campaigns/{id}/leads

Up to 500 leads per call. Each needs a linkedin_url or an email; optional fields are full_name, first_name, company, headline and location. Email-only leads only get the campaign's email steps.

Skipped: rows without a usable URL or email, duplicates, people on your do-not-contact list (email, domain, LinkedIn or company), and anyone already in any campaign of the workspace.

curl
curl -X POST https://hollerlyai.com/api/v1/campaigns/$CAMPAIGN_ID/leads -H "Authorization: Bearer $HOLLERLY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "leads": [
      { "linkedin_url": "https://www.linkedin.com/in/jordan-lee/", "full_name": "Jordan Lee", "company": "Northwind" },
      { "email": "sam@acme.com", "first_name": "Sam", "company": "Acme" }
    ]
  }'
response
{ "added": 2, "skipped": 0 }

GET/api/v1/leads/{id}

One lead with status, pipeline stage, reply tag, tags, deal value and timestamps.

curl
curl https://hollerlyai.com/api/v1/leads/$LEAD_ID -H "Authorization: Bearer $HOLLERLY_KEY"

PATCH/api/v1/leads/{id}

Any of stage (replied, interested, meeting, proposal, won, lost, or null), tags (up to 20 strings, replaces the list) and deal_value (number or null). A stage change fires the lead.stage_changed event.

curl
curl -X PATCH https://hollerlyai.com/api/v1/leads/$LEAD_ID -H "Authorization: Bearer $HOLLERLY_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stage": "proposal", "tags": ["warm", "q4"], "deal_value": 4800 }'

GET/api/v1/replies?since=

Inbound LinkedIn messages and emails, newest first, up to 100. since is ISO 8601 and defaults to 7 days ago.

curl
curl "https://hollerlyai.com/api/v1/replies?since=2026-10-01T00:00:00Z" -H "Authorization: Bearer $HOLLERLY_KEY"
response
{
  "replies": [
    {
      "id": "c1d…", "lead_id": "8a2…", "channel": "linkedin", "subject": null,
      "body": "Sounds good, send me a time next week.", "at": "2026-10-01T15:20:04Z",
      "lead": { "full_name": "Jordan Lee", "company": "Northwind", "stage": "interested", "reply_tag": "interested", … }
    }
  ]
}

POST/api/v1/campaigns/{id}/pause

Stops sending for the campaign until it is resumed.

curl
curl -X POST https://hollerlyai.com/api/v1/campaigns/$CAMPAIGN_ID/pause -H "Authorization: Bearer $HOLLERLY_KEY"

POST/api/v1/campaigns/{id}/resume

Starts it again. Returns 409 until 5 sample messages are approved in the app.

curl
curl -X POST https://hollerlyai.com/api/v1/campaigns/$CAMPAIGN_ID/resume -H "Authorization: Bearer $HOLLERLY_KEY"

POST/api/v1/hooks

Subscribe a URL to one event (REST hooks). source is zapier, make or api. Events: lead.replied, lead.tagged, meeting.booked, lead.accepted, lead.stage_changed, lead.unsubscribed, message.sent.

curl
curl -X POST https://hollerlyai.com/api/v1/hooks -H "Authorization: Bearer $HOLLERLY_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "target_url": "https://hooks.zapier.com/hooks/standard/123/abc/", "event": "lead.replied", "source": "zapier" }'
response
{ "id": "5e0c…" }

DELETE/api/v1/hooks/{id}

Unsubscribe. Returning HTTP 410 from your endpoint also turns the hook off.

curl
curl -X DELETE https://hollerlyai.com/api/v1/hooks/$HOOK_ID -H "Authorization: Bearer $HOLLERLY_KEY"

GET/api/v1/samples/{event}

An array with one sample payload for the event, for Zapier's perform list and Make's sample data.

curl
curl https://hollerlyai.com/api/v1/samples/lead.replied -H "Authorization: Bearer $HOLLERLY_KEY"

Webhooks

Add endpoints in Settings, Integrations, Webhooks. Hollerly sends a POST with this body. Respond with any 2xx within 8 seconds.

json
{
  "event": "lead.replied",
  "created_at": "2026-10-02T09:14:00.000Z",
  "workspace_id": "6f1c…",
  "lead": {
    "id": "8a2…", "full_name": "Jordan Lee", "first_name": "Jordan",
    "headline": "Head of Growth at Northwind", "company": "Northwind",
    "email": "jordan@northwind.com", "profile_url": "https://www.linkedin.com/in/jordan-lee/",
    "public_id": "jordan-lee", "campaign_id": "0b7e…", "status": "replied",
    "stage": "replied", "reply_tag": null, "tags": [], "score": 8, "meeting_at": null
  },
  "data": { "channel": "linkedin", "text": "Sounds good, send me a time next week." }
}

Every request has an X-Hollerly-Signature: sha256=<hex> header: the HMAC-SHA256 of the raw request body, keyed with the webhook's secret. Verify it against the raw bytes before parsing.

node
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyHollerly(rawBody, header, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(header ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}

// Express: keep the raw body
app.post("/hooks/hollerly", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyHollerly(req.body, req.get("x-hollerly-signature"), process.env.HOLLERLY_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // event.event, event.lead, event.data
  res.sendStatus(204);
});

Zapier and Make

Both connect with an API key and REST hooks, so triggers fire instantly instead of polling.

  1. Create an API key named after the tool in Settings, Integrations.
  2. Authentication: API key, sent as the header Authorization: Bearer {{api_key}}. Test with GET /api/v1/me.
  3. Subscribe: POST /api/v1/hooks with { "target_url": "{{bundle.targetUrl}}", "event": "lead.replied", "source": "zapier" }. Store the returned id.
  4. Unsubscribe: DELETE /api/v1/hooks/{{bundle.subscribeData.id}}.
  5. Perform list (samples): GET /api/v1/samples/lead.replied.
  6. Actions: use POST /api/v1/campaigns/{id}/leads to add leads from a form, CRM or sheet, and PATCH /api/v1/leads/{id} to move deals.

In Make, use the HTTP module with the same header, or a custom webhook: create the webhook in Make, then register its URL with POST /api/v1/hooks and "source": "make".

MCP for Claude

Hollerly runs an MCP server at https://hollerlyai.com/api/mcp (Streamable HTTP, stateless). Connect Claude or any MCP client and ask things like "who replied this week?" or "add these 20 people to my Austin campaign". It uses the same API key.

Claude Code

shell
claude mcp add --transport http hollerly https://hollerlyai.com/api/mcp --header "Authorization: Bearer hl_live_..."

Other clients that read a JSON config:

json
{
  "mcpServers": {
    "hollerly": {
      "type": "http",
      "url": "https://hollerlyai.com/api/mcp",
      "headers": { "Authorization": "Bearer hl_live_..." }
    }
  }
}
ToolWhat it does
list_campaignsCampaigns with their counts
get_campaign_statsCounts plus accept and reply rates for one campaign
add_leadsAdd up to 500 leads to a campaign, same rules as the REST endpoint
list_repliesRecent inbound replies, optional since
get_leadOne lead
set_lead_stageMove a lead in the pipeline
pause_campaignPause sending
resume_campaignResume sending

Raw JSON-RPC, if you want to test by hand:

curl
curl -X POST https://hollerlyai.com/api/mcp -H "Authorization: Bearer $HOLLERLY_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": { "name": "list_replies", "arguments": { "since": "2026-10-01T00:00:00Z" } } }'