Guides
Lead management
Add, import, fix, and move people in a campaign.
By the end of this guide you can add people to a campaign, fix missing personalization fields, import a list (sync or async), and move existing people between campaigns.
A person added to a campaign is a lead in that campaign. Read Leads and people first if those words are new.
Replace f_your_key_here and the example ids with your own.
Before you start
- A campaign with an email sequence. The Campaign setup guide builds one.
- If the sequence uses custom fields (for example
{{custom.company}}), every person you add must include those keys incustom_lead_dataor the request is rejected / the lead is marked incomplete.
1. Add one person
Include every custom field the sequence requires:
curl -sS -X POST 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads' \
-H 'Authorization: Bearer f_your_key_here' \
-H 'Content-Type: application/json' \
-d '
{
"email": "alex@acme.com",
"first_name": "Alex",
"last_name": "Rivera",
"custom_lead_data": {
"company": "Acme Corp"
}
}
'Success: the response includes the lead id and the person's global_lead_id. If the campaign is already running, they enter the sequence when enrollment succeeds.
2. Fix or remove a person
Update fields (for example a missing company name) with PATCH:
curl -sS -X PATCH 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads/f2a1b9c4-8e7d-4a3b-9c1e-2d5f6a7b8c9d' \
-H 'Authorization: Bearer f_your_key_here' \
-H 'Content-Type: application/json' \
-d '{
"first_name": "Alexandra",
"custom_lead_data": {
"company": "Acme Corp"
}
}'Remove them from this campaign only:
curl -sS -X DELETE 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads/f2a1b9c4-8e7d-4a3b-9c1e-2d5f6a7b8c9d' \
-H 'Authorization: Bearer f_your_key_here'That does not delete the underlying person from your account. They stay available for other campaigns.
3. Import a list (up to 100)
Use sync bulk when the batch fits in one request (max 100 people):
curl -sS -X POST 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads/bulk' \
-H 'Authorization: Bearer f_your_key_here' \
-H 'Content-Type: application/json' \
-d '
{
"leads": [
{
"email": "alex@acme.com",
"first_name": "Alex",
"custom_lead_data": {
"company": "Acme Corp"
}
},
{
"email": "sam@example.com",
"first_name": "Sam",
"custom_lead_data": {
"company": "Example Inc"
}
}
]
}
'Success: the response reports imported, incomplete, failed, and an errors array. incomplete means the lead was created but is missing required personalization fields — fix those with PATCH before you expect emails to send.
4. Import a large list (async job)
For bigger lists, queue a job (up to 1000 people per request, and up to 3 jobs running at once):
curl -sS -X POST 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads/bulk/async' \
-H 'Authorization: Bearer f_your_key_here' \
-H 'Content-Type: application/json' \
-d '
{
"leads": [
{
"email": "alex@acme.com",
"first_name": "Alex",
"custom_lead_data": {
"company": "Acme Corp"
}
}
]
}
'Poll until the job finishes:
curl -sS 'https://api.getfurnace.io/v1/jobs/9f8e7d6c-5b4a-3210-9876-543210fedcba' \
-H 'Authorization: Bearer f_your_key_here'Success: status is completed (or failed). Counts live in result (created, updated, incomplete, failed, and so on). Prefer webhooks for completion if you already use them — see Webhook integration.
5. Move existing people between campaigns
When people already exist in your account (you have their global_lead_id), add them to another campaign without re-posting full contact rows:
curl -sS -X POST 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads:add' \
-H 'Authorization: Bearer f_your_key_here' \
-H 'Content-Type: application/json' \
-d '{
"global_lead_ids": [
"sha256:example-person-id"
]
}'Remove them from a campaign with POST /v1/campaigns/{id}/leads:remove, or from every campaign with POST /v1/leads:remove-from-all-campaigns.
Success: the response counts created / enrolled / skipped (add) or removed / skipped (remove).
Common mistakes
| Symptom | Likely cause |
|---|---|
| Request rejected or lead incomplete | Missing a required custom_lead_data key from the sequence |
| Person never receives email | Campaign still draft (launch it), no mailbox assigned, or enrollment paused |
| Bulk import times out / too large | Use /leads/bulk/async and poll GET /v1/jobs/{id} |
| DELETE removed them from one campaign only | Expected — the account person remains for other campaigns |
6. Tags and email verification
Tags are account-level and person-keyed. They describe the person (global_lead_id), not one campaign lead row. custom_lead_data stays per campaign.
Pass tags as names or aliases. Furnace matches the built-in catalog case-insensitively (for example Hunter.io → Hunter, GMB → Google Maps) and find-or-creates an account-owned tag when nothing matches. Built-in catalog tags cannot be deleted.
Provider (how you found them): Apollo, Hunter, Prospeo, Clay, Apify, Serper, Google Maps, LinkedIn Sales Navigator, Website Crawl, SkipSherpa, Meta Ad Library, LinkedIn Ad Library, Google Ads Transparency, Business Registry, License Roster, HubSpot, Salesforce, Client CSV, Webinar Registrant List, Demo Request, Content Download, Referral.
Signal (why they are in the list): Running Meta Ads, Running Google Ads, Running LinkedIn Ads, Webinar Or Event Ad, Qualifying Ad Copy, Ads Paused, Hiring Intent, Recently Funded, Tech Match, Regulated Or Licensed, Verified Business, ICP Fit, Decision Maker, Owner Operator, Local Business, Engaged, Webinar Attendee, Webinar No Show.
Other (operator filters): Catch-All Domain, Role Account, Needs Review, Do Not Send, Existing Customer, Open Opportunity, Previously Contacted, High Fit, Low Fit, EU Contact.
If you already have MillionVerifier (or similar) output, send email_verification with status of ok, catch_all, invalid, unknown, or disposable. Do not auto-apply Catch-All Domain or Role Account tags from that payload — those tags are for operators who want them in filters. Omit email_verification unless you have a real check. Do not invent a status.
Bulk and staged import use the same LeadCreate fields. Omitting them behaves as before.
curl -sS -X POST 'https://api.getfurnace.io/v1/campaigns/1d8dc901-3d2d-4d9f-9dcc-4f8b3aa1a1fb/leads' \
-H 'Authorization: Bearer f_your_key_here' \
-H 'Content-Type: application/json' \
-d '
{
"email": "alex@acme.com",
"first_name": "Alex",
"custom_lead_data": {
"company": "Acme Corp"
},
"tags": [
"Hunter",
"ICP Fit"
],
"email_verification": {
"status": "ok",
"provider": "millionverifier"
}
}
'Next
- Read replies in Handling replies.
- Field-level detail is in the API Reference.