Use the control-plane REST API at https://olibato.com/v/api/v1. Hosted app URLs remain under serverl3ss.com; do not send control-plane tokens to app hosts. Send a personal access token on every non-registration request:
Authorization: Bearer oli_pat_...
Content-Type: application/json
Create tokens in Account → API tokens. A read token can inspect resources, a write token can create sites and deploy, and an owner token is required to delete a site. Mutations documented with Idempotency-Key require a stable caller-generated value. Repeating the same request with the same key replays the stored response; changing the request returns 409 idempotency-conflict.
Destination-aware replay binds site requests to the authorized site/namespace
identity and canonical query parameters. Omitting namespace, using personal,
or using your username resolves to the same personal site. Query-key order does
not matter; changing a destination, query value or body does. Tenant access is
checked before site replay.
Receipts from before destination-aware replay cannot prove their original query.
They are retained but return 409 idempotency-legacy-reconciliation-required,
including when the caller believes the retry is identical. Inspect the original
resource/deployment and its outcome before making any new submission; do not
rotate the key or wait out its expiry merely to bypass this response. The upgrade
does not rewrite receipts or reconstruct missing destination evidence.
For CLI deployment, supply a unique --idempotency-key per deployment intent and
retain it until the outcome is known. If omitted, the CLI prints the generated
key before submitting the build. After a timeout or unreadable response, inspect
the site's deploy list/status first; retry only the identical request with the
same key within the API's 24-hour retention window. Do not use a new key to
recover an uncertain submission. Keys must not contain secrets and must be
unique across sites and namespaces. After retention expires, reconcile before
submitting anything again.
Local source uploads use a stable filename and a key scoped to destination,
commit, branch and archive contents. The CLI prints the returned upload UUID;
retain it and use --upload-id UUID when recovering a deployment instead of
archiving a potentially changed worktree. This cannot recover the identity of
an upload made with an older CLI's random filename: inspect existing uploads
before retrying those requests with the repaired CLI.
status HOST --deploy ID and logs HOST are single reads; the CLI does not poll
or activate automatically. Bound any caller polling deadline and space requests
at least two seconds apart. A successful build still requires the activation
API below, followed by runtime status and app-content verification. Updates use
a new deployment intent and the same build-then-activate sequence.
Five-minute container flow#
Set the API URL and token without putting the token in shell history:
API=https://olibato.com/v/api/v1
read -rsp 'PAT: ' TOKEN; export TOKEN
Create a site in the personal namespace. Omit host to derive it from name.
curl -sS "$API/sites" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: quickstart-site-1' \
-d '{"name":"Agent web","host":"agent-web","access":"public"}'
Deploy a versioned container and ask the build agent to author deploy.yaml:
curl -sS "$API/sites/agent-web/deploys" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: quickstart-deploy-1' \
-d '{"source":"container","image":"docker.io/library/nginx:1.27","agent":true}'
Poll the returned deploy id until status is succeeded or terminal:
curl -sS "$API/sites/agent-web/deploys/DEPLOY_ID" -H "Authorization: Bearer $TOKEN"
Activate the successful deploy. Activation starts or replaces the site runtime and preserves exactly one active version:
curl -sS -X POST "$API/sites/agent-web/deploys/DEPLOY_ID/actions/activate" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: quickstart-activate-1' \
-d '{}'
Check site state, inspect logs, and open the returned host_url when state becomes running:
curl -sS "$API/sites/agent-web/status" -H "Authorization: Bearer $TOKEN"
curl -sS "$API/sites/agent-web/logs" -H "Authorization: Bearer $TOKEN"
curl -i https://agent-web.USERNAME.serverl3ss.com
Delete the site with an owner token. This soft-deletes control-plane metadata and submits teardown for any live runtime:
curl -sS -X DELETE "$API/sites/agent-web" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: quickstart-delete-1' \
-d '{}'
Namespaces and host conflicts#
Sites use global /sites URLs. namespace is a JSON field on create and a query parameter on reads, updates, deploys, and deletion. It defaults to the caller's personal namespace. Supply an organization slug for an organization site, for example POST /sites with "namespace":"acme", then use ?namespace=acme on the site-scoped URLs.
Your personal source workspace is provisioned automatically during signup and repaired automatically if it is ever missing. Personal-site deploys are always attributed and billed to that deterministic workspace, regardless of which team workspace is currently selected; organization-site deploys remain attributed to the site's organization. No workspace setup action is required.
Check a host before creation with GET /host-availability?host=web1&namespace=personal. A taken or reserved host returns HTTP 409 with a stable code and a reason. Creation performs the same check atomically. Quota failures return HTTP 429 and a stable quota code.
Deployment sources#
POST /sites/{slug}/deploys accepts exactly one source shape:
{"source":"container","image":"docker.io/library/nginx:1.27","manifest":{"version":1,"build":{"steps":[]},"run":{"image":"docker.io/library/nginx:1.27","port":80}}}
{"source":"repository","repo":"owner/name","ref":"main","manifest_path":".olibato/deploy.yaml"}
The GitHub repository must first be connected and reviewed in the namespace's source workspace. Private repositories use the existing GitHub App installation.
For local code, upload a committed ZIP or tar archive as multipart form data to /sites/{slug}/source-uploads with source, commit_sha, and optional branch. Then deploy it:
{"source":"source_upload","upload_id":"UPLOAD_UUID"}
Install a curated app after enumerating /app-templates and reading its params_schema:
{"source":"app_template","template":"postgresql","version":"16","params":{"database":"app","username":"app","resource_size":"small"}}
For container, repository, and source-upload deployments, provide an embedded manifest, rely on the source tree's manifest_path, or set "agent":true. Supplied and agent-authored manifests enter the same validation, isolated build, security scan, and version pipeline.
Deploy inspection and actions#
GET /sites/{slug}/deployslists deploy attempts.GET /sites/{slug}/deploys/{id}returns status.GET /sites/{slug}/deploys/{id}/eventsstreamstext/event-streambuild milestones.GET /sites/{slug}/deploys/{id}/manifestreturns the supplied or resolved manifest.POST /sites/{slug}/deploys/{id}/actions/cancelcancels an active attempt.POST /sites/{slug}/deploys/{id}/actions/retryretries a terminal GitHub attempt.POST /sites/{slug}/deploys/{id}/actions/activateselects a successful version and starts the site.
Custom domains#
Attach a single hostname after the site has an active deploy. Wildcards are not accepted. The response contains the exact TXT ownership record and CNAME traffic record in dns.records:
curl -sS -X POST "$API/sites/agent-web/domains" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-web-domain-1' \
-d '{"domain":"app.example.com"}'
Publish both returned records at the domain's DNS provider. If Cloudflare hosts the zone, keep the CNAME DNS-only (grey cloud). Do not proxy it through Cloudflare. An A record whose value is the returned edge IP may replace the CNAME where the DNS provider cannot use a CNAME.
After public DNS sees both records, request verification:
curl -sS -X POST "$API/sites/agent-web/domains/app.example.com/verify" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-web-domain-verify-1' \
-d '{}'
pending means DNS has not propagated or the edge target is missing. failed includes an actionable verification or certificate error. verified means ownership and traffic records passed; Certbot HTTP-01 issuance is queued. Poll GET /sites/agent-web/domains/app.example.com until both the domain and certificate are active/issued, then open https://app.example.com.
Remove the router alias and withdraw its dedicated certificate with:
curl -sS -X DELETE "$API/sites/agent-web/domains/app.example.com" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-web-domain-delete-1' \
-d '{}'
For an organization site, add ?namespace=ORGANIZATION_SLUG to every site-domain URL.
Site and TCP access#
Switch an HTTP site between public and signed-in-only access with a write
token. Anonymous traffic to an authenticated site is redirected before the
runtime wakes:
curl -sS -X PATCH "$API/sites/agent-web/access" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-web-private-1' \
-d '{"access":"authenticated"}'
Private TCP services use temporary source-IP grants. Name a non-public service from the active deploy manifest and choose a duration from 1 to 1440 minutes:
curl -sS -X POST "$API/sites/agent-web/tcp-access" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: agent-web-db-access-1' \
-d '{"service":"database","duration_minutes":60}'
The response includes connection, cidr, and expires_at. Connect from the
reported CIDR before expiry; the edge rejects other sources and self-expires
the grant without waking the site.
Endpoint index#
- Account and namespaces:
GET /account,GET /organizations. - Sites:
GET|POST /sites,GET|PATCH|DELETE /sites/{slug},PATCH /sites/{slug}/access,POST /sites/{slug}/tcp-access,GET /host-availability. - Local code:
GET|POST /sites/{slug}/source-uploads. - Deploys:
GET|POST /sites/{slug}/deploys, deploy detail, events, manifest, and actions listed above. - Operations:
GET /sites/{slug}/status,GET /sites/{slug}/logs. - Custom domains:
GET|POST /sites/{slug}/domains,GET|DELETE /sites/{slug}/domains/{domain},POST /sites/{slug}/domains/{domain}/verify. - App registry:
GET /app-templates,GET /app-templates/{slug}. - Legacy organization build and dashboard endpoints remain available for dashboard compatibility.
The complete machine-readable contract is at /v/docs/api/openapi.json.
Errors and rate behavior#
Errors use application/problem+json with status, detail, stable code, request_id, and optional field-level errors. Treat 409 as a state or identity conflict, 412 as stale optimistic concurrency, 429 as quota or rate admission, and 503 as a temporary dependency failure. Honor Retry-After when present. Build and write endpoints are rate-limited independently; poll deploy status no faster than every two seconds and use the events stream when practical.