Skip to content

Config API

10 operations. Every call needs a session cookie or an Authorization: Bearer nct_… token unless marked Public. Conventions, errors and scopes

GET/environments/{environmentId}/config-source

Repository binding and status of an environment's nebula.toml

How the environment finds its file (explicit, derived from the services' repositories, or none), whether auto-apply is on, the status of the last check (in_sync, differs, invalid, not_found, unreachable, error) and the state of each service.

ParameterInTypeDescription
environmentIdrequiredpathstring
Response 200 · OK · application/json
EnvironmentConfigSource
  • autoApplybooleanrequired
  • branchstringrequired
  • checkedAtstring (date-time)
  • commitstringrequired
    Commit the file was read at (empty when it was read at the branch head through an API)
  • connectionIdstring
  • fileSha256stringrequired
  • modeenum: explicit | derived | nonerequired
    explicit: set on the environment; derived: the environment's git services share one repository and branch; none: nothing is read
  • pathstringrequired
    Explicit path; empty when the default rule applies
  • repostringrequired
  • resolvedPathstringrequired
    Path the file is read from (or was last read from)
  • EnvironmentConfigService[] | nullrequired
  • statusenum: unknown | in_sync | differs | invalid | not_found | unreachable | errorrequired
  • statusDetailstringrequired
    Why the status is what it is: the first error of the file, why it could not be read, or why auto-apply refused
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

PUT/environments/{environmentId}/config-source

Bind an environment to a nebula.toml in a repository

Sets the repository, branch and path the environment's file is read from, and whether a push stages it (autoApply). connectionId, repo and branch are set together or not at all. Auto-apply needs the admin scope, is refused for principals that need production approval (403) and for production environments (422 production_auto_apply_unsupported); it stages, it never deploys.

ParameterInTypeDescription
environmentIdrequiredpathstring
Request body · application/json · required
ConfigUpdateSourceInput
  • autoApplyboolean
    Stage the file on every push to the branch. Needs the admin scope, a person (not an agent) and a non-production environment. Never deploys.
  • branchstring
    Branch the file is read from
  • connectionIdstring
    Git connection to read the file through; give it together with repo and branch
  • pathstring
    Path of the file in the repository; empty applies the default rule (nebula.toml at the root, else in the build context)
  • repostring
    Repository (owner/name) on the connection
Response 200 · OK · application/json
EnvironmentConfigSource
  • autoApplybooleanrequired
  • branchstringrequired
  • checkedAtstring (date-time)
  • commitstringrequired
    Commit the file was read at (empty when it was read at the branch head through an API)
  • connectionIdstring
  • fileSha256stringrequired
  • modeenum: explicit | derived | nonerequired
    explicit: set on the environment; derived: the environment's git services share one repository and branch; none: nothing is read
  • pathstringrequired
    Explicit path; empty when the default rule applies
  • repostringrequired
  • resolvedPathstringrequired
    Path the file is read from (or was last read from)
  • EnvironmentConfigService[] | nullrequired
  • statusenum: unknown | in_sync | differs | invalid | not_found | unreachable | errorrequired
  • statusDetailstringrequired
    Why the status is what it is: the first error of the file, why it could not be read, or why auto-apply refused
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entityproduction_auto_apply_unsupported
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

DELETE/environments/{environmentId}/config-source

Clear the explicit binding of an environment

Removes the explicit repository binding and switches auto-apply off. Which fields services own stays as it is.

ParameterInTypeDescription
environmentIdrequiredpathstring
Response 204 · No Content

No body.

Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/environments/{environmentId}/config-source/check

Read the bound nebula.toml now

Reads the file at the branch head (or at sha), validates it, compares it with the environment and stores the status. Nothing is applied.

ParameterInTypeDescription
environmentIdrequiredpathstring
Request body · application/json
CheckConfigSourceRequest
  • shastring
    Commit to read; empty reads the head of the bound branch
Response 200 · OK · application/json
EnvironmentConfigSource
  • autoApplybooleanrequired
  • branchstringrequired
  • checkedAtstring (date-time)
  • commitstringrequired
    Commit the file was read at (empty when it was read at the branch head through an API)
  • connectionIdstring
  • fileSha256stringrequired
  • modeenum: explicit | derived | nonerequired
    explicit: set on the environment; derived: the environment's git services share one repository and branch; none: nothing is read
  • pathstringrequired
    Explicit path; empty when the default rule applies
  • repostringrequired
  • resolvedPathstringrequired
    Path the file is read from (or was last read from)
  • EnvironmentConfigService[] | nullrequired
  • statusenum: unknown | in_sync | differs | invalid | not_found | unreachable | errorrequired
  • statusDetailstringrequired
    Why the status is what it is: the first error of the file, why it could not be read, or why auto-apply refused
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/projects/{projectId}/config/apply

Apply a nebula.toml to an environment

Re-plans inside one transaction and stages the file exactly like dashboard edits (generation bumps, discardable, deployed with the next deploy), then runs the immediate-kind settings (scale, uptime, tasks, backups, git link, DNS) one by one. It never deploys unless deploy: true. Without planId it plans and applies atomically; with it, a plan that changed since answers 409 plan_stale (fresh plan in details.plan) and writes nothing. A plan with errors answers 422 config_blocked (plan in details.plan); missing scopes answer 403 insufficient_scope before any write. Principals that need production approval get approval_required for a plan with immediate changes in production. Status 201: staged or partial (read steps); 200: noop or failed. Running an apply again converges.

ParameterInTypeDescription
Idempotency-KeyheaderstringClient-chosen key (<=255 chars). A retry with the same key replays the original response for 24 hours.
projectIdrequiredpathstring
Request body · application/json · required
ApplyConfigRequest
  • deployboolean
    Deploy the services this apply touched that now have staged changes (needs deployments:write; not available to principals that need production approval)
  • environmentstring
    Environment slug (or id); may be omitted when the project has exactly one standard environment
  • onlystring[] | null
    Restrict the apply to these file key paths (prefixes)
  • planIdstring
    Plan id from a plan call: the apply fails with 409 plan_stale (and the fresh plan in details.plan) when anything changed since
  • pruneboolean
    Also delete sub-resources the file owned before and no longer lists
  • ConfigPlanFileSourcerequired
Response 201 · Created · application/json
ConfigApplyResult
  • deploymentsstring[] | nullrequired
  • ConfigPlanrequired
  • planIdstringrequired
  • ConfigAppliedService[] | nullrequired
  • statusenum: staged | partial | failed | nooprequired
  • ConfigApplyStep[] | nullrequired
Errors
  • 401Unauthorized
  • 403Forbiddeninsufficient_scope
  • 404Not Found
  • 409Conflictplan_stale
  • 422Unprocessable Entityconfig_blocked
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/projects/{projectId}/config/export

Export a project as a nebula.toml

Writes the live state of one environment, or of all standard environments (environment=all, factored into a base plus overrides), as a nebula.toml. Keys equal to their default are left out unless defaults=true. Secret values are never written: secrets become secrets.keys entries. The output is deterministic (same state, same bytes). Names that are not unique make the export fail with ambiguous_reference.

ParameterInTypeDescription
projectIdrequiredpathstring
environmentquerystringEnvironment slug, or "all" for every standard environment factored into a base plus [environments.<slug>] overrides; may be omitted when the project has exactly one standard environment
basequerystringWith environment=all: the environment whose values form the base (default: production, else the first)
defaultsquerybooleanAlso write keys whose value equals the default
Response 200 · The project as a nebula.toml: plain text by default, or `{ file, warnings }` JSON with `Accept: application/json`. A clean export plans to zero changes against the state it was generated from. · application/json
ConfigExportResult
  • filestringrequired
    The nebula.toml text
  • ConfigDiagnostic[] | nullrequired
    Things the file cannot express and findings the first apply would report
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/projects/{projectId}/config/plan

Plan a nebula.toml against an environment

Computes what applying the file would do to one environment: services to create, update or leave alone, every changed field labelled staged (takes effect on the next deploy) or immediate (takes effect at once, like the dashboard control), drift against the dashboard, services that are not in the file, and the scopes an apply needs. Secret and generated values are masked. The answer is always 200: check valid and the diagnostics. planId identifies the plan; pass it to apply to refuse an apply whose inputs changed meanwhile. The file is inline (source.file) or read through the environment's repository binding (source.git, needs projects:write). Nothing is written.

ParameterInTypeDescription
projectIdrequiredpathstring
Request body · application/json · required
PlanConfigRequest
  • environmentstring
    Environment slug (or id); may be omitted when the project has exactly one standard environment
  • onlystring[] | null
    Restrict the plan to these file key paths (prefixes), e.g. services.web
  • pruneboolean
    Also delete sub-resources the file owned before and no longer lists (variables, domains, volumes, networks, links, tasks, backups, uptime). Never deletes services or databases
  • ConfigPlanFileSourcerequired
Response 200 · OK · application/json
ConfigPlan
  • ConfigDiagnostic[] | nullrequired
  • ConfigPlanEnvironmentrequired
  • ConfigGuardrequired
  • planIdstringrequired
  • ConfigPlanProjectrequired
  • requiredScopesstring[] | nullrequired
  • schemainteger (int64)required
  • ConfigPlanService[] | nullrequired
  • ConfigSharedPlanrequired
  • ConfigPlanSourcerequired
  • ConfigPlanSummaryrequired
  • validbooleanrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/projects/{projectId}/config/validate

Validate a nebula.toml

Parses the project file strictly (unknown keys, wrong types, limits), resolves it, and checks it statically: kinds, references, bounds, secrets, paths. With environment it also resolves that environment's view and the names the file uses (servers, git connections, destinations) against the project. Invalid input is data, not an HTTP error: the answer is 200 with valid: false and diagnostics (code, file line and column, "did you mean" suggestions). Nothing is written.

ParameterInTypeDescription
projectIdrequiredpathstring
Request body · application/json · required
ValidateConfigRequest
  • map of string
    Files the file refers to (compose_file, files.*.source), keyed by their path relative to the file
  • environmentstring
    Also validate this environment's resolved view and resolve names against the project (slug)
  • filestringrequired
    The nebula.toml text
Response 200 · OK · application/json
ConfigValidateResult
  • ConfigDiagnostic[] | nullrequired
  • environmentsstring[] | nullrequired
    Environment blocks of the file
  • schemainteger (int64)required
  • ConfigValidateService[] | nullrequired
  • validbooleanrequired
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

GET/services/{serviceId}/config-source

Whether a service is managed by a nebula.toml

Which file last applied to the service, when and by whom, and the drift state of every field the file owns (in_sync, file_changed, drift, conflict). Computed from stored fingerprints, without reading the file.

ParameterInTypeDescription
serviceIdrequiredpathstring
Response 200 · OK · application/json
ServiceConfigSource
  • appliedAtstring (date-time)
  • appliedBystring
  • branchstring
  • commitstring
  • ConfigFieldState[] | nullrequired
  • fileSha256string
  • managedbooleanrequired
  • originenum: git | cli | api | mcp | ui
  • pathstring
  • pendingstring[] | nullrequired
    Immediate changes that could not run yet (the service is not deployed)
  • repostring
  • stateenum: in_sync | file_changed | drift | conflict
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.

POST/services/{serviceId}/config-source/release

Stop managing fields of a service with the file

Releases the named fields: the file no longer owns them and later applies leave their live value alone. Changes nothing else.

ParameterInTypeDescription
serviceIdrequiredpathstring
Request body · application/json · required
ReleaseConfigFieldsRequest
  • pathsstring[] | nullrequired
    Field ids to stop managing (the ids of fields in the service's config source)
Response 200 · OK · application/json
ServiceConfigSource
  • appliedAtstring (date-time)
  • appliedBystring
  • branchstring
  • commitstring
  • ConfigFieldState[] | nullrequired
  • fileSha256string
  • managedbooleanrequired
  • originenum: git | cli | api | mcp | ui
  • pathstring
  • pendingstring[] | nullrequired
    Immediate changes that could not run yet (the service is not deployed)
  • repostring
  • stateenum: in_sync | file_changed | drift | conflict
Errors
  • 401Unauthorized
  • 403Forbidden
  • 404Not Found
  • 422Unprocessable Entity
  • 500Internal Server Error

Errors are RFC 9457 application/problem+json with a stable code field.