Create and enroll prospects with the WarmySender API
How to add prospects to your account and enroll them into a campaign programmatically with the WarmySender REST API — the scopes you need, working curl examples, and the three ways to choose who gets enrolled.
Overview
WarmySender is a 4-pillar outreach platform — Cold Emailing, Email Warmup, LinkedIn Outreach, and Multichannel sequences. The public API lets you feed prospects in from your own systems — a CRM, a form, a workflow tool — and enroll them into a campaign, so your outreach starts without anyone copy-pasting a spreadsheet.
This page covers the two endpoints that do exactly that:
- Create a prospect —
POST /api/v1/prospects(requires theprospects:writescope) - Enroll prospects into a campaign —
POST /api/v1/campaigns/{campaignId}/enrollments(requires theenrollments:writescope)
For the complete endpoint catalogue, error codes, and webhooks, see the full API reference, or import the machine-readable spec at /api/v1/openapi.json.
Step 1 — Create an API key with the right permissions
Go to Settings → API Keys and create a key. When you create it, tick the permissions (scopes) this integration needs:
prospects:write— to create prospects.enrollments:write— to enroll prospects into a campaign.
The full key is shown once, at creation time — copy it and store it somewhere safe (a secrets manager, not your code). Your key starts with ws_. You send it on every request as a Bearer token in the Authorization header:
Authorization: Bearer ws_your_api_key_here
Paste the key exactly — no leading or trailing spaces and no line breaks. A key that was copied with an extra space or a wrapped newline will be rejected and your requests won't connect. A quick way to confirm a key has actually been used: in Settings → API Keys each key shows a "last used" indicator. If your key still says "Never used" after you've sent a request, the request never reached us as that key — almost always a stray space or line break in the header, or the key pasted into the wrong place. See the FAQ on "Never used" below.
Step 2 — Create a prospect
POST /api/v1/prospects
Send at least an email. firstName, lastName, company, role, phone, linkedinUrl, and a customFields object are all optional. The linkedinUrl field lets the same prospect be used for multichannel (email + LinkedIn) outreach.
curl -X POST https://warmysender.com/api/v1/prospects \
-H "Authorization: Bearer ws_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"firstName": "John",
"lastName": "Smith",
"company": "Acme",
"role": "VP Sales",
"linkedinUrl": "https://www.linkedin.com/in/johnsmith",
"customFields": { "source": "webinar-2026" }
}'
A successful create returns 201 Created with the prospect. If a prospect with that email already exists in your account, the existing prospect is reused and returned rather than creating a duplicate — so you can safely send the same prospect more than once without ending up with two records. Hold on to the returned id if you plan to enroll by ID in the next step.
{
"data": {
"id": "prs_a1b2c3d4",
"email": "[email protected]",
"firstName": "John",
"lastName": "Smith",
"company": "Acme",
"status": "active"
},
"meta": { "request_id": "req_9f8e7d" }
}
Step 3 — Enroll prospects into a campaign
POST /api/v1/campaigns/{campaignId}/enrollments
Add prospects to a specific campaign. The request body lets you choose who to enroll in three ways — pass at least one, and you can combine them in a single request:
prospectIds- An array of prospect
idvalues (for example theidyou got back from Step 2) — enrolls those specific prospects. emails- An array of email addresses — WarmySender finds the matching prospects in your account and enrolls them. Handy when you have the emails but not the IDs.
listIds- An array of list
idvalues — enrolls everyone in those lists. The simplest way to enroll a whole audience at once.
Enroll by prospect ID:
curl -X POST https://warmysender.com/api/v1/campaigns/cmp_123/enrollments \
-H "Authorization: Bearer ws_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "prospectIds": ["prs_a1b2c3d4", "prs_e5f6g7h8"] }'
Enroll by email (WarmySender looks up the prospects for you):
curl -X POST https://warmysender.com/api/v1/campaigns/cmp_123/enrollments \
-H "Authorization: Bearer ws_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "emails": ["[email protected]", "[email protected]"] }'
Enroll everyone in one or more lists:
curl -X POST https://warmysender.com/api/v1/campaigns/cmp_123/enrollments \
-H "Authorization: Bearer ws_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{ "listIds": ["lst_marketing_q2"] }'
The response tells you how many were enrolled. Prospects that are already enrolled in the campaign are skipped (counted as processed but not enrolled again), so re-running the same enrollment is safe.
{
"data": { "enrolledCount": 2, "totalProcessed": 2 },
"meta": { "request_id": "req_4c5d6e" }
}
A note on lifecycle: enrolling prospects adds them to the campaign, but it does not start a draft campaign by itself — a campaign sends on its own schedule once it's running. You can create and manage the campaign itself over the API too; see Managing campaigns via API for the create / start / pause flow.
Scopes at a glance
prospects:write- Required to create prospects (
POST /api/v1/prospects). enrollments:write- Required to enroll prospects into a campaign (
POST /api/v1/campaigns/{campaignId}/enrollments).
If a key is missing a scope the call needs, the request is rejected with a clear "insufficient scope" response. The fix is to create a new key in Settings → API Keys with the missing permission ticked — for safety, scopes are chosen at creation time. Grant only the scopes an integration actually needs.
Machine-readable spec and full reference
Two resources cover everything on the public API:
/api/v1/openapi.json— the OpenAPI specification. Import it into Postman, Insomnia, an SDK generator, or any tool that reads OpenAPI to get every endpoint, request body, and response shape automatically.- Full API reference — human-readable docs for every endpoint, authentication, idempotency, rate limits, error codes, and webhooks.
Frequently asked questions
Which permissions do I need to create and enroll prospects?
Two scopes: prospects:write to create prospects with POST /api/v1/prospects, and enrollments:write to enroll them with POST /api/v1/campaigns/{campaignId}/enrollments. Tick both when you create the key in Settings → API Keys. If a key is missing a scope, the matching request is rejected with an insufficient-scope response, and the fix is to create a new key with that permission ticked.
My API key shows "Never used" — what's wrong?
If a key still shows "Never used" after you've sent a request, the request never reached us as that key. The most common cause is the key being pasted with a stray space or a line break, so the Authorization header is malformed. Re-copy the key, paste it exactly with nothing before or after it, and make sure the header reads Authorization: Bearer ws_... on a single line. Also confirm you're sending it as a Bearer token in the Authorization header (not in the URL or a different header). Once a real request lands, the "last used" time updates.
What can I put in the enrollment request body?
Pass at least one of three selectors, and you can combine them: prospectIds (an array of prospect IDs), emails (an array of email addresses, which WarmySender matches to existing prospects for you), or listIds (an array of list IDs, which enrolls everyone in those lists). Enrolling by list is the quickest way to add a whole audience; enrolling by email is handy when you have addresses but not IDs.
What happens if I enroll a prospect that's already in the campaign?
Nothing harmful — already-enrolled prospects are skipped. The response counts them under totalProcessed but they aren't enrolled a second time, so re-running the same enrollment request is safe and won't cause duplicate outreach.
If I create a prospect that already exists, do I get a duplicate?
No. Creating a prospect with an email that already exists in your account reuses and returns the existing prospect rather than making a second record. That means you can re-send the same prospect from your CRM without worrying about duplicates building up.
Where's the machine-readable spec?
The OpenAPI specification is public at /api/v1/openapi.json — import it into Postman, Insomnia, or an SDK generator to get every endpoint and request/response shape automatically. For human-readable docs covering authentication, rate limits, error codes, and webhooks, see the full API reference at /docs/integrations.
Related documentation
- Full API reference — every endpoint, authentication, idempotency, rate limits, and error codes.
- OpenAPI specification — the machine-readable spec you can import into your tooling.
- Managing campaigns via API — create, update, start, and pause campaigns programmatically.
- API quickstart — your first request in five minutes.
Building an integration and stuck on something here? Email [email protected] with the request you are sending and the response you got back, and we will help you wire it up.