Errors
The one shape every error has, and the error codes with what to do about each.
This page describes the shape of every error the API returns and lists the error codes.
Every error, from every endpoint, has the same shape. It is the internet standard for HTTP problem details (RFC 9457), with four extra fields: code, fix, param and request_id. The content type is application/problem+json.
{
"type": "https://docs.done.com/errors/insufficient-credits",
"title": "Not enough credits",
"status": 402,
"code": "insufficient_credits",
"detail": "This run needs 120 credits and the workspace has 45.",
"fix": "Top up credits in Plan and billing, or send 45 people or fewer.",
"param": null,
"request_id": "req_7Tq2LmXa"
}| Field | What it holds |
|---|---|
type | A link to this error's page in the docs. It is https://docs.done.com/errors/ plus the code with hyphens instead of underscores. The page leads to the code's entry below. |
title | The error in a few words. |
status | The HTTP status code. |
code | A stable code your code can branch on. Branch on this, never on title or detail. |
detail | What went wrong this time, in plain words. |
fix | One sentence on what to do next. |
param | The request field at fault, as a dotted path, when there is one. It can also name a header such as Done-Workspace or Idempotency-Key. Otherwise null. |
request_id | The id of this request. It matches the Request-Id header. Quote it when you contact support. |
errors | Every problem, when there is more than one. For example, a launch check lists each failed check here. Each item has code, detail and param. |
Every answer, success or error, carries a Request-Id header.
New error codes can be added within an API version, so handle a code you do not know by its HTTP status. See Versions.
Handling errors
- Look at the HTTP
statusfirst. A4xxstatus means the request needs to change, so do not send it again unchanged. Three cases are different:429and503are worth sending again after a wait, and so is a409withidempotency_key_in_progress. - Branch on
codefor anything you want to handle in code. - Show
detailandfixto a person, or give them to an AI agent. Thefixis written so a machine can act on it. - Log
request_id.
Codes
Each operation in the API reference lists the errors it can answer.
400 Bad request
| Code | Meaning |
|---|---|
invalid_field | A field is malformed or out of range. param names it. |
workspace_required | The key reaches more than one workspace and the call has no Done-Workspace header. |
idempotency_key_missing | The call needs an Idempotency-Key header and has none. See Safe retries. |
too_long | A text is longer than allowed. param names the field. |
401 Unauthorized
| Code | Meaning |
|---|---|
key_missing | The call has no key in the Authorization header. |
key_invalid | The key is wrong. Check that you copied all of it. |
key_revoked | The key was revoked. Make a new key in Settings, API keys. |
key_expired | The key passed its expiry date. Make a new key in Settings, API keys. |
402 Payment required
| Code | Meaning |
|---|---|
insufficient_credits | The workspace does not have the credits the call needs. |
search_allowance_reached | The month's search pages are used up. Credits do not lift this limit. |
plan_limit | The plan allows no more of this, for example scouts. |
account_limit_reached | The plan's limit of sending accounts is reached. |
webhook_limit_reached | The plan's limit of webhook endpoints is reached. |
403 Forbidden
| Code | Meaning |
|---|---|
permission_too_low | The key has a lower permission than the call needs. |
daily_credit_cap_reached | The key has used its daily credit cap. Paid calls with this key are refused until the next UTC day. |
workspace_not_in_reach | The Done-Workspace you sent is not one the key reaches. |
not_available_in_test_mode | A done_test_ key called an operation that has no test version yet. Use a live key for this call. See Test keys. |
api_read_only | The done. API is accepting only calls that read right now. Calls that change or spend are refused for a while; try again later. |
plan_does_not_include | The workspace's plan does not include this operation. For example, people search and lookup are not part of the API-only plan. |
404 Not found
| Code | Meaning |
|---|---|
not_found | Nothing with that id exists in this workspace. Another workspace's ids answer the same way. param names the field. |
409 Conflict
| Code | Meaning |
|---|---|
idempotency_key_reused | The Idempotency-Key was used in the last 24 hours with a different body. |
idempotency_key_in_progress | A request with this Idempotency-Key is still running. Wait, then send the same request again. |
version_conflict | The object changed since you read it. Read it again and reapply your change. |
status_changed | The expected_status you sent does not match the object's status now. |
status_transition_refused | The object's status cannot make that move at all. |
run_finished | The scoring run has already finished, failed or been cancelled. |
already_scored | This Idempotency-Key already scored this person and was charged, and that result is no longer kept. Send a new key to score them again; it is charged again. |
all_already_scored | Everyone was scored for this customer profile in the last 90 days. |
research_in_progress | The customer profile is being researched now. |
not_running | There is no research running for this customer profile, so there is nothing to cancel. |
profile_in_use | The customer profile cannot be deleted: its research is running, a scoring run has used it, or a campaign or scout that is not archived uses it. Cancel the research first, or keep the profile. |
list_in_use | The list cannot be deleted while a scout that is not archived adds people to it, a campaign that has not launched takes people from it, or an active lead magnet puts the people who claim it on the list. |
built_in_read_only | Built-in styles cannot be changed or deleted. Copy one first. |
confirmation_required | The change applies to campaigns that use the style. Confirm it to go ahead. |
style_in_use | Campaigns use the style, so it cannot be deleted. |
first_look_waiting | A scout's first look is waiting for your decision. |
no_first_look_waiting | The scout's first look is not waiting for a decision. |
run_in_progress | A scout run has not finished. Wait for scout.run_finished, then run again. |
nothing_new_left | The scout has found everyone its search can reach. |
locked_after_launch | A campaign setting is fixed once the campaign has launched. |
already_launched | The campaign is already active. |
account_still_unavailable | A sending account the campaign needs is still disconnected. Reconnect it, then resume. |
campaign_not_accepting_people | The campaign takes no new people, for example because it is completed. |
not_retryable | The person is not stuck, so there is nothing to retry. |
maybe_sent | A message might already have been sent. Check before you retry. |
sign_in_in_progress | A LinkedIn sign-in for this workspace is already open. |
already_connected | The account is connected and needs no new link. |
already_handled | The draft was already handled. |
person_not_in_sequence | The person left the sequence, for example because they replied. |
own_reply_not_rewritten | A reply written by a person is never rewritten. |
person_state_refuses_send | The person cannot be messaged, for example because they are on the do-not-contact list. |
no_campaign | The conversation was not started by a campaign, so done. cannot send a reply into it. |
own_reply_waiting | A reply you wrote is queued for this conversation and has not gone out yet. |
webhook_disabled | The endpoint was switched off after 20 failed deliveries in a row. |
413 Request too large
| Code | Meaning |
|---|---|
request_too_large | The request body is larger than 256 KB. Send less in one call. |
422 Unprocessable content
| Code | Meaning |
|---|---|
profile_not_ready | The customer profile has nothing to aim for yet. fix says what to add. |
people_cap_required | A scout without a first look needs people_cap. |
kind_retired | That kind of scout can no longer be created. |
no_destination | The scout has no list or campaign to send approved people to. |
not_enough_writing | There is not enough writing to learn a voice from. |
no_sending_account | The workspace has no connected LinkedIn account to send from. |
launch_checks_failed | The campaign cannot launch yet. errors lists each failed check. |
invalid_time_zone | A time zone must be a place name such as Europe/London, not an offset. |
conflicting_settings | Two settings cannot be on at the same time. param names the one to change. |
url_not_allowed | A webhook URL must be https and reachable from the public internet. |
429 Too many requests
| Code | Meaning |
|---|---|
rate_limited | The key made more than 120 calls in a minute. Wait for Retry-After seconds. See Rate limits. |
daily_limit_reached | A per-day limit on one kind of work is reached. The reset time is in detail and Retry-After. |
500 Internal error
| Code | Meaning |
|---|---|
internal_error | done. could not finish this request. Try again in a moment, with the same Idempotency-Key if the call takes one; if it keeps happening, contact support with the request_id. |
score_outcome_unknown | We could not confirm whether a score finished. It was not charged and is never run again under this Idempotency-Key. Send the request again with a new key. |
503 Service unavailable
| Code | Meaning |
|---|---|
connection_unavailable | Connecting a LinkedIn account is temporarily unavailable. Wait for Retry-After, then ask for a new link. |
Example: reading an error in code
const response = await fetch('https://api.done.com/v1/scoring_runs/run_3WmK9pQa', {
headers: { Authorization: `Bearer ${process.env.DONE_API_KEY}` },
});
if (!response.ok) {
const problem = await response.json();
switch (problem.code) {
case 'not_found':
// The id is wrong, or it belongs to another workspace.
break;
case 'rate_limited':
// Wait for Retry-After seconds, then try again.
break;
default:
console.error(problem.status, problem.code, problem.detail, problem.fix, problem.request_id);
}
}