Provider schemas
credential_schema and configuration_schema — build forms from them, and let 422 be your validator.
Every provider card carries two JSON Schemas (Draft 2020-12). They are the contract between the catalog and every credential you store:
credential_schema— the secret fields.{"api_key": ...}for most providers; key pairs for AWS-style (access_key_id+secret_access_key); a whole nested service-account object for Google. Everything under it is encrypted at rest and returned only masked.configuration_schema— the non-secret settings stored next to the secret: region, base URL, model id. Stored and returned in the open.
Build forms, don't hardcode
The schemas are self-describing enough to render a form: required lists the mandatory fields, properties.*.title gives labels, enum gives dropdowns (e.g. model pickers on LLM providers), const pins discriminators. A UI that renders from the schema survives every catalog update without a release — this is exactly how the operator panel works.
import httpx
provider = httpx.get(
f"{CREDS_BASE}/v1/providers/deepl_api",
headers={"Authorization": f"Bearer {TOKEN}"},
).json()
schema = provider["credential_schema"]
print(schema["required"]) # ['api_key']
print(list(schema["properties"])) # ['api_key']
Validation happens on write
POST /v1/credentials and every rotation validate your credentials object against credential_schema and your configuration against configuration_schema. Mismatches answer 422 with a dedicated code per object:
CREDENTIAL_SCHEMA_VALIDATION_FAILED— the secret object is wrong;CONFIGURATION_SCHEMA_VALIDATION_FAILED— the configuration is wrong.
error.details.errors carries field pointers and messages — but never your submitted values, so the response is safe to log. Unknown fields fail too: most schemas set additionalProperties: false, and what the schema does not allow, the store will not keep. A worked example: cookbook scenario 10.
Secret fields vs configuration fields is a security boundary
A vendor's region or model id lives in configuration — visible in every read. Anything that grants access lives in credentials — encrypted, masked, and revealable only via the operator reveal. When a vendor's docs are ambiguous about a field, the catalog places it on the safe side.