Developers · API 2026-10-26

Build on Leadash.

A versioned REST API for leads, lists, campaigns, replies, inboxes and email verification, with signed webhooks for everything that happens in between. The same service layer runs the Leadash app.

QuickstartOpenAPI 3.1 spec

The API is in private beta. Workspaces in the beta see scope options under Settings → API Keys.

Quickstart

  1. In Leadash, open Settings → API Keys, name a key, tick the scopes it needs (for this example, leads:write) and create it. Copy it now: it is shown once.
  2. Send it as a Bearer token. Create a lead:
shell
curl https://www.leadash.com/api/v1/leads \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1d2c1e-import-row-42" \
  -d '{
    "email": "ada@acme.ng",
    "first_name": "Ada",
    "company": "Acme",
    "linkedin_url": "https://www.linkedin.com/in/ada-obi"
  }'

A 201 returns the new lead in data. Send the same email again and you get 200 with created: false: the existing lead is updated, never duplicated.

Authentication

Every request carries Authorization: Bearer ld_live_…. Keys are stored only as a hash, so a lost key cannot be recovered: revoke it and create another. A key can be bound to a team member (it acts as them, and stops working if they leave the workspace) and can expire after 7 to 365 days. Keep keys on your server, never in a browser or mobile app.

Call https://www.leadash.com, not the bare domain: it redirects, and HTTP clients drop the Authorization header when they follow a redirect to another host, so the request arrives without its key and fails with 401.

Scopes

A key can do only what its scopes allow, and scopes do not imply each other: leads:write does not grant leads:read. Each endpoint below names the one scope it needs.

ScopeAllows
leads:readRead leads and lists
leads:writeCreate and update leads
campaigns:readRead campaigns and stats
campaigns:writeCreate, edit, start and pause campaigns
replies:readRead replies
inboxes:readRead inboxes and their health
verify:writeVerify emails (spends credits)
account:readRead the credit balance and usage

Rate limits

Each key has a token bucket: bursts of 120 requests, refilling at 120 per minute. Verification endpoints have their own, smaller bucket: 20 burst, 30 per minute. Every response reports where you stand:

X-RateLimit-LimitBucket size.
X-RateLimit-RemainingRequests you can make right now.
X-RateLimit-ResetUnix time (seconds) when the bucket is full again.
Retry-AfterOn a 429 only: seconds to wait before retrying.

Errors

Errors share one shape. Switch on error.code, which is stable; message is for people and may change.

http
HTTP/1.1 400 Bad Request
Leadash-Api-Version: 2026-10-26
X-Request-Id: req_4f0c…

{
  "error": {
    "code": "invalid_request",
    "message": "2 fields are invalid",
    "details": {
      "fields": [
        { "field": "email", "message": "must be a valid email address" },
        { "field": "frist_name", "message": "is not a recognised field" }
      ]
    }
  }
}
StatusCodesMeaning
400invalid_requestBad JSON, a missing or invalid field, or an unknown field. details.fields lists every problem.
401unauthorized · key_expiredNo key, an unknown key, an expired key, or a key whose member left the workspace.
402insufficient_credits · plan_requiredNot enough credits, or the plan does not include this.
403insufficient_scope · api_not_enabledThe key lacks a scope the endpoint needs, or the API is not enabled for this workspace.
404not_foundThe id does not exist in this workspace.
409conflict · idempotency_in_progressThe request conflicts with current state (a duplicate lead, a campaign in the wrong status, warnings to acknowledge).
422unprocessable · idempotency_key_reusedUnderstood but refused (e.g. an unsubscribed email), or an Idempotency-Key reused for a different request.
429rate_limitedSlow down. Wait Retry-After seconds.
500 / 503internal_error · unavailableOur side. Safe to retry with the same Idempotency-Key. Quote X-Request-Id to support.

Pagination

List endpoints return { data, next_cursor, has_more }, newest first. Pass next_cursor back as cursor until it is null. Cursors are opaque and stable while rows are being added, so you never skip or repeat a row. limit is 1 to 100 (default 25). All timestamps are ISO 8601 in UTC.

