EkireDocs

Errors

When a request fails, the API answers with a 4xx or 5xx HTTP status code and a JSON body in one consistent shape.

The error response

Every error raised by an endpoint comes back as a JSON object with a single error key:

{
  "error": {
    "code": "not_found",
    "message": "Server not found"
  }
}
  • code — a stable, machine-readable string you can branch on (for example not_found, read_only_key, email_taken). It stays the same across releases even if the wording of the message changes.
  • message — a short, human-readable explanation, safe to show to a person. Read this field to display the problem.

The HTTP status code carries the category of the failure; the code inside the body narrows it down. Server errors (5xx) are logged on our side and always return a generic "Internal server error" message, so no internal detail leaks to the client.

internal_error is the fallback code used whenever the underlying error doesn't carry one of its own — it is not exclusive to 5xx. Rate-limited requests, for example, return 429 with code internal_error and a "Rate limit exceeded, retry in …" message. Branch on the HTTP status, not on internal_error, to decide whether a failure is a server fault.

A request to an unrecognised path is answered by Fastify's built-in 404 handler and does not use this envelope. It returns {"message":"Route GET:/api/v1/foo not found","error":"Not Found","statusCode":404}, where error is a string — so error.code and error.message are undefined. Guard for that shape if you call paths you construct dynamically.

Status codes

The API uses standard HTTP status codes.

StatusMeaningWhen you'll see it
200 OKSuccessA read or an action completed.
201 CreatedCreatedA new resource was created (for example deploying a server or creating an API key).
400 Bad RequestInvalid requestThe body or query failed validation, or the request isn't allowed in the resource's current state.
401 UnauthorizedNot authenticatedThe Authorization header is missing, or the token is invalid or expired.
403 ForbiddenNot permittedYou're authenticated but not allowed — for example a read API key attempting a write (read_only_key).
404 Not FoundNo such resourceThe resource doesn't exist or isn't yours. (An unrecognised path also returns 404, in Fastify's default shape rather than the envelope.)
409 ConflictState conflictThe request clashes with existing state (for example an email that's already registered).
429 Too Many RequestsRate limitedYou've exceeded the request rate limit. Back off and retry.
500 Internal Server ErrorServer errorSomething failed on our side. The message is generic; retry later or contact support.

Branch on the status, show the message

Check the HTTP status code first to decide how to react, then surface error.message to the user. Use error.code when you need to handle a specific case in your own logic.

Validation errors

Request bodies and query parameters are validated against a schema before your request reaches any business logic. When validation fails you get a 400 Bad Request in the same envelope, with a code of FST_ERR_VALIDATION. The message is a JSON-stringified array of validation issues — parse it to get each offending field and what was wrong with it:

{
  "error": {
    "code": "FST_ERR_VALIDATION",
    "message": "[{\"validation\":\"email\",\"code\":\"invalid_string\",\"message\":\"Invalid email\",\"path\":[\"email\"]}]"
  }
}

Parse the message, then fix the fields the issues point to (the path array) and retry. Business-rule failures that aren't schema violations (for example "amount must be positive") also come back as 400, but with their own descriptive code rather than FST_ERR_VALIDATION.

Handling errors in code

Treat any non-2xx status as a failure and read error.message from the JSON body.

const res = await fetch("https://console.ekire.net/api/v1/instances/does-not-exist", {
  headers: { Authorization: `Bearer ${process.env.EKIRE_API_KEY}` },
});
 
if (!res.ok) {
  const body = await res.json();
  // `error` is an object for endpoint errors, a plain string for Fastify's default 404.
  const code = body.error?.code ?? body.error ?? "unknown";
  const message = body.error?.message ?? body.message ?? res.statusText;
  throw new Error(`${res.status} ${code}: ${message}`);
}