Errors

Every error from the public API uses the same JSON shape, so you can handle failures in one place. Branch on code in your code, and show message to people only as a hint, since its wording can change.

Error shape

{
  "error": {
    "code": "forbidden",
    "message": "Your role cannot move cards"
  }
}

The code is stable. The message is human-readable and may change between releases.

Status codes and error codes

StatusCodeMeaning
400invalid_bodyThe request body isn't valid JSON.
401missing_keyNo API key was sent. Send Authorization: Bearer <key> or the x-api-key header.
401invalid_keyThe key is invalid, expired, or revoked, or its owner is banned or no longer active.
403insufficient_scopeThe key doesn't have the Read, Create, or Move permission this endpoint needs.
403organization_not_allowedThe key isn't granted access to the organization that the request targets.
403forbiddenThe key owner isn't a member of the organization, or their role doesn't allow this action.
404not_foundThe board, list, card, or move destination doesn't exist or isn't visible to the key owner. A move destination on another board also returns 404.
409conflictA move's expectedListId no longer matches the card's current list. The card moved since you read it.
422validation_errorA field failed validation. The message names the problem.
422invalid_labelA labelId doesn't belong to the target project.
422invalid_positionThe move's position is outside the valid range for the target list.
429RATE_LIMITEDThis key has sent more than 60 requests in the last minute.
500internal_errorAn unexpected server error. Nothing in the request is wrong.

Retries and rate limits

Retry only when the error is temporary. Fix the request for everything else.

  • Retry 429 and 500 with exponential backoff. Start at about one second and double the wait on each attempt, with a fixed maximum number of tries.
  • Don't retry 400, 401, 403, 404, or 422 without changing the request or key. Retrying the same call returns the same error.
  • On 409, read the card again and use its current listId before you try the move again.
  • Watch the limit. Each API key allows 60 requests per minute, across all of its calls. Spread bulk work over time, or cache reads you repeat.
Tip

The API doesn't accept an idempotency key. If a POST /api/v1/cards fails with a 500 or a network error, the card may already exist. Read the board or list cards before you retry, so you don't create a duplicate.

async function withBackoff(url: string, init: RequestInit, maxAttempts = 4) {
  let delay = 1000;
  for (let attempt = 1; ; attempt++) {
    const res = await fetch(url, init);
    const retryable = res.status === 429 || res.status === 500;
    if (!retryable || attempt === maxAttempts) return res;
    await new Promise((resolve) => setTimeout(resolve, delay));
    delay *= 2;
  }
}

For how keys are sent and how their limits work, see Authentication.