---
title: "POST /api/lead-lists/{leadListId}/source"
method: POST
path: "/api/lead-lists/{leadListId}/source"
tags: ["sources"]
---

# POST /api/lead-lists/{leadListId}/source

`POST /api/lead-lists/{leadListId}/source`

## Request

- Base URL: `https://app.heykairon.com` (the document declares no server; this is the origin it was published from)
- URL: `https://app.heykairon.com/api/lead-lists/{leadListId}/source`
- Auth: API key in header `x-api-key`

## Path parameters

- `leadListId` string, required

## Request body

- CreateSourceBodyDto
  - `icpId` string, required
  - `signals` object[], required
    - `signalKey` 'changed_jobs' | 'posted_recently' | 'posted_with_keyword' | 'viewed_my_profile' | 'recently_connected' | 'engaged_with_profile' | 'engaged_with_keyword' | 'engaged_with_post' | 'company_raised_funds' | 'company_leadership_change' | 'parked_lead_news' | 'lookalike' | 'company_followers' | 'company_hiring', required
    - `config` object
  - `contactFilter` object
    - `buckets` string[], required: Which contact-history buckets may enroll: `new` (no conversation either way, and nothing a Kairon campaign sent), `unanswered` (we messaged, no reply), `in_conversation` (they wrote to us). Unique, at least one. All three means enroll everyone. Anyone outside the selected buckets is skipped, not queued, so a narrow set is why a large audience enrolls almost nobody.
    - `scope` 'seat' | 'org', required: Whose prior-contact history is read: `seat` (this campaign's sending seat) or `org` (every LinkedIn seat). Copying a `seat` filter onto a DIFFERENT seat changes what it means — the new seat's history is empty, so people the original filter excluded now enroll.
  - `acceptExtraListCharge` boolean
  - `seatId` string

## Response `201`

---

[API](https://skmtc.dev/heykairon/apis/kairon-api.md) · [All operations](https://skmtc.dev/heykairon/apis/kairon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/heykairon/apis/kairon-api/revisions/35de550d5938?raw)