javascript
let cursor = null;
do {
  const url = new URL("https://www.leadash.com/api/v1/leads");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);
  const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.LEADASH_API_KEY}` } });
  const page = await res.json();
  for (const lead of page.data) handle(lead);
  cursor = page.next_cursor;
} while (cursor);

Idempotency

Send an Idempotency-Key header (up to 255 characters; a UUID is ideal) on any POST and retry safely for 24 hours. A retry with the same key and the same body returns the original response with Idempotent-Replayed: true; the same key with a different body is a 422; a retry while the first is still running is a 409. Server errors are not stored, so a retry after a 5xx runs again.

Versioning

The path is versioned (/api/v1) and every response carries Leadash-Api-Version: 2026-10-26. New endpoints, new optional fields and new event types are added without notice, so ignore fields you do not know. Anything that would break an existing integration ships under a new version.

Webhooks

Add an endpoint under Settings → Webhooks and pick the events it receives. We POST a JSON envelope to it. Respond with any 2xx within 10 seconds; anything else (or a timeout) is retried with exponential backoff, eight attempts over about an hour. Retries carry the same id, so dedupe on it. Endpoints must be https.

EventWhendata
lead.createdA lead was createdlead (the full Lead object)
lead.updatedA lead was updatedlead, changed (the field names that were written)
campaign.reply_receivedA lead replied to a campaign emailreply_id, thread_id, campaign_id, lead_id, from_email, subject, body_text, category, is_auto_reply, received_at
campaign.lead_bouncedA campaign email bouncedemail, lead_id, campaign_id, thread_id, send_id, bounce_type, source (smtp | ndr | dsn)
campaign.lead_unsubscribedA lead unsubscribedemail, lead_id or lead_ids, thread_id, source (link | reply)
campaign.completedA campaign finished sendingcampaign_id, name, total_enrolled
verification.completedAn email verification finishedkind (single | bulk); single: email, status, score, deliverable, credits_charged; bulk: job_id, status, total, processed, counts
linkedin.invite_acceptedA LinkedIn invite was acceptedlead_id, linkedin_url, email, first_name, last_name, campaign_id, thread_id
linkedin.reply_receivedA lead replied on LinkedInlead_id, linkedin_url, email, first_name, last_name, campaign_id, thread_id
linkedin.sequence_stepA LinkedIn sequence reached a Webhook step
http
POST /webhooks/leadash HTTP/1.1
Content-Type: application/json
Leadash-Signature: t=1761473000,v1=5f2b0c…e91a
Leadash-Event: campaign.reply_received
Leadash-Event-Id: evt_3b9f0e6a1c2d4e5f8a7b6c5d4e3f2a1b
Leadash-Delivery-Attempt: 1
Leadash-Api-Version: 2026-10-26

{
  "id": "evt_3b9f0e6a1c2d4e5f8a7b6c5d4e3f2a1b",
  "type": "campaign.reply_received",
  "created_at": "2026-10-26T10:03:15.000Z",
  "workspace_id": "00000000-0000-4000-8000-000000000000",
  "api_version": "2026-10-26",
  "data": {
    "reply_id": "e4d3c2b1-a098-4f76-8e54-3d2c1b0a9f8e",
    "thread_id": "9f8e7d6c-5b4a-4321-9876-0fedcba98765",
    "campaign_id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
    "lead_id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
    "from_email": "ada@acme.ng",
    "subject": "Re: Quick question",
    "body_text": "Sounds good, send me a time on Thursday.",
    "category": "interested",
    "is_auto_reply": false,
    "received_at": "2026-10-26T10:03:11.000Z"
  }
}

Verifying signatures

Every delivery has one header, Leadash-Signature: t=<unix seconds>,v1=<signature>. To check it:

  1. Read t and every v1 from the header.
  2. Build the string `${t}.${raw body}` from the raw request bytes, before any JSON parsing.
  3. Compute HMAC-SHA256 of it with your endpoint's signing secret, hex-encoded.
  4. Compare with each v1 in constant time. Accept if one matches.
  5. Reject if t is more than 5 minutes from your clock. Because t is signed, this stops a captured delivery being replayed.
import crypto from "node:crypto";
import express from "express";

const SECRET = process.env.LEADASH_WEBHOOK_SECRET; // Settings → Webhooks
const TOLERANCE_SEC = 300;

export function verifyLeadashSignature(rawBody, header, secret = SECRET) {
  if (!header) return false;
  let t = null; const sigs = [];
  for (const part of header.split(",")) {
    const [k, v] = part.split("=", 2).map(s => s.trim());
    if (k === "t") t = Number(v);
    if (k === "v1") sigs.push(v);
  }
  if (!t || !sigs.length) return false;
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SEC) return false;

  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return sigs.some(s => {
    const got = Buffer.from(s, "hex");
    return got.length === expected.length && crypto.timingSafeEqual(got, expected);
  });
}

const app = express();
// Verify against the RAW body: re-serialised JSON will not match.
app.post("/webhooks/leadash", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verifyLeadashSignature(raw, req.get("Leadash-Signature"))) return res.sendStatus(400);
  const event = JSON.parse(raw);
  // Dedupe on event.id: a retry carries the same id.
  console.log(event.type, event.data);
  res.sendStatus(200);
});

MCP: connect Claude

Leadash is an MCP server, so Claude and other AI assistants can search your leads, read campaign stats and replies, check inbox health and work with Simba from a chat. There is no key to paste: the assistant signs you in to Leadash, you pick a workspace and what it may do, and you can revoke it any time under Settings → Connected apps. Same private beta as the API.

server url
https://www.leadash.com/api/mcp

Claude (claude.ai and Claude Desktop)

  1. Open Settings → Connectors and choose Add custom connector.
  2. Name it Leadash and paste https://www.leadash.com/api/mcp as the URL. Leave the OAuth fields empty.
  3. Click Connect, sign in to Leadash, choose the workspace and permissions, and approve.

Claude Code

shell
claude mcp add --transport http leadash https://www.leadash.com/api/mcp

Then run /mcp in Claude Code and choose Authenticate; your browser opens the Leadash sign-in.

Other MCP clients

Any client that speaks Streamable HTTP with OAuth works. The server implements the 2026-07-28 MCP spec and still accepts 2025-11-25 clients (the initialize handshake). A request without a token gets 401 with WWW-Authenticate: Bearer resource_metadata=…; from there the client discovers /.well-known/oauth-protected-resource/api/mcp and /.well-known/oauth-authorization-server. Clients identify themselves with a Client ID Metadata Document (an https client_id) or register dynamically; only public clients with PKCE (S256) are supported. Access tokens last an hour and refresh tokens rotate on every use.

Permissions

ScopeAllows
leads:readRead leads and lists
leads:writeCreate and update leads
campaigns:readRead campaigns and stats
campaigns:writeCreate, edit, start and pause campaigns
replies:readRead replies
inboxes:readRead inboxes and their health
verify:writeVerify emails (spends credits)
account:readRead the credit balance and usage
simba:readRead Simba agents and the review queue
agent:runSet up and run Simba, and approve its leads
discover:readSearch the Discover database (contact details stay hidden)
linkedin:readRead LinkedIn accounts, campaigns, lists and conversations
linkedin:writeCreate LinkedIn campaigns, imports, replies and posts

Tools

ToolScopeWhat it does
search_leadsleads:readSearch the workspace's leads pool, newest first.
get_leadleads:readOne lead by id, with its contact fields, custom fields and verification status..
list_listsleads:readThe workspace's lead lists with lead, verified and pending counts..
get_verification_jobleads:readProgress and results of a list verification started with verify_list (job_id from its result)..
search_discoverdiscover:readSearch Leadash Discover, a database of business contacts, for people matching an ideal customer profile.
search_discover_companiesdiscover:readSearch Discover for companies (industry, location, size, funding, keywords), largest contact count first.
list_campaignscampaigns:readCampaigns, newest first, with status, channel, lists and inboxes.
get_campaign_statscampaigns:readOne campaign with its stats: enrolled, sent, opened, replied, bounced, unsubscribed (same definitions as the campaign page)..
list_repliesreplies:readReplies from leads, newest first, with the AI category.
get_threadreplies:readA full conversation (one lead in one campaign): every email sent and every reply, in time order..
list_inboxes_healthinboxes:readEvery sending inbox with its health (healthy, warming, paused, attention, error), daily limit, warmup progress and whether it can send campaigns.
get_creditsaccount:readThe lead-credit balance (subscription and permanent) and usage over the last days days: credits spent, emails sent, replies, leads created, API calls..
simba_statussimba:readSimba, the AI SDR: each agent's status and autonomy, its latest run (stage, counts, credits) and how many leads wait for review.. Simba beta.
simba_review_queuesimba:readLeads Simba found and wrote to, waiting for a human to approve: who they are, why Simba picked them (score, reason), the drafted emails and the sources each claim cites. Simba beta.
list_linkedin_accountslinkedin:readThe workspace's LinkedIn accounts (run by the Leadash Prospector desktop app on the member's own computer): status, whether the app is online, usage hours, warm-up week and, per action (invite, message, view, follow, like, …), today's limit, used, remaining and whether it can run now.
list_linkedin_campaignslinkedin:readLinkedIn campaigns with senders and stats (leads, invites sent, accepted, replies, acceptance rate) and whether a sender's Prospector app is offline..
get_linkedin_campaignlinkedin:readOne LinkedIn campaign: senders, enrollment counts, the funnel (visited, invited, accepted, messaged, replied), its sequence as a step list, and launch_blocker (why it cannot be launched yet, if anything)..
list_linkedin_listslinkedin:readLinkedIn lead lists with lead counts and how many still need a LinkedIn profile match..
list_linkedin_threadslinkedin:readLinkedIn conversations synced by Prospector, newest first, with the lead, the account and the last message.
get_linkedin_threadlinkedin:readA LinkedIn conversation's messages (oldest first) plus replies still queued for Prospector.
create_leadapprovalleads:writeAsk to add a lead (or update the existing lead with the same email or LinkedIn profile).
add_to_listapprovalleads:writeAsk to move up to 1000 existing leads into a list.
enroll_in_campaignapprovalcampaigns:writeAsk to enroll leads (by lead_ids, or every lead in list_ids) in an email campaign.
pause_campaignapprovalcampaigns:writeAsk to pause an active campaign (no more emails go out until it is resumed).
resume_campaignapprovalcampaigns:writeAsk to resume a paused campaign, which starts sending again.
verify_emailapprovalverify:writeAsk to verify one email address (spends lead credits; unknown verdicts are free).
approve_simba_leadsapprovalagent:runAsk to approve leads from Simba's review queue so Simba launches them (creates the leads and enrolls them in the agent's campaign). Simba beta.
export_discover_peopleapprovalleads:writeAsk to add up to 1000 Discover people (ids from search_discover) to a lead list, unlocking their emails.
import_leadsapprovalleads:writeAsk to add up to 1000 leads you found (from the web or the user's files) to a list in one approval.
verify_listapprovalverify:writeAsk to verify every unverified lead in a list (spends the verification credit rate per lead with an email, charged up front; check get_credits).
create_campaignapprovalcampaigns:writeAsk to create an email campaign with its whole sequence (1-10 steps).
create_simba_agentapprovalagent:runAsk to set up a Simba agent (Leadash's AI SDR) from an offer and an ideal-customer description. Simba beta.
activate_simba_agentapprovalagent:runAsk to activate a Simba agent after its sample, and start a run now (run_now, default true). Simba beta.
create_linkedin_campaignapprovallinkedin:writeAsk to create a LinkedIn campaign with its sequence, as a DRAFT.
enroll_in_linkedin_campaignapprovallinkedin:writeAsk to enroll people in a LinkedIn campaign: existing leads (lead_ids and/or a LinkedIn list_id), or up to 200 LinkedIn profile URLs (profile_urls, saved into list_id / list_name / a list named after the campaign).
set_linkedin_campaign_statusapprovallinkedin:writeAsk to launch (active) or pause a LinkedIn campaign.
start_linkedin_importapprovallinkedin:writeAsk Prospector to collect people from LinkedIn into a LinkedIn list, from: search_url (a linkedin.com/search/results/people/ URL you can build from the ICP: keywords, titles, locations), sales_nav_url (needs a Sales Navigator account), post_engagers (a post URL), group_members, event_attendees, company_employees (company page URL, optional title_keywords), or network / profile_viewers / followers (no URL).
send_linkedin_replyapprovallinkedin:writeAsk to send a reply in a LinkedIn conversation.
schedule_linkedin_postapprovallinkedin:writeAsk to save a text post for a LinkedIn account, as a draft or scheduled (scheduled_for: ISO 8601 with offset, required when scheduled).

An assistant only sees the tools its permissions allow.

Approval links

Nothing an assistant does creates, sends or spends on its own. Tools marked approval record a request and return a link instead of acting:

tool result
{
  "status": "pending_approval",
  "action_id": "6f0c…",
  "approval_url": "https://www.leadash.com/settings/connected-apps/actions/6f0c…",
  "summary": "Enroll 40 lead(s) in campaign \"Lagos agencies Q4\"",
  "expires_at": "2026-10-27T09:12:00Z"
}

Open the link in Leadash to see the exact request, then Approve (it runs once, as you) or Reject. Requests expire after 24 hours, and revoking the app cancels any still waiting. Every tool call is in your workspace's API audit log.

Reference

Base URL https://www.leadash.com/api/v1. Objects come back as { "data": … }.

Leads

GET/api/v1/leadsleads:read

List leads

Newest first. Deleted leads are not returned.

ParameterInDescription
limitqueryPage size, 1-100 (default 25).
cursorquerynext_cursor from the previous page.
list_idqueryOnly leads in this list.
emailqueryExact email (case-insensitive).
linkedin_urlqueryA linkedin.com/in/… profile URL.
statusqueryLead status. One of active, unsubscribed, bounced, invalid.
verification_statusqueryVerification verdict. One of pending, safe, valid, verified_external, catch_all, unknown, risky, invalid, dangerous, disposable.
created_afterqueryOnly leads created after this time.
qquerySearch email, name and company.
request
curl "https://www.leadash.com/api/v1/leads?limit=25" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": [
    {
      "id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
      "list_id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
      "email": "ada@acme.ng",
      "first_name": "Ada",
      "last_name": "Obi",
      "company": "Acme",
      "title": "Head of Growth",
      "website": "https://acme.ng",
      "linkedin_url": "https://www.linkedin.com/in/ada-obi",
      "linkedin_public_id": "ada-obi",
      "city": "Lagos",
      "country": "Nigeria",
      "first_line": null,
      "custom_fields": {
        "industry": "fintech"
      },
      "status": "active",
      "verification_status": "pending",
      "verification_score": null,
      "verified_at": null,
      "source": "api",
      "created_at": "2026-10-26T09:12:44.120Z"
    }
  ],
  "next_cursor": "WyIyMDI2LTEwLTI2VDA5OjEyOjQ0LjEyMFoiLCI1YjBjMmExZSJd",
  "has_more": true
}
POST/api/v1/leadsleads:write

Create or update a lead

A lead needs an email or a linkedin_url. If a lead with the same email (or, failing that, the same LinkedIn profile) exists, it is updated with the fields you send and the response is 200 with created: false; send on_conflict: "error" to get a 409 instead. New leads start with verification_status: "pending". Emails on the workspace's unsubscribe list are refused with 422.

ParameterInDescription
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/leads" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "email": "ada@acme.ng",
    "first_name": "Ada",
    "last_name": "Obi",
    "company": "Acme",
    "linkedin_url": "https://www.linkedin.com/in/ada-obi",
    "list_id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
    "custom_fields": {
      "industry": "fintech"
    }
  }'
response · 201
{
  "data": {
    "id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
    "list_id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
    "email": "ada@acme.ng",
    "first_name": "Ada",
    "last_name": "Obi",
    "company": "Acme",
    "title": "Head of Growth",
    "website": "https://acme.ng",
    "linkedin_url": "https://www.linkedin.com/in/ada-obi",
    "linkedin_public_id": "ada-obi",
    "city": "Lagos",
    "country": "Nigeria",
    "first_line": null,
    "custom_fields": {
      "industry": "fintech"
    },
    "status": "active",
    "verification_status": "pending",
    "verification_score": null,
    "verified_at": null,
    "source": "api",
    "created_at": "2026-10-26T09:12:44.120Z",
    "created": true
  }
}
GET/api/v1/leads/{id}leads:read

Get a lead

ParameterInDescription
idrequiredpathThe lead id.
request
curl "https://www.leadash.com/api/v1/leads/<id>" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
    "list_id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
    "email": "ada@acme.ng",
    "first_name": "Ada",
    "last_name": "Obi",
    "company": "Acme",
    "title": "Head of Growth",
    "website": "https://acme.ng",
    "linkedin_url": "https://www.linkedin.com/in/ada-obi",
    "linkedin_public_id": "ada-obi",
    "city": "Lagos",
    "country": "Nigeria",
    "first_line": null,
    "custom_fields": {
      "industry": "fintech"
    },
    "status": "active",
    "verification_status": "pending",
    "verification_score": null,
    "verified_at": null,
    "source": "api",
    "created_at": "2026-10-26T09:12:44.120Z"
  }
}
PATCH/api/v1/leads/{id}leads:write

Update a lead

Send only the fields to change; null clears a field. Changing the email resets verification to pending. status accepts only "unsubscribed" (which also adds the address to the unsubscribe list).

ParameterInDescription
idrequiredpathThe lead id.
request
curl -X PATCH "https://www.leadash.com/api/v1/leads/<id>" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "VP Growth",
    "custom_fields": {
      "industry": "fintech",
      "stage": "series-a"
    }
  }'
response · 200
{
  "data": {
    "id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
    "list_id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
    "email": "ada@acme.ng",
    "first_name": "Ada",
    "last_name": "Obi",
    "company": "Acme",
    "title": "VP Growth",
    "website": "https://acme.ng",
    "linkedin_url": "https://www.linkedin.com/in/ada-obi",
    "linkedin_public_id": "ada-obi",
    "city": "Lagos",
    "country": "Nigeria",
    "first_line": null,
    "custom_fields": {
      "industry": "fintech"
    },
    "status": "active",
    "verification_status": "pending",
    "verification_score": null,
    "verified_at": null,
    "source": "api",
    "created_at": "2026-10-26T09:12:44.120Z"
  }
}
DELETE/api/v1/leads/{id}leads:write

Delete a lead

Removes the lead from every live campaign and cancels its queued steps. Creating the same lead again restores it.

ParameterInDescription
idrequiredpathThe lead id.
request
curl -X DELETE "https://www.leadash.com/api/v1/leads/<id>" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
    "deleted": true
  }
}

Lists

GET/api/v1/listsleads:read

List lead lists

ParameterInDescription
limitqueryPage size, 1-100 (default 25).
cursorquerynext_cursor from the previous page.
channelqueryOnly lists for this channel. One of email, linkedin.
request
curl "https://www.leadash.com/api/v1/lists?limit=25" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": [
    {
      "id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
      "name": "Lagos fintech",
      "description": null,
      "tags": [
        "q4"
      ],
      "channel": "email",
      "created_at": "2026-10-18T12:00:00.000Z",
      "lead_count": 412,
      "verified_count": 380,
      "pending_count": 20
    }
  ],
  "next_cursor": "WyIyMDI2LTEwLTI2VDA5OjEyOjQ0LjEyMFoiLCI1YjBjMmExZSJd",
  "has_more": true
}
POST/api/v1/listsleads:write

Create a list

ParameterInDescription
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/lists" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "name": "Lagos fintech",
    "description": "From the October event",
    "tags": [
      "q4"
    ]
  }'
response · 201
{
  "data": {
    "id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
    "name": "Lagos fintech",
    "description": "From the October event",
    "tags": [
      "q4"
    ],
    "channel": "email",
    "created_at": "2026-10-26T12:00:00.000Z",
    "lead_count": 0,
    "verified_count": 0,
    "pending_count": 0
  }
}
POST/api/v1/lists/{id}/leadsleads:write

Move leads into a list

A lead belongs to one list, so this moves it. Up to 1,000 ids per call.

ParameterInDescription
idrequiredpathThe list id.
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/lists/<id>/leads" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "lead_ids": [
      "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21"
    ]
  }'
response · 200
{
  "data": {
    "list_id": "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10",
    "updated": 1,
    "not_found": []
  }
}

Campaigns

GET/api/v1/campaignscampaigns:read

List campaigns

ParameterInDescription
limitqueryPage size, 1-100 (default 25).
cursorquerynext_cursor from the previous page.
statusqueryCampaign status. One of draft, active, paused, completed.
channelqueryOnly campaigns for this channel. One of email, linkedin.
request
curl "https://www.leadash.com/api/v1/campaigns?limit=25" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": [
    {
      "id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
      "name": "Lagos fintech founders",
      "status": "active",
      "channel": "email",
      "sequence_mode": "linear",
      "pause_reason": null,
      "list_ids": [
        "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10"
      ],
      "inbox_ids": [
        "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f"
      ],
      "timezone": "Africa/Lagos",
      "daily_cap": 200,
      "created_at": "2026-10-20T08:00:00.000Z",
      "updated_at": "2026-10-25T17:40:02.000Z"
    }
  ],
  "next_cursor": "WyIyMDI2LTEwLTI2VDA5OjEyOjQ0LjEyMFoiLCI1YjBjMmExZSJd",
  "has_more": true
}
GET/api/v1/campaigns/{id}campaigns:read

Get a campaign with stats

ParameterInDescription
idrequiredpathThe campaign id.
request
curl "https://www.leadash.com/api/v1/campaigns/<id>" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
    "name": "Lagos fintech founders",
    "status": "active",
    "channel": "email",
    "sequence_mode": "linear",
    "pause_reason": null,
    "list_ids": [
      "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10"
    ],
    "inbox_ids": [
      "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f"
    ],
    "timezone": "Africa/Lagos",
    "daily_cap": 200,
    "created_at": "2026-10-20T08:00:00.000Z",
    "updated_at": "2026-10-25T17:40:02.000Z",
    "stats": {
      "enrolled": 412,
      "sent": 980,
      "opened": 301,
      "replied": 27,
      "bounced": 4,
      "unsubscribed": 2
    }
  }
}
POST/api/v1/campaigns/{id}/enrollmentscampaigns:write

Enroll leads

Send lead_ids (up to 1,000) or list_ids. Only leads that can be sent to are enrolled: a confirmed-deliverable email (safe, valid, verified_external, plus catch_all when the campaign includes catch-all), not unsubscribed, not already in the campaign. Everything else is counted in skipped.

ParameterInDescription
idrequiredpathThe campaign id.
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/campaigns/<id>/enrollments" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "lead_ids": [
      "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21"
    ]
  }'
response · 200
{
  "data": {
    "enrolled": 1,
    "skipped": {
      "not_deliverable": 0,
      "no_email": 0,
      "unsubscribed": 0,
      "already_enrolled": 0,
      "inactive": 0
    },
    "not_found": 0
  }
}
POST/api/v1/campaigns/{id}/pausecampaigns:write

Pause a campaign

Stops new sends. Pausing a paused campaign is a no-op.

ParameterInDescription
idrequiredpathThe campaign id.
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/campaigns/<id>/pause" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
response · 200
{
  "data": {
    "id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
    "name": "Lagos fintech founders",
    "status": "paused",
    "channel": "email",
    "sequence_mode": "linear",
    "pause_reason": null,
    "list_ids": [
      "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10"
    ],
    "inbox_ids": [
      "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f"
    ],
    "timezone": "Africa/Lagos",
    "daily_cap": 200,
    "created_at": "2026-10-20T08:00:00.000Z",
    "updated_at": "2026-10-25T17:40:02.000Z"
  }
}
POST/api/v1/campaigns/{id}/resumecampaigns:write

Resume a paused campaign

Runs the same checks as the app: a plan that can run campaigns, at least one active inbox, and the leads-pool limit. If the copy reads as high spam risk or uses merge variables most leads lack, you get 409 with the findings in details; fix them, or resend with acknowledge_warnings: true. Drafts are started from the app.

ParameterInDescription
idrequiredpathThe campaign id.
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/campaigns/<id>/resume" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "acknowledge_warnings": false
  }'
response · 200
{
  "data": {
    "id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
    "name": "Lagos fintech founders",
    "status": "active",
    "channel": "email",
    "sequence_mode": "linear",
    "pause_reason": null,
    "list_ids": [
      "0f1d1c3e-2b8a-4b8e-9a51-8b6f0a7a9e10"
    ],
    "inbox_ids": [
      "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f"
    ],
    "timezone": "Africa/Lagos",
    "daily_cap": 200,
    "created_at": "2026-10-20T08:00:00.000Z",
    "updated_at": "2026-10-25T17:40:02.000Z"
  }
}

Replies

GET/api/v1/repliesreplies:read

List replies

Replies to your campaigns, newest first. Out-of-office auto-replies are left out unless include_auto_replies=true.

ParameterInDescription
limitqueryPage size, 1-100 (default 25).
cursorquerynext_cursor from the previous page.
campaign_idqueryOnly replies in this campaign.
lead_idqueryOnly replies from this lead.
categoryqueryAI category, e.g. interested, not_interested, meeting_booked, ooo.
sincequeryReceived at or after this time.
include_auto_repliesqueryInclude auto-replies (default false).
request
curl "https://www.leadash.com/api/v1/replies?limit=25" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": [
    {
      "id": "e4d3c2b1-a098-4f76-8e54-3d2c1b0a9f8e",
      "thread_id": "9f8e7d6c-5b4a-4321-9876-0fedcba98765",
      "campaign_id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
      "lead_id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
      "inbox_id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "from_email": "ada@acme.ng",
      "from_name": "Ada Obi",
      "subject": "Re: Quick question",
      "body_text": "Sounds good, send me a time on Thursday.",
      "category": "interested",
      "is_auto_reply": false,
      "received_at": "2026-10-26T10:03:11.000Z",
      "created_at": "2026-10-26T10:03:15.000Z"
    }
  ],
  "next_cursor": "WyIyMDI2LTEwLTI2VDA5OjEyOjQ0LjEyMFoiLCI1YjBjMmExZSJd",
  "has_more": true
}
GET/api/v1/threads/{id}replies:read

Get a conversation thread

Every email sent to the lead in one campaign and every reply, oldest first. The id is a reply's thread_id.

ParameterInDescription
idrequiredpathThe thread id.
request
curl "https://www.leadash.com/api/v1/threads/<id>" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "id": "9f8e7d6c-5b4a-4321-9876-0fedcba98765",
    "campaign_id": "8a3c9d2e-1b44-4b0e-a7f2-55f0c1d2e3a4",
    "status": "replied",
    "crm_status": "interested",
    "lead": {
      "id": "5b0c2a1e-8f8e-4c55-9d5e-0b6a7a3f1c21",
      "email": "ada@acme.ng",
      "first_name": "Ada",
      "last_name": "Obi",
      "company": "Acme"
    },
    "messages": [
      {
        "type": "sent",
        "id": "1a2b3c4d-0000-4000-8000-000000000001",
        "at": "2026-10-24T09:00:00.000Z",
        "from_email": "tunde@getacme.co",
        "to_email": "ada@acme.ng",
        "subject": "Quick question",
        "body": "Hi Ada, …",
        "status": "opened",
        "opened_at": "2026-10-24T09:40:00.000Z",
        "bounced_at": null
      },
      {
        "type": "reply",
        "id": "e4d3c2b1-a098-4f76-8e54-3d2c1b0a9f8e",
        "at": "2026-10-26T10:03:11.000Z",
        "from_email": "ada@acme.ng",
        "to_email": "tunde@getacme.co",
        "subject": "Re: Quick question",
        "body": "Sounds good, send me a time on Thursday.",
        "category": "interested",
        "is_auto_reply": false
      }
    ]
  }
}

Inboxes

GET/api/v1/inboxesinboxes:read

List inboxes with health

health.state is healthy, warming (inside the mandatory warmup window, or warmup-only), paused, attention (sending, but the last attempt reported an error) or error. Credentials are never returned.

ParameterInDescription
statusqueryInbox status. One of active, paused, error.
request
curl "https://www.leadash.com/api/v1/inboxes" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": [
    {
      "id": "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
      "email_address": "tunde@getacme.co",
      "label": "Tunde",
      "provider": "graph",
      "status": "active",
      "daily_send_limit": 30,
      "warmup": {
        "enabled": true,
        "current_daily": 20,
        "target_daily": 30
      },
      "health": {
        "state": "healthy",
        "can_send_campaigns": true,
        "campaign_sends_blocked_until": null,
        "last_error": null
      },
      "created_at": "2026-09-01T10:00:00.000Z"
    }
  ]
}

Verification

POST/api/v1/verificationsverify:write

Verify one email

Synchronous; can take up to ~30 seconds. Costs the verify credit rate, refunded when the verdict is unknown. deliverable is true for the verdicts campaigns will send to.

ParameterInDescription
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/verifications" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "email": "ada@acme.ng"
  }'
response · 200
{
  "data": {
    "email": "ada@acme.ng",
    "status": "safe",
    "score": 95,
    "deliverable": true,
    "credits_charged": 0.5
  }
}
POST/api/v1/verification-jobsverify:write

Start a bulk verification job

Up to 10,000 unique emails. Credits for every address are charged when the job is accepted. Poll the job, or subscribe to verification.completed.

ParameterInDescription
Idempotency-KeyheaderMakes a retry of this POST safe for 24 hours.
request
curl -X POST "https://www.leadash.com/api/v1/verification-jobs" \
  -H "Authorization: Bearer $LEADASH_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "emails": [
      "ada@acme.ng",
      "tunde@example.com"
    ]
  }'
response · 202
{
  "data": {
    "id": "7c6b5a49-3827-4615-a4b3-c2d1e0f9a8b7",
    "status": "pending",
    "total": 2,
    "processed": 0,
    "counts": {
      "safe": 0,
      "invalid": 0,
      "catch_all": 0,
      "risky": 0,
      "dangerous": 0,
      "disposable": 0,
      "unknown": 0
    },
    "credits_used": 1,
    "refunded": 0,
    "error": null,
    "created_at": "2026-10-26T11:00:00.000Z",
    "completed_at": null
  }
}
GET/api/v1/verification-jobs/{id}verify:write

Get a verification job

ParameterInDescription
idrequiredpathThe job id.
include_resultsqueryInclude per-email results (default false).
request
curl "https://www.leadash.com/api/v1/verification-jobs/<id>" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "id": "7c6b5a49-3827-4615-a4b3-c2d1e0f9a8b7",
    "status": "done",
    "total": 2,
    "processed": 2,
    "counts": {
      "safe": 1,
      "invalid": 1,
      "catch_all": 0,
      "risky": 0,
      "dangerous": 0,
      "disposable": 0,
      "unknown": 0
    },
    "credits_used": 1,
    "refunded": 0,
    "error": null,
    "created_at": "2026-10-26T11:00:00.000Z",
    "completed_at": "2026-10-26T11:01:30.000Z",
    "results": [
      {
        "email": "ada@acme.ng",
        "status": "valid",
        "score": 95
      },
      {
        "email": "tunde@example.com",
        "status": "invalid",
        "score": 0
      }
    ]
  }
}

Account

GET/api/v1/accountaccount:read

Plan and credit balance

request
curl "https://www.leadash.com/api/v1/account" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "balance": 1240.5,
    "subscription_credits": 1000,
    "permanent_credits": 240.5,
    "plan_id": "growth",
    "plan_status": "active"
  }
}
GET/api/v1/account/usageaccount:read

Usage over a period

ParameterInDescription
daysqueryLook-back window, 1-90 (default 30).
request
curl "https://www.leadash.com/api/v1/account/usage" \
  -H "Authorization: Bearer $LEADASH_API_KEY"
response · 200
{
  "data": {
    "period": {
      "start": "2026-09-26T12:00:00.000Z",
      "end": "2026-10-26T12:00:00.000Z",
      "days": 30
    },
    "credits_spent": 812.5,
    "credits_refunded": 14,
    "emails_sent": 9120,
    "replies_received": 211,
    "leads_created": 3400,
    "api_calls": 18250
  }
}

Ready to build?

Create a key, point it at a sandbox list, and you can be importing leads in minutes. Questions about the beta: get in touch.