done.developers
Building blocks

People and lists

How to keep the people you know, group them in lists, look people up, find a work email, and keep the wrong people from ever being contacted.

This page covers the people a workspace knows, the lists that group them, looking people up, finding a work email, and the do-not-contact list.

A person is someone on LinkedIn that the workspace knows about. A list is a named group of people. Lists are what you point a campaign or a scoring run at.

The main calls

Add people

POST /people adds up to 500 people by LinkedIn profile link. People the workspace already knows are matched, not duplicated. Send list to also put them on a list.

curl https://api.done.com/v1/people \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Idempotency-Key: 5e0a8c93-2b71-4d4f-9a16-c8b3e7d20f4a" \
  -H "Content-Type: application/json" \
  -d '{
    "people": [
      { "linkedin_url": "https://www.linkedin.com/in/ana-lima", "first_name": "Ana", "last_name": "Lima" },
      { "linkedin_url": "https://www.linkedin.com/in/tomas-reyes" }
    ],
    "list": "lst_4LmZ8nQw"
  }'

This only saves people. It does not read their profiles, score them or message them. Links that are not a person's profile come back in refused, and the rest are still added. It is free.

Read people

GET /people lists the people the workspace knows, newest first. Filter by list, campaign, scout or min_fit. Filters combine: a person must match all you send. A person who has never been scored has no latest_fit, so min_fit leaves them out.

curl "https://api.done.com/v1/people?list=lst_4LmZ8nQw&min_fit=70" \
  -H "Authorization: Bearer $DONE_API_KEY"

GET /people/{id} returns one person: who they are, the lists and campaigns they are in, and their latest fit score. It never returns an email address. Use the email call for that.

Make and fill lists

# Make an empty list
curl https://api.done.com/v1/lists \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Idempotency-Key: 1c6f9b28-7d34-4e51-8b0a-3a9d52e6c7f1" \
  -H "Content-Type: application/json" \
  -d '{ "name": "UK finance leaders" }'

# Put people on it
curl https://api.done.com/v1/lists/lst_4LmZ8nQw/people \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "people": [ { "person": "per_9aQw1XyZ" }, { "linkedin_url": "https://www.linkedin.com/in/tomas-reyes" } ] }'
  • POST /lists/{id}/people puts up to 500 people on a list, by person id or by linkedin_url. People already on the list are counted in already_on_list and left alone.
  • POST /lists/{id}/people/remove takes up to 500 people off a list, by person id. They stay in the workspace and on any other list.
  • PATCH /lists/{id} renames a list. DELETE /lists/{id} deletes it. The people stay in the workspace, and a campaign that used the list keeps the people it already took in.
  • GET /lists and GET /lists/{id} read lists.

Search and look up (full plan)

These two calls read LinkedIn on your behalf. They are not part of the API-only plan, which sells results and never raw LinkedIn data.

POST /people/search reads one page of LinkedIn people search results, about 25 people. Search by titles, companies, places and industries, or send a Sales Navigator people search link. Ask for page 2, 3 and so on to read more. Nothing is saved unless you send save_to_list.

curl https://api.done.com/v1/people/search \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Idempotency-Key: 8a24d7e1-96c5-4b30-a7f8-12e5c0b94d63" \
  -H "Content-Type: application/json" \
  -d '{
    "titles": ["CFO", "Finance Director"],
    "places": ["United Kingdom"],
    "industries": ["Fintech"],
    "page": 1,
    "save_to_list": "lst_4LmZ8nQw"
  }'

Results are not scored and not messaged. To see who fits, score them with POST /scores or POST /scoring_runs. For people who match a customer profile over time, create a scout instead.

POST /people/lookup reads one person's public LinkedIn profile and returns it cleaned up: name, headline, company, role, location, recent experience and a short summary of recent posts. Nothing is saved.

curl https://api.done.com/v1/people/lookup \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Idempotency-Key: e3f71a09-4c85-4d26-b1d7-6a0c9e82f5b4" \
  -H "Content-Type: application/json" \
  -d '{ "linkedin_url": "https://www.linkedin.com/in/ana-lima" }'

Find a work email

POST /people/{id}/email looks for one person's work email address.

curl -X POST https://api.done.com/v1/people/per_9aQw1XyZ/email \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Idempotency-Key: 7b19e4c6-0d3a-4f82-95e1-a2c8d6f30b57"
{
  "object": "email_lookup",
  "person": "per_9aQw1XyZ",
  "email": "ana.lima@lumenpay.example",
  "status": "found",
  "confidence": "good",
  "checked_at": "2026-10-23T09:50:00Z",
  "credits": 0.7
}

The answer is found or not_found. A found address carries a confidence of good, risky or unknown. done. never test-sends to the address.

Keep people from ever being contacted

The do-not-contact list holds the people, and the companies, that done. will never message.

curl https://api.done.com/v1/do_not_contact/people \
  -H "Authorization: Bearer $DONE_API_KEY" \
  -H "Idempotency-Key: 3d58b0f2-a649-4e17-8c93-5f1e7a2d84c0" \
  -H "Content-Type: application/json" \
  -d '{
    "linkedin_url": "https://www.linkedin.com/in/ana-lima",
    "name": "Ana Lima",
    "company": "Lumen Pay",
    "reason": "Asked us to stop"
  }'
CallWhat it does
POST /do_not_contact/peopleNever message this person. Also takes them out of any campaign still in progress, at once. taken_out_of_campaigns says how many were stopped.
POST /do_not_contact/companiesNever message anyone at this company. Send the name, and the domain if you have it, which matches more reliably.
POST /do_not_contact/importAdd up to 1,000 people in one call. Links already on the list are skipped.
GET /do_not_contact/people and GET /do_not_contact/companiesList entries, newest first. The list never says who added an entry.
DELETE /do_not_contact/people/{id} and DELETE /do_not_contact/companies/{id}Take an entry off the list.

What it costs

CallCost
Add people, lists, do-not-contact, readsfree
POST /people/searchabout 1.4 a page
POST /people/lookupabout 0.1
POST /people/{id}/emailabout 0.7, only when an email is found for the first time

Search pages also count against the plan's monthly search-page allowance. When it is used up, credits do not lift it: the 402 search_allowance_reached says when it resets. See Credits and costs.

Rules that apply

  • Do-not-contact entries reach campaigns at once. Adding a person takes them out of campaigns that are still in progress. People whose campaign already finished are left as they were. Removing an entry later does not put anyone back into a campaign they were taken out of. It only lets campaigns that take people in from then on contact them.
  • Emails are cached for 90 days. Answers, including not_found, are kept for 90 days. Asking again within 90 days returns the same answer and costs nothing. You are charged only when an address is found for the first time. The workspace must still have at least 0.7 available when you ask, or the call is refused with insufficient_credits.
  • A person is never returned with an email. Only the email call returns an address.
  • Permissions. Search, lookup and email need spend. Adding people, changing lists and the do-not-contact list need full. Reading needs read.
  • Retries. Calls that add, search, look up or find an email take an Idempotency-Key. See Safe retries.

On this page