Skip to main content

Configuration API

The Configuration API lets an administrator create, read, update, and delete DIAL Core configuration entities while Core runs. Core stores each entity as a blob in the platform bucket of its blob storage, with secrets encrypted, and merges these entities with the config files. This reference is for platform engineers who automate configuration; it assumes you know how DIAL Core is configured.

note

The Configuration API is in preview: its endpoints are marked x-preview: true in the DIAL Core OpenAPI specification and can change without backward compatibility. POST /v1/ops/config/reload and the metadata listing are not marked preview.

Endpoints​

EndpointPurpose
GET, PUT, DELETE /v1/{type}/platform/{name}Read, create or replace, and delete one entity
GET /v1/metadata/{type}/platform/{path}List the stored entities of a type
POST /v1/admin/validateCheck a batch of entities without writing anything
POST /v1/admin/applyWrite a batch of entities
GET /v1/admin/config/file/{type}[/{name}]Read the entities that come from config files
POST /v1/admin/config/file/migrateCopy file entities into the platform bucket
GET /v1/admin/health/configReport entities that failed to load
POST /v1/ops/config/reloadRe-read the config files and rebuild the configuration
POST /v1/admin/system-exportDownload the public and platform buckets as a ZIP archive

{type} is one of models, interceptors, translators, roles, keys, routes, schemas, catalog_schemas, applications, toolsets, settings, policies, or policy_assignments. Every type is bound only to the platform bucket; any other bucket answers 404 with no body. For the full request and response schemas, see the DIAL Core API reference.

Who can call it​

Every endpoint above requires the admin role: the caller must match access.admin.rules and must not use a per-request key. A denied call answers 403. See Access.

warning

An empty access.admin.rules ([]) denies everyone the Configuration API. POST /v1/ops/config/reload is the exception: it uses the resource admin rule, so with empty rules every caller without a per-request key can reload. Leave access.admin.rules out to keep the built-in default instead.

Entity names​

A name must match ^[A-Za-z0-9._%:@\[\]()-]+$ and must not end with /. Toolset names must match ^[A-Za-z0-9-_]+$. Global settings have one entity only, settings/platform/global.

Read an entity​

GET /v1/{type}/platform/{name} answers from the stored blob only:

  • 200 with the entity JSON, its name, status: "valid", and an ETag header.
  • 304 when If-None-Match matches the stored ETag.
  • 404 when no blob exists. An entity defined only in a config file also answers 404; read it through /v1/admin/config/file/{type}/{name}.

An entity that failed to load answers 200 with status: "invalid" and a validationWarnings array of {"field", "message"} objects. The record clears on the first rebuild that accepts the entity.

Secrets are never returned: a key's key, an upstream or interface key, and secretExtraData are omitted. For applications and toolsets, auth_settings.client_secret and code_verifier are removed, and an admin gets client_secret_hint instead.

Create or replace an entity​

PUT /v1/{type}/platform/{name} creates the entity or replaces it, and answers 200 with {"name": "<name>"} and an ETag header. The change applies before the answer on the instance that served it.

curl -X PUT "https://<DIAL_CORE_HOST>/v1/models/platform/gpt-4o" \
-H "Authorization: Bearer <ADMIN_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"type": "chat", "endpoint": "http://model-provider:5000/compat/openai/openai/deployments/gpt-4o/chat/completions"}'

Conditional headers protect concurrent edits:

HeaderEffect
If-None-Match: *Create only. Answers 412 when the entity already exists.
If-Match: <etag>Compare-and-swap. Answers 412 when the stored ETag differs.

A 412 writes nothing.

Secrets on update​

When a PUT replaces a model, key, or route, a secret field that is absent or null keeps the stored value; any string becomes the new secret. Changing a key's secret revokes the old secret, and deleting a key revokes its secret before Core answers.

Errors​

StatusWhen
400The body is empty, is not JSON, is not a JSON object, or does not match the entity type, or the name is invalid. A key without a key, a project, or a role. A schema without a $id. An invalid global settings value.
409Another model, application, toolset, or interceptor already uses the deployment ID. Another key already uses the secret; the secret is never echoed. A schema PUT changes the $id of an existing schema, or reuses another schema's $id.
412A conditional header does not match.
422The entity breaks a configuration rule, such as pricing, interfaces, translator references, or catalog properties, or it references an interceptor that does not exist. The body lists validationWarnings.

