Skip to main content
This page explains conventions used across the hiloop REST API. For exact routes and schemas, use the API reference.

Base URL

Hosted API:
Self-hosted, BYOC, and on-prem deployments use deployment-specific API URLs:

Authentication

Send API keys as bearer tokens:
The organization is resolved from the credential. Do not include an organization id in request bodies as an authority source.

Organizations and scope

Your account is an organization, and it is the isolation boundary: projects, sandboxes, runs, and telemetry all live in it. The edge stamps the caller’s organization from their credential, so a request can only ever touch its own organization’s data. Every request acts at one of two scopes:
  • Organization scope — the default, and where the whole day-to-day surface lives: sandboxes, telemetry, projects, annotations, members, and organization-wide automation.
  • Sandbox scope — a per-sandbox credential, further confined to its own run lineage. It can read its own sandbox and snapshot lineage and act only on itself; it cannot create, update, or delete sandboxes, or touch siblings.
From the CLI, hiloop whoami prints your resolved principal (its kind, id, email, and key name) and organization. API keys carry a fixed scope from the moment you mint them — mint a key with the scope it needs.

Deleting a project

DELETE /v1/projects/{id} deletes a project within your organization. By default a project that still has resources — sandboxes, runs, scoped API keys, or scoped secrets — cannot be deleted: the call returns a conflict, and you must remove those resources first. To delete a project and everything in it in one call, set cascade to true. The cascade purges the project’s supported records, then removes the project. It is scoped to your organization, so it can only ever touch your own data, and it is irreversible. What a cascade never does is tear down a sandbox that still exists: delete the project’s remaining sandboxes first (DELETE /v1/sandboxes/{id} — the normal delete, which tears down the sandbox’s compute), then cascade. One guard keeps a cascade honest and refuses the whole call atomically: every sandbox in the project must already be deleted. While one is not, the call returns a conflict with error code sandboxes_not_deleted — delete the remaining sandboxes, then retry.
From the CLI, the same delete is hiloop projects delete <id|slug> (add --cascade for the cascade; it asks for confirmation unless you pass --yes). The response reports what the delete actually removed, per resource, so scripts and audit logs can verify the scope of the purge:
Counts are rendered as JSON strings (they are 64-bit integers). A non-cascading delete only ever removes a project with no dependents, so its counts are all zero.

Idempotency

An idempotency key is optional and applies to create-style mutations: create sandbox, snapshot create, exec, and secret rotation. The SDKs generate one automatically. Direct API calls may omit it, but every unkeyed call is a fresh mutation and must not be retried after an ambiguous failure. Pass your own key when you want a retry to be safe across processes: reusing the same key with the same body replays the original result, while reusing it with a different body is rejected with idempotency_conflict (HTTP 409) — a key names one logical request, never a family of them. The hiloop CLI takes the same key as --idempotency-key on these commands, and a key turns on automatic retries for the request itself: an ambiguous failure — a timeout, a dropped connection, a 5xx — is retried up to 3 times with the same key before the error surfaces. Without a key the CLI never retries a mutation, because each attempt would be a fresh one. Delete and the stop/start lifecycle transitions need no key: they are idempotent by sandbox id, so repeating one converges on the same desired state rather than stacking up work. To pass your own key, send it in the idempotency-key header:

Asynchronous state

Sandbox mutations are asynchronous, but there is no separate operation resource to poll. A create, update, or delete returns as soon as the request is accepted, and you poll the sandbox itself until its observed state reaches the outcome you asked for:
Observed states are requested, reserved, materializing, ready, running, stopped, terminating, failed, terminated, quarantined, and attention. The state you read is evidence of what the runtime is doing, not a record of what was asked for. A sandbox that reached failed or quarantined carries a state_reason explaining why; lifecycle states that need no explanation omit it. The CLI polls for you: hiloop sandbox create returns once the sandbox is running.

Resource identifiers

Resource IDs are opaque. Do not parse them for meaning. Recommended application state:

Runtime endpoints

The sandbox surface is nine routes over two resources, sandboxes and snapshots: There is no capability-discovery route. A deployment states what it cannot serve by refusing the request that needs it, with unsupported_capability; see resources and capabilities.

Retry strategy

Rate limits and quotas

Per-organization limits are checked when a request is submitted. A request over a limit is rejected immediately with HTTP 429 and a structured body naming the limit — work is never accepted and then failed later for capacity reasons.
Branch on the stable code, never on the message: Every quota rejection uses the one quota_exceeded code, whichever limit it was: read details.quota.metric to tell them apart, and a limit added later needs no new code to branch on. details.quota also carries the configured limit, the observed current usage (concurrency and storage caps only), and retry_after_seconds (rate limits only). Limited metrics: