Weezly Outreach
|Developer Docs

Leads API

Manage lead lists and individual leads. Import leads, search, export, and organize them into lists and folders.

List Lead Lists

GET/api/leads/lists

Retrieve all lead lists for the authenticated user, newest updated first.

Query Parameters

ParameterTypeDescription
searchstringFilter lists by name (case-insensitive)
Request — cURLcURL
curl --location --request GET 'https://outreach.weezly.com/api/leads/lists' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{
  "lists": [
    {
      "id": "cm3list789abc",
      "name": "SaaS Founders (West Coast)",
      "description": "Founders of B2B SaaS companies in CA, WA, OR",
      "userId": "user_123",
      "leadCount": 245,
      "folderId": null,
      "folder": null,
      "primarySource": "SEARCH",
      "createdAt": "2026-08-01T08:00:00.000Z",
      "updatedAt": "2026-08-15T14:30:00.000Z",
      "campaigns": [
        {
          "id": "cm3camp456",
          "name": "Q3 SaaS Outreach",
          "status": "ACTIVE"
        }
      ]
    }
  ]
}

Response Codes

200Lists returned successfully
401Unauthorized

Create Lead List

POST/api/leads/lists

Create a new lead list.

Request Body

ParameterTypeDescription
namerequiredstringName of the lead list
descriptionstringOptional description
Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/leads/lists' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Enterprise CTOs",
  "description": "CTOs at companies with 500+ employees"
}'
Response - 201 CreatedJSON
{
  "list": {
    "id": "cm3list_new123",
    "name": "Enterprise CTOs",
    "description": "CTOs at companies with 500+ employees",
    "userId": "user_123",
    "leadCount": 0,
    "createdAt": "2026-08-18T10:00:00.000Z",
    "updatedAt": "2026-08-18T10:00:00.000Z"
  }
}

Response Codes

201List created successfully
400Name is required
401Unauthorized

Get Lead List

GET/api/leads/lists/:id

Get a lead list with paginated leads. Supports free-text search, sorting, and server-side attribute filters that hold across pagination.

Query Parameters

ParameterTypeDescription
searchstringSearch leads by name, company, job title, headline, email, or location
pagenumberPage number (default: 1)
limitnumberLeads per page (default: 25, max: 100)
sortBystringSort field: addedAt, fullName, company, jobTitle, email, location, createdAt (default: addedAt)
sortOrderstringasc or desc (default: desc)
filtersstringURL-encoded JSON object of attribute filters, applied server-side (see below)
idsOnlystringSet to 1 to return only the matching lead IDs in display order, without pagination

The filters object supports: photo, email, and phone (each "yes" or "no" for presence), connectionsMin / connectionsMax (numbers, note that LinkedIn reports at most 500), jobTitle (matches job title or headline), company, and country (a country name or 2-letter code). Example: ?filters={"email":"yes","country":"DE"} (URL-encoded).

Request — cURLcURL
curl --location --request GET 'https://outreach.weezly.com/api/leads/lists/:id' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{
  "list": {
    "id": "cm3list789abc",
    "name": "SaaS Founders (West Coast)",
    "description": "Founders of B2B SaaS companies",
    "leadCount": 245
  },
  "leads": [
    {
      "id": "cm3lead_001",
      "firstName": "Sarah",
      "lastName": "Chen",
      "fullName": "Sarah Chen",
      "headline": "CEO at TechStartup Inc.",
      "jobTitle": "CEO",
      "company": "TechStartup Inc.",
      "email": "sarah@techstartup.com",
      "phone": null,
      "location": "San Francisco, CA",
      "linkedinUrl": "https://linkedin.com/in/sarahchen",
      "photoUrl": "https://media.licdn.com/...",
      "connectionStatus": "UNKNOWN",
      "source": "SEARCH",
      "createdAt": "2026-08-01T08:00:00.000Z"
    }
  ],
  "total": 245,
  "page": 1,
  "totalPages": 10
}
Response with idsOnly=1 - 200 OKJSON
{
  "ids": ["cm3lead_001", "cm3lead_002", "cm3lead_003"]
}

Response Codes

200List and leads returned
404List not found
401Unauthorized

Update Lead List

PATCH/api/leads/lists/:id

Update a lead list's name, description, or folder.

Request Body

