Search the docs

Search guides, API endpoints and MCP tools.

Errors

A failed request returns an HTTP status and a JSON body with one of six error tags and a message a person can act on.

A failed request returns a 4xx or 5xx status and a JSON body with two fields: _tag, which names the kind of error, and message, a sentence that says what went wrong and, where it can, what to do about it.

json
{
  "message": "An archived product already uses the slug storefront. Restore it or choose another slug.",
  "_tag": "Conflict"
}

Branch on _tag or the status, never on the message: messages are written for people and can change.

Error tags

Status_tagMeaning
400InvalidInputThe request is malformed, or a value is not allowed, such as a slug with capitals or a body missing a required field
401UnauthorizedThe request has no valid API key: it is missing, unknown or revoked
403ForbiddenThe key's user or role may not do this, such as a viewer creating a ticket or a member writing a production secret
404NotFoundThe workspace or object does not exist, or the caller cannot see it
409ConflictThe change conflicts with current data, such as a slug that is already in use or an archived object
500InternalClanker Ship failed to handle the request

Each endpoint in the reference lists the tags it can return.

What each one means for you

  • InvalidInput names the rule in its message, such as Use 2–48 lowercase letters, numbers or hyphens, …. A body that does not match the endpoint's schema at all, such as a missing required field or a value of the wrong type, gets the message Invalid request payload. Fix the request before sending it again.
  • Unauthorized means the key itself failed. Check that you sent it, and that it was not revoked. See Authentication.
  • Forbidden means the key works but its role or kind does not allow the action, such as an account API key commenting on a ticket. Use a key with the right role.
  • NotFound covers objects in other workspaces too: a workspace you do not belong to, or another workspace's product, returns 404, never 403, so the API does not reveal what exists.
  • Conflict means the request was valid but the current data stops it: a taken name, an archived product, a test run that already finished, or a secret that changed since you read it. Read the object again, then decide. Repeating the same request gets the same answer.
  • Internal means Clanker Ship failed. Retry after a short wait; before retrying a write that is not idempotent, such as creating a ticket, check whether the first attempt went through. If it keeps happening, report it; see Request IDs.

Handle errors in code

typescript
const response = await fetch("https://api.clankertest.com/v1/workspaces/acme/products", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLANKER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ slug: "storefront", name: "Storefront", type: "website" }),
});

if (!response.ok) {
  const error = (await response.json()) as { _tag: string; message: string };
  if (error._tag === "Conflict") {
    // Already there: read it instead of creating it.
  } else {
    throw new Error(`${response.status} ${error._tag}: ${error.message}`);
  }
}

Other shapes

  • A path that matches no endpoint returns 404 with an empty body.
  • Test-run file uploads and downloads (/test-runs/{run}/files/{path}) return their errors as { "message": "…" }, without _tag.
  • The agent HTTP API and MCP use their own shape, { "code", "message", "requestId" }, with codes such as not_found and permission_denied; see Agent HTTP API.