Register a model provider
const url = 'https://example.com/v1/providers';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"metadata":{"id":"example","region":"example","models":[{"name":"example","contextWindow":1,"features":["structured-output"],"cost":{"promptUsdPer1kTokens":1,"completionUsdPer1kTokens":1},"p95LatencyMs":1,"maxOutputTokens":1,"sampling":true,"thinking":{"mode":"adaptive","lowest":"example"},"description":"example"}],"defaultModel":"example","attributes":["example"],"description":"example","capabilityKind":"example","fallback":true,"labels":{"additionalProperty":"example"}},"adapter_id":"example","secret_ref":{"envName":"example","name":"example"},"adapter_config":{"additionalProperty":"example"},"send_traceparent":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/providers \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "metadata": { "id": "example", "region": "example", "models": [ { "name": "example", "contextWindow": 1, "features": [ "structured-output" ], "cost": { "promptUsdPer1kTokens": 1, "completionUsdPer1kTokens": 1 }, "p95LatencyMs": 1, "maxOutputTokens": 1, "sampling": true, "thinking": { "mode": "adaptive", "lowest": "example" }, "description": "example" } ], "defaultModel": "example", "attributes": [ "example" ], "description": "example", "capabilityKind": "example", "fallback": true, "labels": { "additionalProperty": "example" } }, "adapter_id": "example", "secret_ref": { "envName": "example", "name": "example" }, "adapter_config": { "additionalProperty": "example" }, "send_traceparent": true }'Body is a full ProviderMetadata. Server validates shape: provider-level id + region non-empty; models[] non-empty with unique name per entry; per-model contextWindow positive integer; per-model features against the closed enum; per-model cost non-negative; optional per-model p95LatencyMs / maxOutputTokens well-shaped; optional labels within their limits — same rules as @kindgi/capabilities.createProviderRegistry. Secrets (API keys, endpoints) are NOT part of the wire shape; deployments store them inside the binding. When the runtime has the adapter the body names, that adapter checks the registration first (its adapter_config, the metadata and the presence of secret_ref; static: no network, no secret read): a problem refuses it with 422 provider-config-invalid. A model provider’s key is used by its provider only: a secret_ref naming a secret that a tool, an MCP endpoint or a webhook endpoint already uses is refused (409 provider-key-in-use), and from then on the name is refused to tools and endpoints.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per docs/API-ROUTE-CONVENTIONS.md §3.1). A retry sent while the first request still runs is answered 409 idempotency-key-in-flight with Retry-After, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets 409 idempotency-key-replay-withheld, with the first request’s status and when it succeeded (from 0.1.5).
Request Bodyrequired
Section titled “Request Bodyrequired”object
object
Region the connection routes to. All models on this provider share the region (the same models served from two clouds or regions are separate providers). 'unspecified' when not region-scoped.
Models this connection exposes. Non-empty. models[i].name must be unique within the list.
object
Vendor-facing model id passed to the SDK (e.g. claude-sonnet-5-5).
Context window in tokens.
Features this model supports (tool-use, thinking, structured-output, …).
USD per 1K tokens. An adapter may take more rate fields (see the adapter’s README).
object
Best-effort p95 latency estimate in milliseconds. Varies by model.
Fallback cap on output tokens. Adapters that require max_tokens on every request (e.g. Anthropic) use this when ModelCallInput.maxOutputTokens is unset.
Whether the model takes sampling settings (temperature). false: its API rejects a non-default value, so the call goes without one and the answer’s warnings say so (sampling-unsupported). Absent: it takes them.
How the model thinks before it answers, so a call that wants as little as it allows (a judge’s) gets it. Absent: it doesn’t think, or nothing is known.
object
adaptive: on unless turned down. always: on, and it can only be lowered.
The vendor’s own setting for the least thinking: for Anthropic disabled, between_tools or an effort (low); for Gemini a thinking level (low, minimal); for OpenAI a reasoning effort (low, none).
Short per-model description surfaced in logs.
The model to use when an agent doesn’t choose: one of models[].name. When candidates rank equally, it comes before the provider’s other models; without it, ties break by model name. A preset sets it. A runtime before 0.1.4 ignores it.
Soft attributes for preference-ranking (local, lower-cost, higher-accuracy, …). Matched by string equality against Preference.feature.
Short human description of the connection.
Kind of resource this provider fulfils. Absent = llm-inference. Adapters for other kinds (embedding, gpu-compute, sandbox-exec, …) set this explicitly.
A fallback serves a capability only when no other provider satisfies it (e.g. kindgi dev’s scripted dev-echo); an agent turn routed to one carries a fallback-provider warning. Absent = false.
Bookkeeping, such as who manages the provider; the router ignores labels. At most 32 keys; a key is 1-63 lowercase letters and digits, with ., -, _ or / inside; a value is at most 256 characters. The convention key kindgi.com/managed-by names the manager (kindgi-dev, kindgi-deploy:<environment>). Out of bounds: 400 invalid-provider, reason invalid-labels.
object
The adapter that instantiates this provider (e.g. @kindgi/adapter-model-gemini). Must be registered for the tenant.
The tenant secret holding the adapter’s credential (an API key, a service-account key). Absent when the adapter needs none or finds its own (a cloud’s default credentials).
object
Send each model call’s W3C traceparent to this provider, as a request header, so its request logs can be matched to the run. Ids only, never content. Default false: nothing about a run’s trace leaves the deployment unless a registration opts in. The runtime enforces it; an older runtime ignores the field and sends none.
Responses
Section titled “Responses”Provider registered.
object
Examplegenerated
{ "providerId": "example"}Validation failed (see details.reason).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Missing / malformed / expired / revoked bearer token.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Provider already registered at that id. Or provider-key-in-use: secret_ref names a secret that a tool (its current version), an MCP endpoint, a webhook endpoint or a webhook trigger uses; details.usedBy lists each (kind, id, a tool’s version). Store the provider’s key under its own name. Nothing is stored.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The request body is over the API’s limit (request-too-large; details.maxBytes says how much).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}provider-config-invalid: a send_traceparent that isn’t a boolean, or the provider’s adapter refuses the registration (its adapter_config, its metadata, or a missing secret_ref); details.issues lists each (path, a JSON pointer, and message), the registration’s own fields first, as other validation errors do. Nothing is stored.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Server error (unmapped domain code or framework crash).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}