Authorize

#Configuration & Config Schemas

Apps and workflows read their settings through config.*. A value there comes from one of two layers: the definition (the author's app spec or workflow) or the activation (what a tenant stored when it set the definition up). A config schema is a JSON Schema you register once and name from the definition, and every write to that scope is then validated against it.

Authenticate with a Personal Access Token (HTTP Basic auth). Enter it once in the Authorize bar above and every Try it below uses it.

#Two layers

Layer Where it lives Who writes it Behaviour
Definition config.<scope> in the app spec, config.env in the workflow definition The definition's author Live: an edit reaches every tenant that has not set that key itself.
Activation Stored per tenant, integration or principal, written through the activation endpoints The activating tenant Wins over the definition, key by key. Only what the tenant supplied is stored.

At render and at run time the two are merged per key: definition values underneath, stored values on top. The merge is top-level only, so a stored nested object replaces the definition's nested object whole.

#Scopes

Scope Definition key Written with Read as
app config.app PUT /apps/{specId}/config config.app.*
integration config.integration POST /apps/{specId}/integrations, PUT /apps/{specId}/integrations/{integrationId} config.integration.*
company config.company PUT /apps/{specId}/principals/company/self config.company.*
team config.team PUT /apps/{specId}/principals/team/{teamId} config.team.*
user config.user PUT /apps/{specId}/principals/user/self, or a user id config.user.*
env config.env POST /workflows/{id}/activate, PUT /workflows/{id}/config (workflows only) config.env.*
  • company is your own tenant. Its principal id is the calling tenant's id, and self resolves to it. There is one company config per activation, shared by every integration under it. An external company's id is refused with a 403.
  • team takes a team id. self is refused with a 400, because a caller may belong to several teams.
  • user takes self (the caller) or a user id.
  • The scope names are a closed set. An app spec whose config or configSchemas names any other key is refused at publish with a 422 (spec_document.configSchemas.apps: Invalid key in record). A workflow accepts env only.

#Declare a schema

#1. Register the schema

Register it in the tenant that owns the definition:

POST/schema/schemasRegister a config schemaAPI docs ↗Try it

A name may contain letters, digits, _ and -. A definition's reference to it is matched case-insensitively.

#2. Name it from the definition

An app spec names one schema per scope under configSchemas, next to its definition values under config:

{
  "config": {
    "app": { "threshold": 50 },
    "company": { "tag": "standard" }
  },
  "configSchemas": {
    "app": "fleet_app_config",
    "company": "fleet_company_config",
    "user": "fleet_user_config"
  }
}

A workflow definition names one schema, for env:

{
  "config": { "env": { "REGION": "us" } },
  "configSchemas": { "env": "fleet_workflow_env" }
}
  • A scope that is omitted, or set to null, is not validated. An empty string is refused at publish. A workflow that has a configSchemas block must set env in it, to a name or null.
  • The name is not looked up at publish. A definition that names a schema nobody registered publishes fine, and the first write to that scope fails with UNKNOWN_CONFIG_SCHEMA. Register the schema first.

Required env keys in a workflow. With configSchemas.env declared, list required keys in the schema's required. A null value in the definition's config.env is then refused at publish, so the two ways of marking a key required cannot disagree. Without a schema, a null value in config.env marks a key the activating tenant must supply. Activation and config writes fail with 422 and details.missing_env_vars until every such key is present. false, 0 and "" count as present.

#Consumers in other tenants

A tenant that activates a definition you shared needs to read your schema too. Add every schema the definition names as a json_schema member of the manifest that shares it (see Share with Another Tenant):

{
  "members": [
    { "resource_type": "app_spec", "resource_name": "fleet-app" },
    { "resource_type": "json_schema", "resource_name": "fleet_app_config" },
    { "resource_type": "json_schema", "resource_name": "fleet_company_config" }
  ]
}

Without that member, every config write the consumer makes to the scope fails with UNKNOWN_CONFIG_SCHEMA, and so does every run of a shared workflow whose configSchemas.env names it. A consumer that registers a schema of the same name in its own tenant is validated against its own copy instead: the caller's tenant is checked first, then the definition owner's.

#How a write works

Every config write, at every scope, runs the same four steps:

  1. Merge over the stored row. Keys you send are set, a key sent as null is unset, and keys you do not mention keep their stored value. The merge is top-level: a nested object you send replaces the stored one.
  2. Add the definition underneath. The definition's values for the scope go under the merged row. The result is the effective config.
  3. Validate the effective config against the scope's schema:
    • Primitive values are coerced to the declared type. "75" becomes 75 for a number, and 5 becomes "5" for a string.
    • Schema defaults are filled in.
    • Keys the schema does not declare are dropped. This happens when the schema sets additionalProperties: false, or declares properties without setting additionalProperties at all. Set additionalProperties: true to keep them.
  4. Store what the tenant supplied. The validated result is stored, minus the keys that came only from the definition, so a definition value stays live rather than being copied into the row. Filled-in schema defaults are stored.

A key you unset falls back to the definition's value if there is one. If the schema has a default for it, the default is filled in again.

Concurrent writes to the same row are merged on the server, so two partial writes that touch different keys both succeed and both keep their keys.

#Worked example

With fleet_app_config above and "config": { "app": { "threshold": 50 } } in the spec, each write below was made in order:

Request config Stored config afterwards Why
{ "model": "m1" } { "model": "m1", "label": "Default label" } threshold comes from the spec. The default is stored.
{ "threshold": "high" } unchanged (422) "high" is not a number.
{ "threshold": "75", "junk": true } { "model": "m1", "label": "Default label", "threshold": 75 } Coerced. junk is not declared, so it is dropped.
{ "model": null } { "label": "Default label", "threshold": 75 } null unsets.
{ "threshold": null } { "label": "Default label" } The spec's 50 applies again.

The first call to PUT /apps/{specId}/config creates the activation, so it must carry name:

PUT/apps/{specId}/configActivate an app and write its app-scope configAPI docs ↗Try it

#Integration and principal config

An integration's config is validated against configSchemas.integration on create and on update:

POST/apps/{specId}/integrationsCreate an integration with its configAPI docs ↗Try it
PUT/apps/{specId}/integrations/{integrationId}Change one key of an integration's configAPI docs ↗Try it

Principal config is written by natural key. Use company/self for your tenant's company config and user/self for your own:

PUT/apps/{specId}/principals/{type}/{principalId}Write your tenant's company config (type company, principalId self)API docs ↗Try it

A principal write needs the app to be activated first; before that it fails with 404 "App not activated for this tenant." A form or a setPrincipalConfig step inside the app writes through the same path. See Apps.

#Workflow env

POST /workflows/{id}/activate and PUT /workflows/{id}/config both write the tenant's env with the same four steps. The config PUT needs an existing activation. Before one exists it fails with 404 "Workflow is not activated for this tenant."

POST/workflows/{id}/activateActivate with the env keys the schema requiresAPI docs ↗Try it
PUT/workflows/{id}/configOverride one env key; null would unset itAPI docs ↗Try it

A PUT that carries only job_bindings leaves the env untouched. See Job Bindings.

#Reading config back

  • GET /apps/{specId}/config, the integration reads and the principal reads return the stored config. Definition values are merged in at render.
  • GET /workflows/{id}/config returns the effective env: the definition's config.env with the stored values on top. PUT /workflows/{id}/config answers with the same view.

#Reading config at render and run time

Definitions read config with smart values:

  • App render: {{ config.app.threshold }}, {{ config.integration.region }}, {{ config.company.tag }}, {{ config.team.* }}, {{ config.user.theme }}. Each scope is the definition's config.<scope> with the stored row on top. config.integration is the integration the request arrived through, config.company is the activating tenant's company config, and config.user is the caller's.
  • Workflow: {{ config.env.REGION }} in triggers, job step inputs, conditions and auth profiles.

Workflow env is validated again on every run, against the current definition. An edit to the definition can therefore leave an activated tenant unable to run. For example, the edit might stop supplying a key the schema requires. The run is then refused with 400, and one PUT /workflows/{id}/config that supplies the missing keys fixes it.

#Secrets in config

Config is where secret references live. A definition never contains one. Store the value with the secrets API, wire {{ secrets.<scope>.NAME }} into a config key, and have the definition read that key. Secrets covers the scopes and which config may carry which reference.

Give a property that holds a reference "type": "string" in its schema. Validation runs on the reference text, so a reference in a number property fails with 422.

#Auth profiles read config

A root auth profile on an app spec or a workflow reads its credentials from config, for example "token": "{{ config.app.api_token }}" or "password": "{{ config.env.API_PASS }}". Each tenant's own config, including the secret references wired into it, supplies the credential. A job bound into the definition that names the profile uses the same values. See HTTP Auth & External Data.

#Errors

Errors use the standard envelope. Match on error.code and details, not on message.

Situation Status error.code details
The effective config fails the schema 422 VALIDATION_FAILED schema_name, scope, schema_errors[] (plus principal_type on principal writes)
The named schema is not visible to your tenant 422 VALIDATION_FAILED code: "UNKNOWN_CONFIG_SCHEMA", schema_name, scope
A workflow without a schema is missing a required env key 422 VALIDATION_FAILED missing_env_vars[]
First app activation without name 422 VALIDATION_FAILED spec_id
A secret reference in the wrong config 422 VALIDATION_FAILED code: "SECRET_REF_SCOPE_VIOLATION", offending_secret_refs[]
A malformed or scope-less secret reference 422 VALIDATION_FAILED code: "INVALID_SECRET_REF", token
The app spec is not visible to your tenant (for example, its publication was revoked) 403 FORBIDDEN spec_id
Not activated yet (principal write, workflow config PUT) 404 NOT_FOUND spec_id (app) or id (workflow)
A run whose env fails configSchemas.env 400 BAD_REQUEST schema_name, scope: "env", schema_errors[], workflow_id
A run whose configSchemas.env schema is not visible 400 BAD_REQUEST code: "UNKNOWN_CONFIG_SCHEMA", schema_name
A run of a workflow without a schema with a required key unset 400 BAD_REQUEST missing_env_vars[]

A 422 schema_errors entry names the path and the rule, for example /threshold: Instance type "string" is invalid. Expected "number".

#See also