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.
The API is the active authenticated Sites API. Historical demo_*, Fleet,
golden-image, and Image names may still appear in persistence fields, audit
events, or compatibility routes; those names are retained for wire and data
compatibility and are not separate public products.
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:
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:
Authorization: Bearer oli_pat_…
See 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:
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:
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
GET /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/variables
POST /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/variables/reveal
POST /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/containers/{service}/logs
GET /v/api/v1/sites/{site_slug}/deploys/{deploy_id}/containers/{service}/logs/{operation_id}
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.
Inspect site configuration and secrets#
Use the site-scoped inspection routes to inspect the active version separately from an individual build attempt:
GET /v/api/v1/sites/{site_slug}/manifest
POST /v/api/v1/sites/{site_slug}/manifest/resolve
GET /v/api/v1/sites/{site_slug}/secrets
POST /v/api/v1/sites/{site_slug}/secrets/{name}/reveal
GET /v/api/v1/sites/{site_slug}/variables
Manifest, secret-list, and variable-list reads require read permission and never
return secret values. The manifest resolve and secret reveal operations require
write permission, are throttled, return Cache-Control: private, no-store, and
write an audit event. manifest/resolve accepts an empty JSON object and
returns resolved manifest_yaml; secret reveal accepts an empty JSON object
and returns the selected name and value. The {name} path segment may
contain slashes, for example owner/DATABASE_URL.
The site and deploy variables responses include secret_name for references
that resolve to one declared secret. Combined or non-secret values have a null
secret_name. The deploy-scoped variables/reveal endpoint retains its
existing read permission and response shape.
Discover supported app templates and their parameter schemas through:
GET /v/api/v1/app-templates
GET /v/api/v1/app-templates/{template_slug}
MySQL/MariaDB seed definitions default username to u_{{ site.token }}.
Omit a username override to let the renderer substitute the persisted site ID;
inspect the resolved deploy manifest for the concrete routing username. Existing
template rows require the normal seed rollout to expose changed defaults.
Redis/Valkey seed manifests use TLS and ACL user app with default disabled
and @dangerous commands excluded.
The OpenAPI manifest schema includes the additive version: 1 fields
services.<name>.protocol (http|postgres|mysql|redis|amqp|tcp|mongodb),
services.<name>.tls (empty map only), and optional connect.scheme (display
hint). See Sites and deploys for validation
constraints and the certificate path convention. These fields do not establish
external routing or certificate-delivery readiness.
Access and domains#
Set HTTP access to public or authenticated, and create expiring source-IP
grants for private TCP services:
GET|PATCH /v/api/v1/sites/{site_slug}/access
GET|POST /v/api/v1/sites/{site_slug}/tcp-access
The implemented TCP grant response exposes service, hostname, port,
generic connection: tcp://<hostname>:<port>, cidr, expires_at,
operation_id, grant_id, edge_state, protocol_state, protocol, and
nullable routing_username. MySQL instead returns
mysql://<routing_username>@<hostname>:3306. Configuration
or allocation with an unverified protocol state does not prove connectivity.
Application credentials and verified TLS are still required.
The protocol edge is implemented in source and
component-tested: shared ports are
443 for HTTP/client-first TLS, 5432 for PostgreSQL, 3306 for the MySQL gateway
with a site-unique routing username, and 5671 for AMQPS. Protocol policy and
private edge-state/authz/credential-sync APIs and their Envoy/gateway consumers
exist in source. Public deployment must be verified separately. socket_path, internal_port,
and draining are internal endpoint fields, not public grant fields. Non-MySQL
grant URIs remain generic tcp://; configure clients for their actual TLS
protocol. OpenAPI types the TCP-access response, including nullable
routing_username, and the usage response, including integer network_bytes.
It contains only implemented public routes. Client connections
require reconnection after runtime recycle; keepalives keep the instance awake
and billable.
Custom-domain creation returns the required ownership TXT and traffic CNAME records. Verification must observe both public records before routing and certificate issuance begin:
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.
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 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:
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.
The Supervisor is authoritative for metering; edge counters only reconcile.
Relay source emits network_bytes; Django ingestion records received plus sent
bytes with directional counters and duration in ledger metadata. It deduplicates
by node, Supervisor stream, and sequence and validates node/endpoint ownership.
The metered set is wake_minutes, requests, storage_gb_hours, and
network_bytes. The public site usage response does not expose a network total;
recording these facts does not introduce a pricing rule or prove full coverage.
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:
{"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.