curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/lists/hot/members" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"slugs":["derek-morrow"],"stage":"to-contact","notes":"Review after demo"}'
const response = await fetch("https://api.freshtalent.ai/v1/lists/hot/members", {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ slugs: ["derek-morrow"], stage: "to-contact" }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const body = await response.json();
console.log(`processed ${body.meta.added}; missing ${body.meta.missing.join(", ")}`);
import json
import os
from urllib.request import Request, urlopen
payload = json.dumps({"urls": ["https://www.linkedin.com/in/derek-morrow"], "stage": "contacted"}).encode()
request = Request(
"https://api.freshtalent.ai/v1/lists/hot/members",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["meta"], body["data"])
{
"data": [
{
"id": "8c5f6a6b-5cb7-4e3f-a1fd-9aabf2d2c6e0",
"list_id": "550e8400-e29b-41d4-a716-446655440000",
"person_linkedin_url": "https://www.linkedin.com/in/derek-morrow",
"person_slug": "derek-morrow",
"person_full_name": "Derek Morrow",
"person_headline": null,
"person_picture_url": null,
"stage": "to-contact",
"notes": "",
"added_at": "2026-09-07T10:00:00.000Z",
"updated_at": "2026-09-07T10:00:00.000Z"
}
],
"meta": {
"added": 1,
"missing": []
}
}
Add list members
Upsert one or more people into a custom list by LinkedIn slug or URL.
POST
/
lists
/
{id}
/
members
curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/lists/hot/members" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"slugs":["derek-morrow"],"stage":"to-contact","notes":"Review after demo"}'
const response = await fetch("https://api.freshtalent.ai/v1/lists/hot/members", {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ slugs: ["derek-morrow"], stage: "to-contact" }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const body = await response.json();
console.log(`processed ${body.meta.added}; missing ${body.meta.missing.join(", ")}`);
import json
import os
from urllib.request import Request, urlopen
payload = json.dumps({"urls": ["https://www.linkedin.com/in/derek-morrow"], "stage": "contacted"}).encode()
request = Request(
"https://api.freshtalent.ai/v1/lists/hot/members",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["meta"], body["data"])
{
"data": [
{
"id": "8c5f6a6b-5cb7-4e3f-a1fd-9aabf2d2c6e0",
"list_id": "550e8400-e29b-41d4-a716-446655440000",
"person_linkedin_url": "https://www.linkedin.com/in/derek-morrow",
"person_slug": "derek-morrow",
"person_full_name": "Derek Morrow",
"person_headline": null,
"person_picture_url": null,
"stage": "to-contact",
"notes": "",
"added_at": "2026-09-07T10:00:00.000Z",
"updated_at": "2026-09-07T10:00:00.000Z"
}
],
"meta": {
"added": 1,
"missing": []
}
}
Adds memberships to a custom list. Slugs are looked up in the people graph; an unknown slug is reported in
At least one of
Each
meta.missing and does not create a membership. URLs are accepted even when the graph lookup does not find a person, so an arbitrary non-empty URL can create a membership with nullable person metadata.
The operation processes slugs first and urls second. It is not transactional: a later lookup or database failure can leave earlier items written. Each item is an upsert keyed by the exact trimmed person_linkedin_url within the list.
string
required
List UUID or case-insensitive name. Default lists reject writes with
403; add members only to a custom list.string[]
Optional LinkedIn public identifiers. If present, 1–100 non-validated strings. Unknown slugs are returned in
meta.missing.string[]
Optional URL strings, 1–100 items. The Gateway trims each value, extracts a slug from
/in/<slug> when present, and otherwise stores the trimmed string as the membership URL. Empty trimmed values are skipped.string
Optional enum:
to-contact (default), contacted, responded, or passed.string
Optional notes, up to 2,000 characters. Defaults to
"" and is not nullable in the stored/API membership shape.slugs or urls must contain a processable value. The body allows both arrays; the per-array maximum is 100, so a request can contain up to 200 entries. Only documented fields are supported; the framework strips unknown top-level fields.
Partial success and repeat calls
If the exact URL already belongs to the list, the existing membership is updated rather than duplicated. The suppliedstage and notes always overwrite the existing values, including the defaults when those fields are omitted. Repeating a request can therefore reset a member’s stage to to-contact and notes to "". Enrichment fields are updated only when the new lookup supplies non-null values.
Response schema
A successful request returns201:
| Field | Type | Nullability and meaning |
|---|---|---|
data | object[] | One membership result per successfully processed slug or URL, including upserts. |
meta.added | integer | Number of returned membership objects. Despite the name, this includes updates. |
meta.missing | string[] | Unknown slugs only. URL misses are not included. |
data item uses the membership shape from Get list: UUID id/list_id, required person_linkedin_url, nullable person enrichment fields, stage enum, string notes, and ISO added_at/updated_at timestamps.
{
"data": [
{
"id": "8c5f6a6b-5cb7-4e3f-a1fd-9aabf2d2c6e0",
"list_id": "550e8400-e29b-41d4-a716-446655440000",
"person_linkedin_url": "https://www.linkedin.com/in/derek-morrow",
"person_slug": "derek-morrow",
"person_full_name": "Derek Morrow",
"person_headline": null,
"person_picture_url": null,
"stage": "to-contact",
"notes": "",
"added_at": "2026-09-07T10:00:00.000Z",
"updated_at": "2026-09-07T10:00:00.000Z"
}
],
"meta": {
"added": 1,
"missing": []
}
}
curl --fail-with-body --max-time 30 -X POST "https://api.freshtalent.ai/v1/lists/hot/members" \
-H "Authorization: Bearer $FRESHTALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"slugs":["derek-morrow"],"stage":"to-contact","notes":"Review after demo"}'
const response = await fetch("https://api.freshtalent.ai/v1/lists/hot/members", {
method: "POST",
signal: AbortSignal.timeout(30_000),
headers: {
Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ slugs: ["derek-morrow"], stage: "to-contact" }),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
const body = await response.json();
console.log(`processed ${body.meta.added}; missing ${body.meta.missing.join(", ")}`);
import json
import os
from urllib.request import Request, urlopen
payload = json.dumps({"urls": ["https://www.linkedin.com/in/derek-morrow"], "stage": "contacted"}).encode()
request = Request(
"https://api.freshtalent.ai/v1/lists/hot/members",
data=payload,
method="POST",
headers={
"Authorization": f"Bearer {os.environ['FRESHTALENT_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request, timeout=30) as response:
body = json.load(response)
print(body["meta"], body["data"])
Errors, reconciliation, and retry safety
400: invalid body, invalid stage, no processableslugs/urls, or a missing platform-keyX-FreshTalent-Org-IdUUID. Fix the request/header. A body containing an unknown slug can still return201with that slug inmeta.missing.401(unauthorized): provide a valid API key.402(payment_required): restore entitlement.403(forbidden): the addressed list is a Gateway-managed default list; use a custom list.404(not_found): the list UUID/name is not visible to the organization.429or5xx: the outcome can be unknown and the request can be partially applied. Do not blindly replay a request that includes stage/notes. FirstGET /lists/{id}and reconcile by exactperson_linkedin_url; retry only missing items with the intended stage and notes.
next link. The batch is bounded by the request arrays, not paginated.