ParameterTypeDescription
namestringNew name for the list
descriptionstringNew description
folderIdstring|nullMove the list into a folder (null or empty string to unfile it). Allowed regardless of how the list is used.
Request — cURLcURL
curl --location --request PATCH 'https://outreach.weezly.com/api/leads/lists/:id' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "SaaS Founders (Updated)",
  "folderId": "cm3folder123"
}'
Response - 200 OKJSON
{
  "list": {
    "id": "cm3list789abc",
    "name": "SaaS Founders (Updated)",
    "description": "Founders of B2B SaaS companies",
    "folderId": "cm3folder123",
    "leadCount": 245,
    "updatedAt": "2026-08-18T10:05:00.000Z"
  }
}

Response Codes

200List updated, returns the updated list
400Name cannot be empty
404List not found
401Unauthorized

Delete Lead List

DELETE/api/leads/lists/:id

Delete a lead list and all its entries. Leads themselves are not deleted. If any campaigns use the list, the request is rejected with 400 unless you explicitly opt into deleting those campaigns too.

Query Parameters

ParameterTypeDescription
deleteCampaignsstringSet to true to also delete every campaign that uses this list. Running campaigns (ACTIVE or PAUSED) are stopped first, then deleted together with the list. Use with care, this cannot be undone.
Request — cURLcURL
curl --location --request DELETE 'https://outreach.weezly.com/api/leads/lists/:id' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{
  "ok": true,
  "deletedCampaigns": 0
}
Response - 400 Bad Request (list in use by campaigns)JSON
{
  "error": "Cannot delete this list because it is used in: Q3 SaaS Outreach. Remove it from those campaigns first.",
  "campaigns": ["Q3 SaaS Outreach"],
  "campaignDetails": [
    { "id": "cm3camp456", "name": "Q3 SaaS Outreach", "status": "ACTIVE" }
  ]
}

Response Codes

200List deleted (deletedCampaigns reports how many campaigns were removed with it)
400List is used by campaigns and deleteCampaigns=true was not passed
404List not found
401Unauthorized

Add Lead to List

POST/api/leads/lists/:id/leads

Add a lead to the list. All fields are optional, but you should provide at least a name and a LinkedIn URL for outreach to work. If a lead with the same LinkedIn URL already exists for this user, it is linked to the list (and enriched with any new fields) instead of duplicated.

Request Body

ParameterTypeDescription
firstNamestringFirst name
lastNamestringLast name
fullNamestringFull name (derived from first/last name when omitted, and vice versa)
headlinestringLinkedIn headline
jobTitlestringJob title
companystringCompany name
emailstringEmail address
phonestringPhone number
locationstringLocation
linkedinUrlstringLinkedIn profile URL (normalized and used for deduplication)
websitestringCompany website URL, stored on the list entry as a "Website" custom field (used for website personalization)
Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/leads/lists/:id/leads' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "firstName": "Marcus",
  "lastName": "Johnson",
  "headline": "VP Engineering at ScaleUp",
  "company": "ScaleUp Solutions",
  "email": "marcus@scaleup.io",
  "linkedinUrl": "https://linkedin.com/in/marcusjohnson",
  "website": "https://scaleup.io"
}'
Response - 201 CreatedJSON
{
  "lead": {
    "id": "cm3lead_new456",
    "firstName": "Marcus",
    "lastName": "Johnson",
    "fullName": "Marcus Johnson",
    "headline": "VP Engineering at ScaleUp",
    "company": "ScaleUp Solutions",
    "email": "marcus@scaleup.io",
    "linkedinUrl": "https://www.linkedin.com/in/marcusjohnson",
    "source": "MANUAL",
    "createdAt": "2026-08-18T10:00:00.000Z"
  }
}
Response - 200 OK (lead was already in this list)JSON
{
  "lead": { "id": "cm3lead_new456", "fullName": "Marcus Johnson" },
  "alreadyInList": true
}

Response Codes

201Lead added to list
200Lead already existed in this list (alreadyInList: true)
404List not found
401Unauthorized

Remove Lead from List

DELETE/api/leads/lists/:id/leads/:leadId

Remove a lead from the list. The lead record itself is not deleted.

Request — cURLcURL
curl --location --request DELETE 'https://outreach.weezly.com/api/leads/lists/:id/leads/:leadId' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{ "ok": true }

Response Codes

200Lead removed from list
404Lead or list not found
401Unauthorized

Bulk Delete Leads

POST/api/leads/lists/:id/leads/bulk-delete

Remove multiple leads from the list at once.

