# Register a model provider

POST

/v1/providers

```sh
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

- **[bearerAuth](https://docs.kindgi.com/v0.1.9/reference/api.md#bearerauth)**

## Parameters

### Header Parameters

**Idempotency-Key**

string

\>= 1 characters

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

application/json

object

**metadata**

required

object

**id**

required

string

\>= 1 characters

**region**

required

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.

string

\>= 1 characters /^\[a-z]\[a-z0-9-]{0,62}$/

**models**

required

Models this connection exposes. Non-empty. `models[i].name` must be unique within the list.

Array\<object>

\>= 1 items

object

**name**

required

Vendor-facing model id passed to the SDK (e.g. `claude-sonnet-5-5`).

string

\>= 1 characters

**contextWindow**

required

Context window in tokens.

integer

\>= 1

**features**

required

Features this model supports (tool-use, thinking, structured-output, …).

Array\<string>

Allowed values: structured-output vision audio-input audio-output tool-use parallel-tool-use thinking long-context code-execution web-search file-search streaming batch

**cost**

required

USD per 1K tokens. An adapter may take more rate fields (see the adapter’s README).

object

**promptUsdPer1kTokens**

required

number

**completionUsdPer1kTokens**

required

number

**_key_**

additional properties

any

**p95LatencyMs**

Best-effort p95 latency estimate in milliseconds. Varies by model.

number

**maxOutputTokens**

Fallback cap on output tokens. Adapters that require `max_tokens` on every request (e.g. Anthropic) use this when `ModelCallInput.maxOutputTokens` is unset.

integer

\>= 1

**sampling**

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.

boolean

**thinking**

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

**mode**

required

`adaptive`: on unless turned down. `always`: on, and it can only be lowered.

string

Allowed values: adaptive always

**lowest**

required

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`).

string

\>= 1 characters

**description**

Short per-model description surfaced in logs.

string

**defaultModel**

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.

string

\>= 1 characters

**attributes**

Soft attributes for preference-ranking (`local`, `lower-cost`, `higher-accuracy`, …). Matched by string equality against `Preference.feature`.

Array\<string>

**description**

Short human description of the connection.

string

**capabilityKind**

Kind of resource this provider fulfils. Absent = `llm-inference`. Adapters for other kinds (embedding, gpu-compute, sandbox-exec, …) set this explicitly.

string

**fallback**

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`.

boolean

**labels**

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

<= 32 properties

**_key_**

additional properties

string

<= 256 characters

**adapter_id**

required

The adapter that instantiates this provider (e.g. `@kindgi/adapter-model-gemini`). Must be registered for the tenant.

string

\>= 1 characters

**secret_ref**

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

**envName**

required

string

\>= 1 characters

**name**

required

string

\>= 1 characters

**adapter_config**

The adapter’s connection settings: flat, non-secret values (a cloud project, a base URL). Each adapter documents its keys. Credentials go in `secret_ref`, never here.

object

**_key_**

additional properties

Any of:

- **string**

  string

- **number**

  number

- **boolean**

  boolean

**send_traceparent**

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.

boolean

## Responses

### 201

Provider registered.

application/json

object

**providerId**

required

string

##### Examplegenerated

```json
{
  "providerId": "example"
}
```

### 400

Validation failed (see `details.reason`).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 401

Missing / malformed / expired / revoked bearer token.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 409

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. Or `env-not-served`: this runtime keeps every value under the env it serves (whoami’s `servedEnv`, with `refusesOtherEnvs`), and the secret reference names another. Name it under `details.servedEnv`. Nothing is stored.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 413

The request body is over the API’s limit (`request-too-large`; `details.maxBytes` says how much).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 422

`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.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 500

Server error (unmapped domain code or framework crash).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

## See also

- [`GET /v1/providers`: List model providers](https://docs.kindgi.com/v0.1.9/reference/api/operations/providerslist.md)
- [Models](https://docs.kindgi.com/v0.1.9/guides/models.md)
- [Error code `env-not-served`](https://docs.kindgi.com/v0.1.9/reference/errors.md#env-not-served)
- [Error code `provider-config-invalid`](https://docs.kindgi.com/v0.1.9/reference/errors.md#provider-config-invalid)
- [Error code `provider-key-in-use`](https://docs.kindgi.com/v0.1.9/reference/errors.md#provider-key-in-use)
- [Error code `request-too-large`](https://docs.kindgi.com/v0.1.9/reference/errors.md#request-too-large)
- [Every error code](https://docs.kindgi.com/v0.1.9/reference/errors.md)
