Reference
Adding leads
Source people however you like, then let Gelee enrich them, filter them and run the outreach. Works best with an evergreen campaign you top up continuously. Needs a Read + write key.
Push leads into a campaign
Up to 100 leads a call. The only required field is the LinkedIn URL.
{{custom.key}}. See merge fields.The lead is saved at once, but enrichment and the campaign's full filters resolve later, from minutes to hours. Anything we can decide on the spot comes back in rejected right away.
Safe to retry. Leads are keyed on campaign plus LinkedIn profile, so sending a batch again returns the same lead_id values with created: false and creates nothing new.
curl -X POST "https://www.gelee.ai/api/v1/campaigns/9a2c…/leads" \
-H "Authorization: Bearer gk_your_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: my-run-42" \
-d '{
"leads": [
{
"linkedin_url": "https://www.linkedin.com/in/janedoe",
"first_name": "Jane",
"last_name": "Doe",
"company": "Acme",
"title": "VP Sales",
"source": "reddit-monitor",
"custom_fields": {
"report_link": "https://acme.example/r/5d70"
},
"tags": [
"Series A"
]
}
]
}'{ "batch_id": "my-run-42",
"campaign_id": "9a2c…",
"accepted": [ { "lead_id": "5d70…", "linkedin_id": "janedoe",
"name": "Jane Doe", "created": true } ],
"rejected": [],
"counts": { "received": 1, "accepted": 1, "rejected": 0, "duplicates": 0 } }Why a lead was turned away
| reason | Meaning |
|---|---|
invalid_linkedin_url | Not a LinkedIn profile URL, handle or member id. |
duplicate_in_request | The same person appeared twice in one call. |
blacklisted | On your do-not-contact list, by profile or by company. |
already_in_another_campaign | Already being worked in another of your campaigns. |
hard_filter | Excluded by this campaign's filters. Comes with a lead_id and the exact reason. |
invalid_fields | A custom field or tag was not valid. detail names it. |
Your do-not-contact list and the check for people already in another campaign always apply. No setting turns them off.
Check how a push landed
The whole batch at once, instead of polling each lead. GET /leads/{id} returns one lead in exactly this shape, with the whole story.
| filter_verdict | Meaning |
|---|---|
pending | Not enriched yet, so the filters have not had the data to judge. |
accepted | Enriched and cleared for outreach. |
rejected | Excluded. filter_reason says why. |
filter_reason is always worked out against your filters as they stand now. A rejected lead with filter_unknown_only: true failed only on data we never learned, not on a proven mismatch. To be told instead of asking, subscribe to the lead_accepted and lead_rejected webhooks: they fire once per pushed lead when the verdict lands.
curl "https://www.gelee.ai/api/v1/campaigns/9a2c…/leads?batch_id=my-run-42" \
-H "Authorization: Bearer gk_your_key"{ "data": [
{ "lead_id": "5d70…", "campaign_id": "9a2c…", "linkedin_id": "janedoe",
"name": "Jane Doe", "status": "active", "contacted": false,
"enrichment_status": "done",
"filter_verdict": "accepted", "filter_reason": null,
"batch_id": "my-run-42", "source": "api_intake",
"company": "Acme", "role": "VP Sales",
"linkedin_url": "https://www.linkedin.com/in/janedoe", …,
"created_at": "…", "updated_at": "…" }
],
"pagination": { "limit": 50, "offset": 0, "total": 1 } }No-code intake URL
If your tool can only paste a URL (Zapier, Make, Clay, a form), use a campaign's intake URL instead. No key, no headers, no body shape to match. Make it in the campaign's Settings, under Lead intake URL.
curl -X POST "https://web.gelee.ai/api/webhooks/leads/YOUR_URL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"linkedin_url":"https://www.linkedin.com/in/janedoe",
"first_name":"Jane","company":"Acme","title":"VP Sales"}'It reads whatever your tool sends. Field names are matched loosely, ignoring case, spaces, underscores and hyphens.
| We look for | Names that work |
|---|---|
| The profile (required) | linkedin_url, linkedin, profile_url, url, link, public_identifier, Person Linkedin Url |
| The person | first_name, last_name, name, full_name, display_name |
| The company | company, company_name, organization, employer, account_name |
| The role | title, job_title, role, position |
| The rest | email, location, city, country, headline, bio, source, list_name |
Send one object, a bare list, { "leads": [ … ] }, { "data": [ … ] } or a nested { "body": … }. Up to 100 people a call. Extra columns are kept on the lead. Rows we could not read come back counted as unreadable, so a partial success is never mistaken for a full one. Everything after that works like the endpoint above. A GET on the URL returns a short note, so pasting it in a browser tells you it is alive.
Anyone holding it can add people to that one campaign. It cannot read anything and works for nothing else. Replace or revoke it in the campaign's settings and the old one stops working at once.