Koo Docs
View as Markdown

API overview

The Koo API is how the console, the SDKs, and your own tools talk to Koo. Everything you can do in the console — create apps and services, trigger deployments, read logs — goes through the same API.

This page covers the conventions that apply to every endpoint. For the endpoint-by-endpoint reference, see the API reference.

Base URL & format

All requests go to:

text
https://api.koo.io
  • Requests and responses are JSON, with camelCase field names.
  • There is no version prefix. The API is stable and evolves additively: new fields and endpoints appear, existing ones don't change meaning or disappear.
  • Every response includes a request ID you can use when contacting support.

Authentication

Authenticate every request with a bearer token:

text
Authorization: Bearer <token>

There are two kinds of token:

  • Session tokens — issued when you sign in; these power the console. They identify you as a user and carry your role in each account.
  • API tokens — opaque tokens prefixed kc_…, made for machines: CI pipelines, scripts, and the SDKs. See API tokens.

Public endpoints — discovery, public profiles, and public app pages — need no authentication.

Errors

Every error response uses the same envelope:

json
{  "error": {    "code": "not_found",    "message": "Service not found.",    "details": null,    "requestId": "..."  }}

code is a stable, machine-readable enum — branch on it in your code. message is for humans and may change without notice; never parse it. details carries structured context for some codes, such as the failing fields on a validation error.

Common codes:

CodeMeaningWhat to do
unauthenticatedMissing, expired, or invalid tokenRe-authenticate and retry
forbiddenYour role in the account doesn't allow thisAsk the account's owner or an admin, or switch accounts
not_foundThe resource doesn't exist or isn't visible to youCheck the ID and the account you're acting in
validation_failedThe request body didn't pass validationFix the fields listed in details
quota_exceededA plan ceiling was reachedUpgrade the plan or remove something
rate_limitedToo many requests in a short windowBack off and retry

Include the requestId from the error envelope when you contact support — it lets us find the exact request in our logs.

Pagination

List endpoints are cursor-based. Pass limit and cursor as query parameters; the response wraps results in data and a page object:

json
{  "data": [],  "page": {    "nextCursor": "...",    "hasMore": true  }}

To fetch the next page, pass page.nextCursor as the cursor parameter. Cursors are opaque — store and replay them, but don't construct or decode them. When hasMore is false, you've reached the end.

Idempotency

POST endpoints that create resources honor an Idempotency-Key header. Send a unique key (a UUID works well) with each create; if the request is retried — after a timeout, say — the same key returns the original result instead of creating a duplicate.

Rate limits

Two distinct limits can reject a request, and they call for different fixes:

  • rate_limited means you're sending requests too fast. This is a rolling window — back off, retry with exponential delay, and the requests will succeed.
  • quota_exceeded means you've hit a plan ceiling — for example, the per-tier cap on builds or deploys per hour. Retrying won't help; upgrade the plan on the billing page or reduce usage.

Explore the reference

The API reference documents every endpoint, generated from the same definitions the API exposes. Good starting points: