---
title: REST API
description: Authenticate and automate Olibato namespaces, sites, deploys, access, domains, and usage.
audience: developer
status: private-beta
last_updated: 2026-09-02
order: 0
related:
  - /v/docs/getting-started/quickstart.md
  - /v/docs/getting-started/api-tokens.md
  - /v/docs/concepts/sites-and-deploys.md
---

The Olibato REST API is available at `/v/api/v1/` when enabled for a deployment.
It exposes the sites model used by the browser: reserve sites, create deploys,
inspect versions and events, operate runtimes, manage access and custom domains,
and read usage. App templates are machine-readable so an agent can discover
supported parameters before installing one.

Operators enable the interface with `OLIBATO_REST_API_ENABLED=true`. Production
deployments must also set an independent high-entropy
`OLIBATO_API_TOKEN_PEPPER`; throttles use the shared Redis cache so limits apply
across web workers.

## Register and create a personal access token

An API-only client can complete signup and mailbox verification without a
browser:

```text
GET  /v/api/v1/auth/registration
POST /v/api/v1/auth/registration
POST /v/api/v1/auth/registration/verify
```

The registration request includes `username` for the permanent personal
namespace, `email`, `password`, and the legal versions returned by the first
request. Verification activates the account and returns an initial owner token
once. Verification and recovery secrets are single-use and delivered only to
the registered mailbox.

Authenticated owners manage credentials at `/account/tokens`. Tokens have one
ordered permission: `read`, `write`, or `owner`. Write includes read; owner adds
credential management and irreversible operations. Organization roles remain an
independent authorization boundary.

Send the token as a bearer credential:

```http
Authorization: Bearer oli_pat_…
```

See [API tokens](/v/docs/getting-started/api-tokens/) for safe storage,
inspection, recovery, and revocation.

## Personal and organization namespaces

Personal site requests use the caller's permanent namespace. Organization
requests include the organization slug where the operation supports it. A
request for an organization the token owner cannot access returns `404`; API
requests never depend on the dashboard's currently selected workspace.

## Reserve and inspect sites

Use the primary site collection and detail resources:

```text
GET|POST          /v/api/v1/sites
GET|PATCH|DELETE  /v/api/v1/sites/{site_slug}
GET               /v/api/v1/sites/{site_slug}/status
GET               /v/api/v1/sites/{site_slug}/logs
GET               /v/api/v1/sites/{site_slug}/usage
```

Creating a site reserves its slug and namespaced host before a deploy exists.
`state: running` reports runtime state, and `active_version_id` identifies the selected
version. `host_url` is the intended public address constructed from the site namespace;
its presence does not verify DNS provisioning, certificate installation, or public HTTPS
reachability. The additive `https` status below supplies bounded, timestamped proof.
Verify trusted HTTPS and expected application content separately before reporting a
public deployment complete.

`GET /sites/{site_slug}/logs` returns build logs, build-step events and runtime
state/error metadata; it is not a live application stdout/stderr stream. Treat logs
as potentially sensitive and summarize them before sharing. The CLI supports
`status HOST`, `status HOST --deploy ID`, and `logs HOST`; usage is currently a
public API read, without a dedicated CLI command.

Site deletion removes its runtime and route while preserving append-only usage
records required for audit.

## Create deploys

Deploy a container image, repository revision, local source upload, or app
template through the site's deploy collection. Follow the returned build and
version through the detail, event, and manifest resources:

```text
POST /v/api/v1/sites/{site_slug}/source-uploads
GET|POST /v/api/v1/sites/{site_slug}/deploys
GET      /v/api/v1/sites/{site_slug}/deploys/{deploy_id}
GET      /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/events
GET      /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/manifest
POST     /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/actions/{action}
```

Local source uploads accept committed archives and return an upload ID for the
deploy request. Source bytes are temporary; source-free identity and build
evidence remain after the deploy reaches a terminal state.

Discover supported app templates and their parameter schemas through:

```text
GET /v/api/v1/app-templates
GET /v/api/v1/app-templates/{template_slug}
```

## Access and domains

Set HTTP access to `public` or `authenticated`, and create expiring source-IP
grants for private TCP services:

```text
GET|PATCH /v/api/v1/sites/{site_slug}/access
GET|POST  /v/api/v1/sites/{site_slug}/tcp-access
```

Custom-domain creation returns the required ownership TXT and traffic CNAME
records. Verification must observe both public records before routing and
certificate issuance begin:

```text
GET|POST /v/api/v1/sites/{site_slug}/domains
GET|DELETE /v/api/v1/sites/{site_slug}/domains/{domain}
POST /v/api/v1/sites/{site_slug}/domains/{domain}/verify
```

## Safe writes

JSON creation and action `POST` requests require an `Idempotency-Key` header.
Repeating the same request with the same key replays its stored response; using
the key with different input returns `409`. Streamed source upload uses its
canonical SHA-256 as content identity, and the returned upload ID is supplied to
the idempotent deploy request.

Mutable resources return an `ETag`. Send that value in `If-Match` for `PATCH`
and `DELETE`; missing preconditions return `428` and stale values return `412`.

```bash
curl -sS https://olibato.com/v/api/v1/sites \
  -H "Authorization: Bearer $OLIBATO_API_TOKEN"
```

Errors use `application/problem+json` with RFC 9457 fields, a stable code,
request correlation ID, and optional field errors. Lists use opaque cursor
pagination with `next`, `previous`, and `results`.

## OpenAPI source of truth

The canonical [OpenAPI 3.1 document](/v/docs/api/openapi.json) lists supported
routes, exact request and response schemas, permissions, idempotency rules, and
concurrency preconditions. Agents should use that document for interface
discovery rather than inferring routes from browser pages.

Regenerate and validate the checked-in artifact after changing the REST surface:

```bash
cd web
python3 manage.py spectacular \
  --urlconf olibato_api.urls \
  --format openapi-json \
  --file ../docs/api/openapi.json \
  --validate
```

### Usage totals and accounting availability

`GET /v/api/v1/sites/{site_slug}/usage` adds an `accounting` object.
`GET /v/api/v1/organizations/{organization_slug}/usage` adds the same object
inside `summary`. Existing numeric totals and access requirements are unchanged.
Those numbers aggregate recorded ledger entries: zero is **not evidence of zero
actual consumption**, and nonzero is not a complete-history guarantee.

`accounting.basis` is `recorded_ledger`; `history_complete` is currently false.
`collection.instances` counts only the authorized scope's `missing`, `unavailable`,
`stale`, `fresh` and `binding_mismatch` checkpoints. `freshness_window_seconds` is
60, matching the existing collector's observation bound. `all_fresh` means every
scoped instance has a recent successful observation; it does not erase gaps,
prove uninterrupted sampling, or establish complete monthly usage. No instances
means `all_fresh: false`, not proof that collection occurred.

`reasons` contains finite public reason codes, including retained stream/event
and observation gaps. Raw checkpoint data, node addresses and identities from
other namespaces are not returned. `storage.availability` remains `unavailable`,
`interval_coverage` is `unproven`, and `new_interval_metering` is `disabled`.
The existing `storage_gb_hours` field retains its numeric contract and historical
values; its unit is GiB-hours (1024³ bytes × hours). This metadata does not change
quota enforcement, rates, ledger records or authorize backfill. API clients must
not present recorded totals as fully measured consumption or guaranteed remaining
capacity when accounting coverage is unavailable/incomplete.

### HTTPS readiness (additive field)

Site detail/list/status and activation responses now include `https` separately
from application `state` and the intended `host_url`:

```json
{"state":"running","https":{"state":"ready","reason":"trusted_https_verified","retryable":false,"verified_at":"2026-09-13T17:25:51Z","valid_until":"2026-09-13T17:30:51Z"}}
```

`https.state` is `preparing`, `ready`, `failed`, or `unknown`. `ready` means a
recent bounded public-DNS, trusted TLS, exact namespace certificate and content
comparison against the selected node's owned Host route passed. This is an
observation, not an uptime guarantee. Proofs expire after five minutes and become
`unknown` if the runtime/version binding changes. Missing proof is `unknown`,
not success. Numeric usage and ordinary runtime state are unchanged.

After the normal build succeeds, explicitly activate it through the existing
API. Personal activation queues scoped follow-through; do not infer activation
from build completion. The background worker observes only those new operation
bindings, enrolls through private mTLS, and uses the existing guarded DNS/ACME
path when platform order authority is available. Operators configure node/edge
trust once; customers never supply upstream addresses or use SSH.

Use bounded status reads. Preserve operation/idempotency receipts; an HTTPS
failure does not justify creating another deploy or order. Safe reasons include
`runtime_pending`, `node_not_enrolled`, `new_order_authority_exhausted`,
`public_dns_not_ready`, `public_routing_not_ready`, `proof_stale`, and
`runtime_binding_changed`. Follow `retryable`; there is no public DNS/issuance
retry bypass. Current new-order allocation is zero because the authorized test
budgets have been consumed; this is reported as a gate, not HTTPS readiness.

The source CLI supports `status hello --namespace personal --require-https`:
it prints normal JSON and exits nonzero unless HTTPS proof is fresh and ready.
Without the flag, status remains backward compatible. `--deploy` reports a build,
so it cannot be combined with `--require-https`. No CLI package was published.