Request Body

ParameterTypeDescription
leadIdsrequiredstring[]Array of lead IDs to remove
Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/leads/lists/:id/leads/bulk-delete' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "leadIds": [
    "cm3lead_001",
    "cm3lead_002",
    "cm3lead_003"
  ]
}'
Response - 200 OKJSON
{ "deleted": 3 }

Response Codes

200Leads removed from the list
400No lead IDs provided
404List not found

Export Leads

GET/api/leads/lists/:id/export

Export all leads from a list as a CSV file download (not JSON). Columns: Full Name, First Name, Last Name, Headline, Job Title, Company, Email, Phone, Location, LinkedIn URL, plus one column per custom field defined on the list.

Request — cURLcURL
curl --location --request GET 'https://outreach.weezly.com/api/leads/lists/:id/export' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OK (text/csv, Content-Disposition: attachment)CSV
Full Name,First Name,Last Name,Headline,Job Title,Company,Email,Phone,Location,LinkedIn URL
Sarah Chen,Sarah,Chen,CEO at TechStartup Inc.,CEO,TechStartup Inc.,sarah@techstartup.com,,"San Francisco, CA",https://linkedin.com/in/sarahchen

Response Codes

200CSV file returned
404List not found
401Unauthorized

Start Email Finding

POST/api/leads/lists/:id/enrich-emails

Start background email finding for a list. Every lead that has a LinkedIn URL, no email yet, and has never been looked up before is checked against the email enrichment provider. Each lead is only ever looked up once, re-running the endpoint never re-checks leads that were already looked up. Poll the GET endpoint below for progress.

Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/leads/lists/:id/enrich-emails' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{
  "started": true,
  "pending": 42
}

Response Codes

200Run started (started: true) or nothing left to check (started: false, pending: 0)
404List not found
503Background jobs are temporarily unavailable, try again shortly

Email Finding Progress

GET/api/leads/lists/:id/enrich-emails

Progress counts for background email finding on a list. active is true while a run is working the list (lookups recorded in the last 90 seconds and leads still pending).

Request — cURLcURL
curl --location --request GET 'https://outreach.weezly.com/api/leads/lists/:id/enrich-emails' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{
  "withEmail": 120,
  "pending": 30,
  "checkedNoEmail": 55,
  "recentlyChecked": 12,
  "active": true
}

Response Fields

ParameterTypeDescription
withEmailnumberLeads in the list that have an email
pendingnumberLeads with a LinkedIn URL, no email, and no lookup yet
checkedNoEmailnumberLeads that were looked up but no email was found
recentlyCheckednumberLeads looked up in the last 90 seconds
activebooleanWhether a run is currently working this list

Response Codes

200Progress returned
404List not found

Import from LinkedIn Search

POST/api/leads/lists/:id/import-sales-navigator

Import the results of a LinkedIn people search into the list, one page (up to 100 leads) per call. Accepts both classic LinkedIn search URLs (linkedin.com/search/results/people, works with any connected LinkedIn account) and Sales Navigator search URLs (linkedin.com/sales/search/people, requires an account with an active Sales Navigator subscription). The first call sends url, follow-up calls send the cursor from the previous response until it comes back null. Leads are deduplicated by LinkedIn URL.

Request Body

ParameterTypeDescription
accountIdrequiredstringID of the connected LinkedIn account to search with
urlstringThe LinkedIn people-search URL (required on the first call)
cursorstringCursor from the previous response (required on follow-up pages, instead of url)
searchTypestringEcho of the searchType from the previous response ("classic" or "sales_navigator"), send it on cursor pages so leads keep the right source tag
Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/leads/lists/:id/import-sales-navigator' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "accountId": "acc1",
  "url": "https://www.linkedin.com/sales/search/people?query=..."
}'
Response - 200 OKJSON
{
  "imported": 100,
  "cursor": "eyJwYWdlIjoyfQ==",
  "total": 843,
  "searchType": "sales_navigator"
}

Response Codes

200Page imported (cursor is null when there are no more pages)
400Missing accountId, missing url/cursor, or the URL is not a LinkedIn people-search URL
403The LinkedIn account has no active Sales Navigator subscription
404Account or list not found
502The Sales Navigator session for this account has expired, re-sync the account and retry

Import LinkedIn Post Reactions

POST/api/leads/lists/:id/import-post

