Search documentation

Search documentation

The Kallo API


Kallo has an HTTP API. It is the same API the web app and the mobile app use — there is no separate public one — and this page describes it honestly, including the parts that are not ready for you to build on.

The machine-readable description is at https://kallo.fit/openapi.json. It is OpenAPI 3.1, every operation has a unique operationId and a description, and request bodies are generated from the same schemas the server validates against, so they cannot drift from what the endpoints actually accept.

Base URL

https://kallo.fit

Endpoints live under /api/v1/. The one exception is /api/healthz, which is unversioned because it is a liveness probe rather than part of the surface.

Authentication

Every endpoint except the ones tagged Public needs a Supabase-issued access token for a Kallo user:

Authorization: Bearer <jwt>

The token carries no scopes. A bearer holds the account's full authority: it can read every meal, change the profile, and delete the account. There is no way to issue a narrower one. That is why the protected-resource metadata declares no scopes_supported — publishing a scope list nothing enforces would be worse than publishing none.

Protected-resource metadata follows RFC 9728 and names the Supabase Auth issuer as the authorization server. Kallo itself is not an authorization server: there is no /oauth/authorize, no /oauth/token, and no dynamic client registration.

Errors

Every documented API error returns the same envelope:

{
  "error": {
    "code": "VALIDATION_FAILED",
    "status": 400,
    "retryable": false,
    "message": "date must be YYYY-MM-DD.",
    "resolution": "Correct the request using the published schema, then retry."
  }
}

code is stable and meant to be branched on. message is for a human and may be translated. retryable says whether repeating the identical request can succeed. resolution gives an agent the next action without asking it to parse the human prose. Redirect-only operations and the health probe document their own response shapes in OpenAPI.

CodeStatusRetryableMeans
VALIDATION_FAILED400noThe request body or query is wrong. Fix it.
NOT_AUTHENTICATED401noMissing, expired or invalid token.
feature_locked402noThe account's plan does not include this.
NOT_FOUND404noNo such resource, or it is not yours. The two are deliberately indistinguishable.
CONFLICT409noThe resource changed under you.
RATE_LIMITED429yesBack off. Honour Retry-After when it is present.
PIPELINE_TIMEOUT504yesMeal analysis took too long.
INTERNAL500yesOur fault.

Any /api/ path with no handler returns a 404 in this same JSON shape, never an HTML page — so a mistyped endpoint is something your client can parse rather than something it has to guess at.

Rate limits

There is no published per-endpoint quota. Limits exist and are applied per user and, for the unauthenticated endpoints, per IP address. When you hit one you get RATE_LIMITED with a Retry-After header where the wait is known. Treat that header as authoritative rather than backing off on a fixed schedule.

What you may rely on

Operations tagged Public in the spec are safe to depend on:

  • GET /api/healthz
  • POST /api/v1/waitlist
  • GET /api/v1/waitlist/confirm
  • GET /api/v1/groups/invite/{slug}

Everything else is marked x-internal: true. Those endpoints exist to serve Kallo's own clients. They are versioned under /v1, but that version is not a promise to third parties — a shape can change to suit the apps without a bump. If you build on one, pin your expectations loosely and read the spec again before you upgrade.

Cost

The free tier is real, needs no card, and is enough to use the API for anything you can do in the app on a free account. There is no separate API price and no sandbox environment — you develop against production, with your own account and your own data.