With config.write.softValidation: true, a model whose only problem is a reference to a missing interceptor is written and answers 200. Rule violations still answer 422. See Config loading.

Delete an entity​

DELETE /v1/{type}/platform/{name} answers 204 after it removes the blob, 404 when no blob exists, and 412 on an If-Match mismatch. Deleting settings/platform/global answers 204 whether or not it exists, and restores the global settings from the config files. POST on an entity URL answers 405.

List entities​

GET /v1/metadata/{type}/platform/{path} lists the stored blobs of a type, in the same folder format as the resource API. It supports token, limit (default 100, from 0 to 1000), and recursive (default false). Entities that exist only in config files are not listed.

Validate and apply a batch​

POST /v1/admin/validate and POST /v1/admin/apply take a batch of manifests:

{
"precheck": true,
"manifests": [
{
"kind": "Interceptor",
"name": "interceptors/platform/pii-redaction",
"spec": { "endpoint": "http://pii-interceptor:4088/api/v1/interceptor/handle" }
},
{
"kind": "Model",
"name": "models/platform/gpt-4o",
"spec": {
"type": "chat",
"endpoint": "http://model-provider:5000/compat/openai/openai/deployments/gpt-4o/chat/completions",
"interceptors": ["pii-redaction"]
}
}
]
}
  • kind is one of Settings, Schema, CatalogSchema, Translator, Interceptor, Role, Key, Route, Model, ToolSet, Application, Policy, or PolicyAssignment.
  • name is <type>/<bucket>/<name> with the segment that matches the kind. The bucket is platform, or public for Application and ToolSet. Settings is always settings/platform/global.
  • Order. Core processes a batch by kind in the order listed above, so an entity can reference another one later in the same batch.
Endpointprecheck: true (default)precheck: false
/v1/admin/validateWrites nothing. Answers 422 if any entry fails, with the valid entries marked SKIPPED.Writes nothing. Answers 200 with each entry VALID or FAILED.
/v1/admin/applyWrites nothing if any entry fails, and answers 422. Otherwise writes all entries and answers 200.Writes every valid entry, skips each failing one, and answers 200.

Results report APPLIED, APPLIED_INVALID, FAILED, or SKIPPED for each entry. APPLIED_INVALID is a model written under soft validation with an unresolved interceptor reference.

Move file configuration to the platform bucket​

GET /v1/admin/config/file/{type} lists the entities that come from config files, and GET /v1/admin/config/file/{type}/{name} reads one. File keys are hidden: keys answers 403 for every caller.

POST /v1/admin/config/file/migrate copies each file entity that is missing from the platform bucket into a blob, through the same path as apply. The config files are not changed.

{ "types": ["models", "keys"], "dryRun": true }
  • types lists entity types, or ["all"] (default).
  • With dryRun: true, nothing is written, and results report would_migrate, would_skip, or would_fail instead of migrated, skipped, or failed.
  • An entity already in the bucket is skipped with the reason already in blob.
  • Keys are named <project in lowercase>-<first 12 hex characters of the SHA-256 of the secret>. The secret keeps working, and results never include it.
  • Schemas are named after the last path segment of their $id. A name that is not valid, or that collides with another schema, fails the migration; Core does not rename it.

After migration, a blob replaces the file entity of the same name. Remove migrated entities from the config files so the two sources do not drift.

Check configuration health​

GET /v1/admin/health/config answers 200 with:

{
"status": "degraded",
"skipped": [
{ "id": "models/platform/broken-model", "reason": "<why the entity was skipped>" }
]
}

status is ok when no invalid entity is recorded. id is always the canonical ID, also for an entity from a config file.

Export the configuration​

POST /v1/admin/system-export streams the public and platform buckets as one ZIP archive, for backups and migrations:

  • Blobs are exported as stored, so secrets stay encrypted. The encryption_keys/ and credentials/ folders are never exported; keep the key material separately if you need to restore the archive.
  • The export holds the locks of both buckets while it runs, so writes wait and a second export answers 409. The locks expire after 5 minutes, and an export that takes longer fails.
  • A resource written within resources.syncDelay may not be in the archive yet.
  • Once streaming starts, a failure resets the connection and leaves an invalid ZIP. Check that the download completed before you use it.
  • Entry names are blob paths and can contain characters that Windows tools reject, such as :. Extract the archive on Linux or read it with a ZIP library.

The archive holds the full content of both buckets; treat it as confidential. adminSystemExport.stallTimeout is described in Background jobs.

Further reading​