Import the people who reacted to a LinkedIn post into the list, one page (around 50 reactors) per call. The first call (page 1) also fetches the post itself and stores it on the list. Send increasing page values until page exceeds totalPages. Only person profiles are imported (company pages that reacted are skipped), deduplicated by LinkedIn URL.

Request Body

ParameterTypeDescription
urlrequiredstringThe LinkedIn post URL (contains linkedin.com/posts/ or linkedin.com/feed/)
pagenumberPage number (default: 1)
Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/leads/lists/:id/import-post' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "url": "https://www.linkedin.com/posts/janedoe_activity-7231...",
  "page": 1
}'
Response - 200 OKJSON
{
  "imported": 48,
  "page": 1,
  "totalPages": 4,
  "total": 187,
  "post": {
    "url": "https://www.linkedin.com/posts/janedoe_activity-7231...",
    "text": "Excited to announce...",
    "authorName": "Jane Doe"
  }
}

Response Codes

200Page imported (post is only returned on page 1)
400Missing or invalid LinkedIn post URL
404List not found
503Post import is not available on this deployment

Update Lead

PATCH/api/leads/:leadId

Update a lead's fields. Name fields are kept consistent: providing only fullName derives first/last name, and vice versa.

Request Body

ParameterTypeDescription
firstNamestringFirst name
lastNamestringLast name
fullNamestringFull name
headlinestringLinkedIn headline
jobTitlestringJob title
companystringCompany name
emailstringEmail address
phonestringPhone number
locationstringLocation
linkedinUrlstring|nullLinkedIn profile URL (normalized; null or empty string clears it)
Request — cURLcURL
curl --location --request PATCH 'https://outreach.weezly.com/api/leads/:leadId' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "email": "sarah@newemail.com",
  "jobTitle": "Founder & CEO"
}'
Response - 200 OKJSON
{
  "lead": {
    "id": "cm3lead_001",
    "firstName": "Sarah",
    "lastName": "Chen",
    "email": "sarah@newemail.com",
    "jobTitle": "Founder & CEO",
    "updatedAt": "2026-08-18T10:05:00.000Z"
  }
}

Response Codes

200Lead updated
404Lead not found
401Unauthorized

Lead List Folders

Organize lead lists into folders. Move a list into a folder with PATCH /api/leads/lists/:id and the folderId field. Deleting a folder never deletes its lists, they just become unfiled.

List Folders

GET/api/lead-list-folders

Retrieve all lead list folders for the authenticated user, in display order.

Request — cURLcURL
curl --location --request GET 'https://outreach.weezly.com/api/lead-list-folders' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{
  "folders": [
    {
      "id": "cm3folder123",
      "name": "Outbound Q3",
      "emoji": "🚀",
      "position": 0,
      "_count": { "leadLists": 4 }
    }
  ]
}

Response Codes

200Folders returned
401Unauthorized

Create Folder

POST/api/lead-list-folders

Create a new lead list folder. Folder names must be unique per user.

Request Body

ParameterTypeDescription
namerequiredstringFolder name
emojistringFolder emoji (defaults to 📁)
Request — cURLcURL
curl --location --request POST 'https://outreach.weezly.com/api/lead-list-folders' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Outbound Q3",
  "emoji": "🚀"
}'
Response - 201 CreatedJSON
{
  "folder": {
    "id": "cm3folder123",
    "name": "Outbound Q3",
    "emoji": "🚀",
    "position": 0,
    "_count": { "leadLists": 0 }
  }
}

Response Codes

201Folder created
400Folder name is required
409A folder with this name already exists

Update Folder

PATCH/api/lead-list-folders/:id

Rename a folder or change its emoji.

Request Body

ParameterTypeDescription
namestringNew folder name
emojistringNew folder emoji
Request — cURLcURL
curl --location --request PATCH 'https://outreach.weezly.com/api/lead-list-folders/:id' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Outbound Q4"
}'
Response - 200 OKJSON
{ "success": true }

Response Codes

200Folder updated
404Folder not found
409A folder with this name already exists

Delete Folder

DELETE/api/lead-list-folders/:id

Delete a folder. The lists inside it are not deleted, they become unfiled.

Request — cURLcURL
curl --location --request DELETE 'https://outreach.weezly.com/api/lead-list-folders/:id' \
  --header 'X-API-KEY: sk_live_YOUR_API_KEY'
Response - 200 OKJSON
{ "success": true }

Response Codes

200Folder deleted
404Folder not found