Cost Center Admin API
Manage a tenant's valid project codes and the cost-center validation toggle.
Scope
Admin API for managing a tenant's valid project codes and the cost-center validation toggle: how to authenticate, the single-code and batch CRUD endpoints, bulk CSV/JSON import with a dry-run preview, value validation, and the config toggle.
Per-endpoint reference is intentionally condensed here. The live, always-current reference lives at analytics.theblockbrain.ai/docs (Scalar, auto-rendered from the OpenAPI spec). This page covers the narrative, auth model, and request/response examples.
Overview
The Cost Center Admin API manages a tenant's list of valid project codes and the cost-center validation toggle. Codes arrive through the single-code REST endpoint, the JSON batch endpoints, or a bulk CSV/JSON import, and any candidate value can be checked against the tenant's active codes.
Base URL: https://analytics.theblockbrain.ai
Base path: /api/v1/admin/cb-limit/cost-center
Base path
/api/v1/admin/cb-limit/cost-center
Host
https://analytics.theblockbrain.ai
Auth
Bearer JWT or API key (sk-), role admin. The acted-on tenant is derived from the token.
When is validation enforced? Only when the tenant's validation toggle is on (see
PUT /config) and at least one active code is configured. Otherwise any value is accepted. Each JSON batch endpoint accepts up to 10,000 codes per request. Use/importfor larger loads.
API summary
Every endpoint the Cost Center Admin API exposes. Paths are relative to the base path /api/v1/admin/cb-limit/cost-center. Full request and response detail is in Scalar.
Codes
GET
/codes
List the tenant's project codes (paginated, searchable, sortable).
Codes
POST
/codes
Add or update a single project code (idempotent upsert).
Codes
DELETE
/codes
Delete a project code by its value.
Codes
POST
/codes/batch
Add or update many codes in one request.
Codes
POST
/codes/batch-set-active
Activate or deactivate many codes (pause or resume).
Codes
POST
/codes/batch-delete
Delete many codes in one request.
Validation
POST
/validate
Check a value against the tenant's active codes.
Import
POST
/import
Bulk import codes from a CSV or JSON file.
Import
POST
/import/preview
Dry-run an import and classify every row.
Config
GET
/config
Read the validation toggle.
Config
PUT
/config
Set the validation toggle on or off.
Authentication
Every endpoint expects a Bearer token in the Authorization header:
Two kinds of credential are accepted, and both go through the same header:
Zitadel JWT
a JWT (header.payload.signature)
Verified against Zitadel (JWKS, with token introspection as a fallback). This is the existing access-token path.
API key
sk- prefixed key
Introspected through Blocky. The key authorizes by its own role, so it must carry the admin role, the same bar a user token has to clear.
Every example below uses
$TOKEN, which can hold either credential. A missing or malformed token returns401, and a token whose role is below admin returns403.
Shared schemas
ProjectCode
label is nullable. source is one of rest-api, csv-import, json-import, admin-manual. Codes match case-insensitively (trimmed, lower-cased), and the display casing is whatever was last written.
Problem (422, application/problem+json)
Returned only by POST /validate when the value does not match an active code. All other errors return a plain { "error": "…" } JSON body.
Codes
GET /codes
List the tenant's project codes. Paginated list with a total count. The admin view shows paused codes too. Default sort is newest first.
Query parameters
search
string
No
Case-insensitive substring match on code or label. Minimum length 1.
activeOnly
"true" | "false"
No
false
Pass true to hide paused codes (what the dropdown does).
from
date (ISO)
No
Inclusive lower bound on createdAt.
to
date (ISO)
No
Exclusive upper bound on createdAt.
page
integer ≥ 1
No
1
1-based page number.
pageSize
integer 1..100
No
25
Rows per page. Values above 100 return 400.
sortBy
code | createdAt | active
No
createdAt
Sort column.
sortOrder
asc | desc
No
desc
Sort direction.
Responses
200
{ items, totalCount, page, pageSize }
Paginated project codes.
400
{ error }
Invalid query parameters.
401
Missing or invalid token.
403
Missing the cost-controls permission.
Request
Response (200)
POST /codes
Add or update a single project code. Idempotent upsert on the case-insensitive code. This is the endpoint a client's automation calls to keep codes in sync: create on project start, pause with active: false. Re-adding a code refreshes its casing, label, and active flag but keeps its id and audit columns.
Omitting
activeresets it totrue, so a re-add reactivates a paused code.
Body · application/json
code
string 1..256
Yes
The project code. Trimmed before storing.
label
string ≤ 512
No
Human label. Defaults to null.
active
boolean
No
Defaults to true. Set false to pause without deleting.
Responses
200
ProjectCode
The upserted code.
400
{ error }
Code missing, blank, or too long.
401 / 403
Unauthorized or forbidden.
Request
Response (200)
DELETE /codes
Delete a project code. Hard-deletes the code matching code (case-insensitive) for the calling tenant. The client's automation calls this when a project closes.
Query parameters
code
string
Yes
The code value to delete. Minimum length 1.
Responses
200
{ "deleted": true }
A row was removed.
400
{ error }
The code query parameter is missing.
404
{ error }
No code matched.
401 / 403
Unauthorized or forbidden.
Request
POST /codes/batch
Add or update project codes (batch). The multi-code sibling of POST /codes. Upserts many codes in one JSON request (idempotent, case-insensitive). Each entry carries its own label and active (defaults true). Duplicate codes within the request are collapsed case-insensitively, first wins (same as the file import). Returns the count of unique codes written. Use /import for very large file loads.
Body · application/json
codes
array 1..10000
Yes
Non-empty array of code entries.
codes[].code
string 1..256
Yes
The project code.
codes[].label
string ≤ 512
No
Human label.
codes[].active
boolean
No
Defaults to true.
Responses
200
{ "upserted": number }
Count of unique codes written.
400
{ error }
Empty array, over 10,000 entries, or a blank/oversized code.
401 / 403
Unauthorized or forbidden.
Request
Response (200)
POST /codes/batch-set-active
Activate or deactivate project codes (batch). Flips only the active flag of every listed code (case-insensitive). Unlike POST /codes, it never rewrites the label or source, so a multi-select pause or resume keeps each code's display metadata intact. Deactivating pauses codes: they stop validating and leave the dropdown without being deleted. Returns the number of existing codes updated (codes that do not exist are ignored).
Body · application/json
codes
string[] 1..10000
Yes
Non-empty array of code values, each 1..256 chars.
active
boolean
Yes
true to activate, false to pause.
Responses
200
{ "updated": number }
Count of existing codes updated.
400
{ error }
Empty array, missing or non-boolean active, or an oversized code.
401 / 403
Unauthorized or forbidden.
Request
Response (200)
POST /codes/batch-delete
Delete project codes (batch). Hard-deletes every listed code (case-insensitive) in one request. Backs the admin UI's multi-select delete. Returns the number actually removed, which may be fewer than requested when some codes did not exist.
Body · application/json
codes
string[] 1..10000
Yes
Non-empty array of code values, each 1..256 chars.
Responses
200
{ "deleted": number }
Count of codes actually removed.
400
{ error }
Empty array, over 10,000 entries, or an oversized code.
401 / 403
Unauthorized or forbidden.
Request
Response (200)
Validation
POST /validate
Validate a project code. Returns { "valid": true } when the value matches one of the tenant's active codes. Matching is case-insensitive and trims whitespace. A paused code counts as invalid.
Body · application/json
value
string
Yes
The value to check. Minimum length 1 after trimming.
field
string
No
Present in the contract. Only project-code is validated today.
Responses
200
{ "valid": true }
The value matches an active code.
400
{ error }
value is missing or blank.
422
Problem (problem+json)
The value is not a valid project code.
401 / 403
Unauthorized or forbidden.
Request
Response (422)
Import
POST /import
Import project codes from a CSV or JSON file. Multipart upload. Codes are trimmed, blank rows skipped, and in-file duplicates collapsed case-insensitively (first wins). Each code is upserted so re-importing is idempotent. Imported codes are always set active. A row whose code exceeds 256 characters or whose label exceeds 512 is reported as an error row and not written (mirrors the API field caps). Max 5 MB and 200,000 rows.
Body · multipart/form-data
file
binary
Yes
CSV (header row with a code column, optional label) or JSON.
format
"csv" | "json"
Yes
How to parse the file.
Accepted JSON shapes
Responses
200
{ imported, duplicatesInFile, errors[] }
Import summary. Each error carries { row, code, reason }.
400
{ error }
File missing, bad format, or unparseable (no code column, invalid JSON, wrong shape).
413
{ error }
File exceeds the size or row limit.
401 / 403
Unauthorized or forbidden.
Request
Response (200)
POST /import/preview
Preview a CSV/JSON import without writing anything. Parses the file and classifies every row in file order (new, update, duplicate, error) so the admin can review before importing. A row is an error when its code is empty or its code/label exceeds the field caps (256 / 512). update versus new is resolved against the tenant's existing codes. Writes nothing. The entries array is capped at 2000 rows, but the summary counts every row.
Body · multipart/form-data
file
binary
Yes
Same file shapes as /import.
format
"csv" | "json"
Yes
How to parse the file.
Responses
200
{ entries[], totalRows, truncated, summary }
Per-row classification plus a summary over all rows.
400
{ error }
File missing, bad format, or unparseable.
413
{ error }
File exceeds the size or row limit.
401 / 403
Unauthorized or forbidden.
Response (200)
Config
GET /config
Get the tenant's cost-center validation toggle. Returns whether validation is switched on for the calling tenant. A tenant with no settings row reports false.
Responses
200
{ "validationEnabled": boolean }
Toggle state.
401 / 403
Unauthorized or forbidden.
Request
Response (200)
PUT /config
Set the tenant's cost-center validation toggle. Switches validation on or off. Turning it on for the first time seeds a settings row from the system default. Turning it off, or back on, only touches this toggle and the audit columns and preserves all other config. Validation enforces only once the toggle is on and at least one active code is configured.
Body · application/json
validationEnabled
boolean
Yes
The new toggle state.
Responses
200
{ "validationEnabled": boolean }
Updated toggle state.
400
{ error }
validationEnabled is missing or not a boolean.
401 / 403
Unauthorized or forbidden.
Request
Last updated

