Documentation menu

API overview

Authenticate to the InstaCloud REST API with an insta_ token, and find which endpoints each token scope can call.

The hosted platform is a REST API at https://api.instacloud.com. The CLI, the console and the MCP server all speak to it, so anything they can do, a script with a token can do too. This reference is generated from the platform's own OpenAPI document and split by which API token can call an endpoint:

  • Account level: the token's owner: their organizations, their tokens, the public catalog. Creating an organization lives here.
  • Organization level: everything under /orgs/{orgId}: members, projects, billing, domains. Creating a project lives here.
  • Project level: everything under /projects/{projectId}: branches, services, deploys, secrets, databases, storage, cron, logs and metrics.

Every endpoint page opens with a Token scope line naming the bindings that may call it, and whether a read-only token can.

Authentication

Send the token as a bearer credential on every request:

curl https://api.instacloud.com/me \
  -H "Authorization: Bearer insta_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx"

A console or CLI session token works on the same header, but sessions expire and belong to a browser or a machine. Integrations, CI and partner systems should use an insta_ API token.

A missing, expired or revoked token answers 401 with a WWW-Authenticate: Bearer header. Requests and responses are JSON; identifiers for organizations, projects, services and tokens are UUIDs, and a branch is addressed by name.

Token scopes

A token is bound at creation and never widens. Three bindings exist:

ScopeBound toReachesMint with
Account-widenothingeverything the owner can do, in every organization, including POST /orgsinsta tokens create <name> --account
Organizationone orgIdthat organization and all of its projects, current and future; can create projects; can mint further tokens inside the organization, never wider than itselfinsta tokens create <name> --org <orgId> (the CLI's default)
Projectone orgId + projectIdthat project only, plus two reads on its organization: GET /orgs/{orgId} and GET /orgs/{orgId}/projects (filtered to itself). Cannot list organizations or mint tokensinsta tokens create <name> --project <projectId>

Each binding also carries an access level: full (the default) or read_only. A read-only token is accepted on GET and HEAD only. It cannot run SQL through POST /projects/{projectId}/database/query or change anything, and this applies to account-wide tokens too.

A token never exceeds its owner. Organization roles still apply on top of the binding: an endpoint that needs admin (deleting a project, changing members) refuses a token whose owner is a member, with 403 {"error":"requires admin role"}.

Minting a token

Over the API, POST /tokens mints a token bounded by the credential that calls it. Omit orgId for an account-wide token; give orgId for an organization token; give both for a project token:

curl -X POST https://api.instacloud.com/tokens \
  -H "Authorization: Bearer $INSTA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"partner-provisioner","orgId":"0f3c…","access":"full","expiresInDays":90}'

The 201 body carries token (shown once) and record (id, scope, access, expiry). An organization token can mint only inside its organization, a project token cannot mint at all, and a request for something wider answers 403 token_scope. The console mints the same tokens from your profile.

From the CLI, insta tokens create prints the plaintext alone on stdout, so $(insta tokens create ci) captures it:

insta tokens create partner-provisioner --org 0f3c…              # organization token, 90 days
insta tokens create deploy --project 7a91… --expires 1y          # project token
insta tokens create automation --account                         # account-wide
insta tokens create dashboard --org 0f3c… --read-only --expires 30d
insta tokens list
insta tokens revoke <tokenId>

--expires takes <n>d, <n>y or never and defaults to 90d.

Provisioning end to end

The three levels compose into one flow. One account-wide token covers all of it; the comments say which narrower binding would do from each step on, once the organization or project it binds to exists.

API=https://api.instacloud.com

# 1. An organization. Only an account-wide token can do this.
ORG=$(curl -s -X POST $API/orgs -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"name":"acme"}' | jq -r .org.id)

# 2. A project in it. An organization token bound to $ORG would do from here on.
PROJECT=$(curl -s -X POST $API/orgs/$ORG/projects -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"name":"web"}' | jq -r .project.id)

# 3. Services on the project. A project token bound to $PROJECT would do from here on.
curl -s -X POST $API/projects/$PROJECT/services -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"type":"postgres","name":"db"}'
curl -s -X POST $API/projects/$PROJECT/services -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"type":"compute","name":"api","port":3000}'

# 4. Deploy an image to the compute service
curl -s -X POST $API/projects/$PROJECT/deploy -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"image":"ghcr.io/acme/api:1.4.2","group":"api","port":3000}'

A new project provisions nothing until you add services, and a compute service has no credentials until you bind them (PUT /projects/{projectId}/secret-bindings/{name}) and deploy again. Both are documented on their endpoint pages.

Errors

StatusBodyMeaning
400{"error": "…"}Validation: a missing field, a name that is not a valid DNS label, a size above the plan's ceiling. The message says what to change.
401{"error": "…"} + WWW-Authenticate: BearerNo token, or an invalid, expired or revoked one.
403{"error": "token_scope", "message": "…"}The endpoint is outside this token's binding, or the token is read-only. The message names the binding. This is not a permissions problem: the owner may well be a member.
403{"error": "requires admin role"}The owner's organization role is too low.
404{"error": "org not found"} / project not foundThe owner is not a member. Existence is not revealed.
409{"error": "…"}A conflict: a taken name, a teardown that failed and kept its rows, a delete of the default branch.
503{"error": "…"}A provider or billing is not configured on this deployment.

Governance and approvals

A request made with an API token is the token's owner acting directly: role checks apply, and the policy a project sets for agents does not. The 202 approval_required envelope some endpoints document is for agent credentials, the identity an agent gets from insta agent setup or the MCP server's OAuth flow; their gated actions pass that policy and may wait for a human to approve in the console. An integration that should be governed like an agent should run under an agent credential rather than an API token; see Agent-native.

Payload casing

Account and token payloads use camelCase (orgId, expiresAt). Organization, project, branch, approval and event rows are returned as stored, in snake_case (org_id, created_at). Each endpoint's schema shows the exact shape.

Self-hosted InstaCloud

This reference documents the hosted platform. A self-hosted box implements the same command surface with a subset of these routes, single-tenant, and answers 501 with guidance for cloud-only concepts; the compatibility table is the per-command record. Point the CLI at your box with --api-url or INSTA_API_URL.

The full document

The platform serves everything it registers, including the browser, console and agent-enrollment routes this reference omits, at GET https://api.instacloud.com/openapi.json. The pages here are regenerated from it with npm run docs:openapi in the instacloud-oss repository, which also splits the operations by token scope.

View as Markdown