API reference
Sourced profiles
Candidates your customer went out and found, rather than ones who applied. Same shape of person, very different consent footing — and the payload says so on every row.
GET/sourced-profilessourced_profiles:read
| Parameter | Type | Notes |
|---|---|---|
origin | string | Where the profile came from: coresignal, xray, import or github. |
has_contact_info | boolean | Only profiles that do (or do not) have contact data held against them. Cheap way to avoid hydrating rows that have nothing to hydrate. |
q | string | Search by name, title or company. |
updated_after | RFC 3339 | For incremental sync. |
limit | integer | Default 25, max 100. |
cursor | string | From the previous page. |
{
"object": "list",
"data": [
{
"object": "sourced_profile",
"id": "cnd_8Fj3kLm2Qd7s",
"workspace_id": "wsp_4Kd8sPm2Qx7L",
"full_name": "Minjae Park",
"headline": "Staff Engineer, Payments",
"job_title": "Staff Software Engineer",
"company_name": "Toss",
"company_industry": "Financial Services",
"company_website": "https://toss.im",
"company_linkedin_url": "https://www.linkedin.com/company/viva-republica",
"department": "Engineering",
"management_level": "individual_contributor",
"location_full": "Seoul, South Korea",
"location_country": "KR",
"linkedin_url": "https://www.linkedin.com/in/minjae-park-9x8y7z",
"photo_url": null,
"summary": "Twelve years building payment infrastructure ...",
"total_experience_months": 148,
"connections_count": 1840,
"follower_count": 2210,
"match_score": 91,
"match_tier": "strong",
"created_at": "2026-08-19T07:41:02.115Z",
"updated_at": "2026-09-06T12:08:55.400Z",
"has_contact_info": true,
"has_resume": false,
"has_business_card": false,
"has_profile_photo": true,
"has_linkedin_capture": true,
"provenance": {
"origin": "sourced",
"acquisition": "provider_search",
"provider": "coresignal",
"owner_workspace_id": "wsp_4Kd8sPm2Qx7L",
"first_seen_at": "2026-08-19T07:41:02.115Z",
"last_seen_at": "2026-09-06T12:08:55.400Z",
"consent": {
"basis": "legitimate_interest_sourcing",
"candidate_facing_notice": false,
"unsubscribed": false,
"suppressed": false
},
"licence": {
"redistributable": false,
"note": "Provider-derived. Not licensed for onward redistribution."
}
},
"links": {
"self": "/api/partner/v1/sourced-profiles/cnd_8Fj3kLm2Qd7s",
"contact_info": "/api/partner/v1/sourced-profiles/cnd_8Fj3kLm2Qd7s/contact-info",
"attachments": "/api/partner/v1/sourced-profiles/cnd_8Fj3kLm2Qd7s/attachments",
"list_memberships": "/api/partner/v1/sourced-profiles/cnd_8Fj3kLm2Qd7s/list-memberships"
}
}
],
"has_more": true,
"next_cursor": "cur_eyJrIjoiMjAyNi0wOC0xOVQwNzo0MTowMloi..."
}match_score and match_tier are Tahoe’s assessment against the search that found this person, not a general quality rating. They are meaningless outside that context, so do not display one as a standalone score.
GET/sourced-profiles/{profile_handle}sourced_profiles:read
One profile, same shape as a list row.
GET/sourced-profiles/{profile_handle}/contact-infocontact:read
Only values this workspace has already revealed. This endpoint does not reveal anything new, does not call a provider, and does not spend your customer’s credits. If a field was never unlocked, it is reported as such rather than fetched on demand.
{
"object": "contact_info",
"subject": { "object": "sourced_profile", "id": "cnd_8Fj3kLm2Qd7s" },
"emails": [
{ "value": "[email protected]", "kind": "work", "status": "found" }
],
"phones": [],
"unsubscribed": false,
"field_states": {
"work_email": "found",
"personal_email": "not_found",
"phone": "not_found"
}
}Here phones is empty and phone is not_found with nothing in restricted — that means Tahoe genuinely does not hold a phone number, not that one is being withheld. Had the scope been missing, phones would have been named in restricted instead. The difference is the whole reason both fields exist.
Each disclosed value consumes one unit of the daily personal-data budget and writes an audit row naming your key, the record and the field. Hydrate contact details when someone is about to use them, not for every profile at sync time.
GET/sourced-profiles/{profile_handle}/attachmentsattachments:read
The index of files held against this profile — resume, business_card and profile_photo. Files whose own scope you lack are still listed, with readable: false and the scope named.
{
"object": "list",
"data": [
{
"object": "attachment",
"type": "profile_photo",
"filename": "minjae.jpg",
"mime": "image/jpeg",
"bytes": 48120,
"uploaded_at": "2026-08-19T07:41:04.882Z",
"required_scope": "attachments:read",
"readable": true,
"links": {
"content": "/api/partner/v1/sourced-profiles/cnd_8Fj3kLm2Qd7s/attachments/profile_photo/content"
}
},
{
"object": "attachment",
"type": "resume",
"filename": "minjae-park.pdf",
"mime": "application/pdf",
"bytes": 210444,
"uploaded_at": "2026-08-21T10:02:19.301Z",
"required_scope": "resume:download",
"readable": false
}
],
"has_more": false,
"next_cursor": null
}The links.content key is present only when readable is true, so a client that follows links will never construct a request it is not allowed to make. Listing the unreadable entry anyway is deliberate: knowing a resume exists but needs another scope is more useful than the row silently vanishing.
GET/sourced-profiles/{profile_handle}/attachments/{kind}/contentattachments:read
Download one attachment. In the download rate-limit tier (30/minute). A resume needs resume:download as well — the attachment index tells you which scope each file wants in required_scope.
curl -s "https://tahoe.workonward.com/api/partner/v1/sourced-profiles/cnd_8Fj3kLm2Qd7s/attachments/profile_photo/content" \
-H "Authorization: Bearer $TAHOE_API_KEY"GET/sourced-profiles/{profile_handle}/list-membershipslists:read
Which of the workspace’s candidate lists this profile is in, and what stage it sits at in each. This is the sourcing-side equivalent of an application’s pipeline stage — see projects and lists.
Related events
sourced_profile.created, sourced_profile.updated and sourced_profile.deleted need sourced_profiles:read. sourced_profile.contact_info_revealed needs contact:read and sourced_profile.attachment_added needs attachments:read.
