Skip to content
NebulaCtrldocs
HTTP API

HTTP API

Authenticate to the NebulaCtrl HTTP API with an API token or a browser session, name the organization to act in, and read its errors, pages and event streams.

The control plane serves a JSON API under /api/v1 on the same address as the console. The console is a client of this API, so anything you can do there you can script. This page covers what every endpoint shares. Endpoints lists each operation.

NebulaCtrl is in beta. The HTTP API may still change before 1.0. Each change is announced in the changelog.

Quick reference

Base URLYour installation's address, then /api/v1, for example https://nebula.example.com/api/v1.
DescriptionGET /api/openapi.json on your installation returns the OpenAPI document for the version it runs. It needs no credential.
FormatRequests and responses are JSON. Identifiers are UUIDs. Times are RFC 3339. Every response object carries a $schema property that names a schema this server does not serve; ignore it.
CredentialsAn API token in Authorization: Bearer, or the nebula_session cookie.
OrganizationX-Nebula-Organization header. Optional with an API token, required with a session.
Errorsapplication/problem+json with a detail sentence.
Pagescursor and limit query parameters. limit is 1 to 200, default 50.
StreamsServer-sent events, for deployments, logs, builds and organization changes.

Authenticate

Every /api/v1 endpoint refuses a request without a credential, except the sign-in handshake (/api/v1/auth/config, /start and /callback), the Git provider callbacks, a shared invitation, GET /api/v1/version and GET /api/v1/releases. The generated reference marks each operation that needs one.

API tokens

Use an API token for scripts and CI. A token belongs to one organization and has one role: viewer, deployer or admin. It can be limited to chosen projects. A token is not read-only. It carries its role's permissions across the whole API: a viewer token reads, a deployer token also deploys and changes services, and an admin token also creates clusters, nodes and projects and manages members. Give a script the lowest role that does its job, and store the token like a password.

Send the token in the Authorization header:

curl -H "Authorization: Bearer TOKEN" https://nebula.example.com/api/v1/projects

TOKEN is the full token, nbl_ followed by an identifier and a secret. The secret is shown once, when you create the token. A token cannot create tokens.

Sessions

The console signs you in through your identity provider and keeps the session in the nebula_session cookie. A script can use the same cookie, but an API token is the supported way to script.

Two extra rules apply to a request authenticated by cookie:

  • Name the organization with the X-Nebula-Organization header.
  • Send X-Nebula-CSRF: 1 on every request that is not GET, HEAD or OPTIONS. Without it, the control plane treats the request as unauthenticated.

Organizations

Most endpoints act in one organization. A token already belongs to one, so it needs no header. If you send X-Nebula-Organization with a token, it must hold that token's organization, or the request fails with 403. With a session, the header is required and must hold the id of an organization you belong to.

GET /api/v1/organizations lists the organizations you belong to, with their ids.

curl -H "Authorization: Bearer TOKEN" -H "X-Nebula-Organization: ORG_ID" https://nebula.example.com/api/v1/projects

Errors

A failed request answers with a 4xx or 5xx status and a application/problem+json body:

{
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "validation failed",
  "errors": [
    { "message": "expected number <= 200", "location": "query.limit", "value": 500 }
  ]
}

detail is a sentence that says what is wrong and, usually, what to do. errors appears when a field is at fault: location names it, message says why. Show detail to people and match on status.

StatusMeaning
400The request is invalid in a way that names no field, or an X-Nebula-Organization header is not an organization id.
403There is no valid credential, the credential's role cannot do this, or it names an organization it does not belong to. A request without a credential gets 403, not 401.
404The resource does not exist.
409The request conflicts with the current state, for example starting a deployment when the project is already running its maximum number of concurrent deployments.
422A field is invalid. errors names it.
429A client address sent too many requests to a rate-limited endpoint. See Limits.
499The client closed the connection before the request finished.
500A defect in the control plane. The detail carries a request id; quote it when you report the problem.
504The request took longer than the server allows.

Every response carries an X-Request-Id header. Quote it when you ask for help.

Pages

List endpoints that can be long take two query parameters and return one page:

ParameterMeaning
limitItems per page, 1 to 200. Default 50. A value outside the range answers 422, naming query.limit.
cursorThe nextCursor of the previous response. Omit it for the first page. The value is opaque: pass it back unchanged. A cursor the server did not issue answers 422.

The response holds items (an empty array, never null) and nextCursor. nextCursor is absent on the last page.

curl -H "Authorization: Bearer TOKEN" "https://nebula.example.com/api/v1/projects?limit=100&cursor=CURSOR"

A few lists, such as the built-in template catalog and a cluster's mesh peers, return everything in one response and take no parameters. The endpoint reference shows which operations take cursor.

Event streams

Four endpoints answer with a server-sent event stream (text/event-stream) instead of one JSON body:

EndpointEventsCloses
GET /api/v1/deployments/{id}/streamstate: the deployment's state, blocker, message and stages, sent when any of them changesAfter the lifetime below. No event follows a final state, so close the connection when you see one
GET /api/v1/environments/{id}/logs/streamlog: one log lineAfter the lifetime below
GET /api/v1/builds/{id}/logs/streamlog: one build log lineAfter the lifetime below
GET /api/v1/events/streamchange: what changed in the organization; resync: hints were dropped, so refetch what you showAfter the lifetime below

A stream behaves the same way on every endpoint:

  • It is a normal authenticated request, so send the same headers as for any other call. A browser's EventSource cannot send headers; read the response with fetch instead, as the console does.
  • A comment line (:) is sent as a heartbeat every 5 seconds, or every 15 seconds on the organization stream.
  • The control plane checks the credential again every 30 seconds and closes the stream if the token was revoked or its role or projects changed. The deployment stream also re-reads the deployment's state every second.
  • A stream ends after 15 minutes, or 30 minutes for the organization stream. Reconnect, and read the current state again: events sent while you were disconnected are not replayed.
  • One credential can hold 4 streams at once, or 8 organization streams. Past that, the stream opens, sends a comment that starts with refused: and closes.
  • A stream that is refused after it opened still has the status 200. The reason arrives as a refused: comment. The deployment stream closes without a comment when the credential cannot read the deployment.
  • If a reader falls too far behind, the control plane drops frames for it instead of waiting.
curl -N -H "Authorization: Bearer TOKEN" https://nebula.example.com/api/v1/deployments/DEPLOYMENT_ID/stream
event: state
data: {"state":"pending","stages":[]}

Next steps

On this page