Config API
Config endpoints are nested under tenants. All endpoints require authentication and enforce tenant scope for scoped API keys.
Endpoints
Section titled “Endpoints”| Method | Path | Description |
|---|---|---|
GET |
/api/v1/tenants/:id/config |
Get resolved config |
PUT |
/api/v1/tenants/:id/config/:key |
Set config value |
DELETE |
/api/v1/tenants/:id/config/:key |
Delete config override |
PUT |
/api/v1/tenants/:id/config/batch |
Batch set (up to 200, atomic) |
GET |
/api/v1/tenants/:id/config/inheritance |
Get full inheritance view |
Get resolved config
Section titled “Get resolved config”GET /api/v1/tenants/:id/configReturns resolved config for the tenant with all inheritance applied. Walks up the ancestry path and merges entries.
Response: 200 OK
{ "max_users": { "key": "max_users", "value": 500, "source_tenant_id": "msp-uuid", "inherited": true, "locked": false }, "theme": { "key": "theme", "value": "dark", "source_tenant_id": "root-uuid", "inherited": true, "locked": true }}Each entry tells you:
| Field | Description |
|---|---|
value |
The effective value (any JSON type) |
source_tenant_id |
Which tenant set this value |
inherited |
true if the value came from an ancestor |
locked |
true if no descendant can override this key |
sensitive |
Present and true when the value was set with sensitive: true |
masked |
Present and true when an inherited sensitive value was withheld; value is then null |
Sensitive values
Section titled “Sensitive values”A sensitive value set on an ancestor is inherited, but the API does not return it in a descendant’s config. It comes back masked:
{ "api_secret": { "key": "api_secret", "value": null, "source_tenant_id": "msp-uuid", "inherited": true, "locked": false, "sensitive": true, "masked": true }}The value is returned only when the caller’s key belongs to the tenant that set it (source_tenant_id). A global key is not that tenant, so it also gets the masked entry; it can read the value from the owning tenant’s own config. A tenant’s own sensitive values are returned decrypted to any key that may read that tenant. The same rule applies to /config/inheritance, /api/v1/config/diff and /api/v1/tenants/:id/context.
Set config value
Section titled “Set config value”PUT /api/v1/tenants/:id/config/:keyBody:
{ "value": "any JSON value", "locked": false, "sensitive": false}| Field | Type | Default | Description |
|---|---|---|---|
value |
any | Required | The config value (string, number, boolean, object, array) |
locked |
boolean | false |
Prevent descendants from overriding |
sensitive |
boolean | Key’s current flag | Encrypt at rest with AES-256-GCM. When omitted, the key keeps its current flag (false for a new key). |
A key that an ancestor marked sensitive is always stored sensitive, even when the request sends sensitive: false. An explicit sensitive: false clears a flag that this tenant set itself. When a key is stored as sensitive, the overrides of that key in the tenant’s descendants are stored sensitive too.
Returns 403 CONFIG_LOCKED if the key is locked by an ancestor.
Response: 200 OK. Returns the config entry.
Delete config override
Section titled “Delete config override”DELETE /api/v1/tenants/:id/config/:keyRemoves the tenant’s override for this key. If an ancestor has set the key, the tenant reverts to inheriting that value.
Response: 204 No Content
Batch set config
Section titled “Batch set config”PUT /api/v1/tenants/:id/config/batchSets up to 200 config keys in a single atomic transaction. If any key is locked by an ancestor or any entry is invalid, the entire batch rolls back and nothing is written.
Body:
{ "entries": [ {"key": "max_users", "value": 500, "locked": false}, {"key": "theme", "value": "dark", "locked": true}, {"key": "api_secret", "value": "sk_live_abc", "sensitive": true} ]}Each entry in the entries array supports the same fields as the single PUT endpoint (value, locked, sensitive), plus a key field. A key may appear only once; a batch that names a key twice returns 400 VALIDATION_ERROR and writes nothing.
Limits:
- Maximum 200 entries per batch
- Empty arrays return
400 VALIDATION_ERROR
Response: 200 OK when every entry was written:
{ "results": [ { "key": "max_users", "status": "ok", "entry": { "key": "max_users", "value": 500, "locked": false } }, { "key": "theme", "status": "ok", "entry": { "key": "theme", "value": "dark", "locked": true } } ], "succeeded": 2, "failed": 0, "rolled_back": false}When the batch is rolled back, the response is an error, and error.details holds the per-key result with rolled_back: true, succeeded: 0, and status error for every key. The keys that caused the rollback carry their own reason; the others say they were not applied.
403 CONFIG_LOCKEDwhen a key is locked by an ancestor, the same status as a single locked write.400 VALIDATION_ERRORwhen an entry is invalid, for example an emptykeyor a missingvalue.error.details.issuesnames each bad field, such as["entries", 1, "key"].
{ "error": { "code": "CONFIG_LOCKED", "message": "The batch was rolled back and nothing was written: 'max_users' locked by an ancestor", "details": { "results": [ { "key": "max_users", "status": "error", "error": "Config 'max_users' is locked by tenant root-uuid and cannot be overridden" }, { "key": "theme", "status": "error", "error": "Not applied: the batch was rolled back because 'max_users' failed" } ], "succeeded": 0, "failed": 2, "rolled_back": true } }}Get config inheritance
Section titled “Get config inheritance”GET /api/v1/tenants/:id/config/inheritanceReturns the full config with inheritance metadata, showing which level set each value, whether it is inherited or overridden, and the full ancestry chain.
Response: 200 OK
Error responses
Section titled “Error responses”| Code | Status | Description |
|---|---|---|
CONFIG_LOCKED |
403 | Key is locked by an ancestor |
TENANT_NOT_FOUND |
404 | Tenant ID does not exist |
VALIDATION_ERROR |
400 | Invalid request body, invalid batch entry, empty batch, or a tenant id that is not a UUID |
{ "error": { "code": "CONFIG_LOCKED", "message": "Config 'max_users' is locked by tenant root-uuid and cannot be overridden" }}