> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mka1.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Model registry

> Activate models, manage catalog access, and configure organization model defaults.

Every request that names a model, including `model: "auto"`, resolves through your organization's model registry. This guide covers the two layers behind that lookup and the operations that change them.

* The **catalog** is what your organization could activate: its own bring-your-own (BYO) definitions and the cluster models a cluster admin has granted it access to.
* The **registry** is what it has activated. A model name resolves only while the registry holds it.

Access to a cluster model does nothing by itself. Until someone activates the entry, `GET /models` omits it, a request that names it fails to resolve, and `auto` cannot land on it either.

## Before you start

| Requirement | Notes |
| - | - |
| API key | Send it as `Authorization: Bearer <mka1-api-key>` on every request. |
| Owner or admin role | Catalog, registry, and auto-model operations reject a bearer whose organization role is `member` with `403 Organization admin access required`. Listing models and effective prices needs no role. |
| `read:registry` scope | Lets a key list the catalog, the registry, and auto-model overrides. Any member can mint it, but the role check above still applies. |
| `write:registry` scope | Lets a key activate and deactivate entries, define and health-check BYO entries, and set auto-model overrides. Only owners and admins can mint it. Console sessions carry no scopes and pass on role alone. |

The console has the same views under **Admin → Model Registry**, on the **Models**, **Auto models**, **Add model**, and **Raw JSON** tabs. **LLM → Models** shows what your requests can use right now.

## Step 1 - List the models your requests can use

`GET /models` returns the active entries of your registry, minus any whose definition sets `hidden: true` (see [Troubleshooting](#troubleshooting)). Read each item's `capabilities` before you send sampling or reasoning parameters.

Use the SDKs or the CLI to manage the model catalog and registry.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  import { SDK } from '@meetkai/mka1';

  const sdk = new SDK({ bearerAuth: 'Bearer <mka1-api-key>' });

  const models = await sdk.llm.models.list({
    xOnBehalfOf: '<end-user-id>', // optional — attribute the request to one of your end users
  });
  console.log(models.data.map((m) => `${m.id} (${m.modelType})`));

  const model = await sdk.llm.models.get({ modelId: 'meetkai:functionary-pt' });
  console.log(model.capabilities.supportsTemperature, model.capabilities.supportsTopP);
  ```

  ```ts OpenAI SDK theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    apiKey: '<mka1-api-key>',
    baseURL: 'https://apigw.mka1.com/api/v1/llm/',
  });

  const models = await openai.models.list();
  console.log(models.data.map(model => model.id));
  const model = await openai.models.retrieve('meetkai:functionary-pt');
  console.log(model.id);
  ```

  ```python Python SDK theme={null}
  from meetkai_mka1 import SDK

  sdk = SDK(bearer_auth="Bearer <mka1-api-key>")

  models = sdk.llm.models.list(
      x_on_behalf_of="<end-user-id>",  # optional — attribute the request to one of your end users
  )
  print([f"{m.id} ({m.model_type})" for m in models.data])

  model = sdk.llm.models.get(model_id="meetkai:functionary-pt")
  print(model.capabilities.supports_temperature, model.capabilities.supports_top_p)
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;
  using MeetKai.MKA1.Types.Requests;

  var sdk = new SDK(bearerAuth: "Bearer <mka1-api-key>");

  var models = await sdk.Llm.Models.ListAsync(
      xOnBehalfOf: "<end-user-id>" // optional — attribute the request to one of your end users
  );
  foreach (var m in models.Object!.Data)
  {
      Console.WriteLine($"{m.Id} ({m.ModelType})");
  }

  var model = await sdk.Llm.Models.GetAsync(modelId: "meetkai:functionary-pt");
  Console.WriteLine(model.Object!.Capabilities.SupportsTemperature);
  ```

  ```bash CLI theme={null}
  mka1 llm models list -H 'X-On-Behalf-Of: <end-user-id>'

  mka1 llm models get --model-id meetkai:functionary-pt
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'

  curl https://apigw.mka1.com/api/v1/llm/models/meetkai:functionary-pt \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

A list item looks like this:

```json theme={null}
{
  "id": "meetkai:functionary-pt",
  "object": "model",
  "created": 0,
  "owned_by": "meetkai-pt",
  "model_type": "llm",
  "input_modalities": ["text"],
  "output_modalities": ["text"],
  "parameter_count": null,
  "active_parameter_count": null,
  "release_date": null,
  "capabilities": {
    "supports_temperature": true,
    "supports_top_p": true,
    "supports_reasoning": true,
    "supports_reasoning_effort": false
  }
}
```

| Field | Meaning |
| - | - |
| `id` | The name to send as `model`. |
| `created` | The Unix time the definition records, or `0` when it records none. |
| `owned_by` | The provider the platform calls for this entry. For an entry reached through an aggregator, it is the aggregator's name; for a BYO entry, it is the definition's `provider` label. |
| `model_type` | `llm`, `embeddings`, `images`, `speech_to_text`, or `text_to_speech`. |
| `input_modalities`, `output_modalities` | What the model accepts and produces, such as `text`, `image`, `audio`, or `video`. |
| `parameter_count`, `active_parameter_count` | The model's total parameters, and the parameters active per token for a mixture-of-experts model. Each is `null` when unrecorded. |
| `release_date` | The model's public release date as `YYYY-MM-DD`, or `null` when unrecorded. |
| `capabilities` | `supports_temperature`, `supports_top_p`, `supports_reasoning`, and `supports_reasoning_effort`. All four are `false` for anything that is not an LLM. [Check what a model supports](/docs/generate-a-response#check-what-a-model-supports) explains each flag. |

`GET /models/{model_id}` returns `404` for a name that is not in the catalog, for one that is in the catalog but inactive, and for one whose definition is hidden. The gateway does not say which, so a response cannot reveal what other organizations have.

## Step 2 - Read the catalog

`GET /models/catalog` lists every definition your organization could activate, with its source, activation state, provider health, and a price preview. Cluster entries appear only after a cluster admin grants access to them.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const catalog = await sdk.llm.models.listCatalog();
  for (const entry of catalog.data) {
    console.log(entry.modelId, entry.source, entry.activationState, entry.blockerSource);
  }
  ```

  ```python Python SDK theme={null}
  catalog = sdk.llm.models.list_catalog()
  for entry in catalog.data:
      print(entry.model_id, entry.source, entry.activation_state, entry.blocker_source)
  ```

  ```csharp C# SDK theme={null}
  var catalog = await sdk.Llm.Models.ListCatalogAsync();
  foreach (var entry in catalog.CatalogListResponse!.Data)
  {
      Console.WriteLine($"{entry.ModelId} {entry.Source} {entry.ActivationState}");
  }
  ```

  ```bash CLI theme={null}
  mka1 llm models get \
    --model-id catalog
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models/catalog \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

Field notes:

| Field | Meaning |
| - | - |
| `source` | Where the definition lives. `byo` is your organization's own entry, and `cluster` is the operator's catalog, meaning the cluster admins who run your cluster; a cluster entry is visible only through an access grant. |
| `activation_state` | `available`, `active`, or `available_name_blocked`. See [Activation states](#activation-states). |
| `blocker_source` | For `available_name_blocked`, the source that holds the name. |
| `health` | The last check of the entry's provider. Health is per provider, not per activation. See [Provider health](#provider-health). |
| `effective_price` | `rates` plus a `source` of `org_override`, `cluster_default`, or `unpriced`. Cluster entries preview their default rate before activation. |

The same model id can appear more than once, once per source. Only one of them can hold the name at a time.

### Provider health

`health` holds `is_available`, `last_health_check` (a Unix timestamp in milliseconds), and `error`. The definition's `healthCheck` sets when the gateway checks the provider:

| `healthCheck.mode` | When the provider is checked |
| - | - |
| `periodic` | Every `intervalSeconds` for cluster entries. For an entry your organization defines, `periodic` works like `preflight`. |
| `preflight` | Before a request, when no result is kept from an earlier check. |
| `off` | Never. A definition without `healthCheck` is `off`. |

A check sends `method` (`GET` by default) to `baseUrl` plus `path` with the entry's credentials, and any `2xx` response counts as available. A result is kept for `cacheTtlSeconds`, which defaults to 300 seconds for `preflight` and to `intervalSeconds` plus 60 seconds for `periodic`. Unless the mode is `off`, defining or updating a BYO entry checks its provider at once, and so does `checkCatalogHealth`. Entries that share a `provider` label share one result.

When the kept result says the provider is unavailable, Responses and Chat Completions requests skip it. They follow the entry's `error-fallback` [reroute rule](#reroute-rules) if it has one, and fail otherwise. Health does not change what `GET /models` lists or which candidates [auto routing](/docs/auto-routing) considers.

## Step 3 - Activate a catalog entry

Activation claims a model name for one source. Pass the id and the source you are activating from.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const entry = await sdk.llm.models.activateRegistryEntry({
    modelId: 'meetkai:functionary-pt',
    source: 'cluster',
  });
  console.log(entry.activatedBy, entry.activatedAt);
  ```

  ```python Python SDK theme={null}
  entry = sdk.llm.models.activate_registry_entry(
      model_id="meetkai:functionary-pt",
      source="cluster",
  )
  print(entry.activated_by, entry.activated_at)
  ```

  ```csharp C# SDK theme={null}
  var entry = await sdk.Llm.Models.ActivateRegistryEntryAsync(
      request: new ActivateRegistryEntryRequest()
      {
          ModelId = "meetkai:functionary-pt",
          Source = RegistrySource.Cluster,
      }
  );
  Console.WriteLine(entry.RegistryEntryResponse!.ActivatedAt);
  ```

  ```bash CLI theme={null}
  mka1 llm models activate-registry-entry \
    --body '{"model_id": "meetkai:functionary-pt", "source": "cluster"}'
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models/registry \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --data '{"model_id": "meetkai:functionary-pt", "source": "cluster"}'
  ```
</CodeGroup>

The gateway checks two things, in order:

1. The entry exists for your organization from that source. A cluster id you have no access to, or a BYO id nobody defined, returns `422`.
2. Nobody else holds the name. If the same source already holds it, the call returns the existing activation. If a different source holds it, the call returns `409`. Neither case changes anything.

The response carries `model_id`, `source`, `activated_by`, and `activated_at`. The activation applies to the next request. In the console, the **Activate** button on **Admin → Model Registry → Models** makes the same call. The setup wizard's first step activates every available cluster entry the same way, but only while nothing in the registry is active yet.

## Step 4 - Review the registry and deactivate

`GET /models/registry` is the catalog filtered to `active` entries, so each row has the same shape as in Step 2. `DELETE /models/registry?id=...` releases a name.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const registry = await sdk.llm.models.listRegistry();
  console.log(registry.data.map((entry) => `${entry.modelId} from ${entry.source}`));

  const result = await sdk.llm.models.deactivateRegistryEntry({ id: 'meetkai:functionary-pt' });
  console.log(result.deactivated);
  ```

  ```python Python SDK theme={null}
  registry = sdk.llm.models.list_registry()
  print([f"{entry.model_id} from {entry.source}" for entry in registry.data])

  result = sdk.llm.models.deactivate_registry_entry(id="meetkai:functionary-pt")
  print(result.deactivated)
  ```

  ```csharp C# SDK theme={null}
  var registry = await sdk.Llm.Models.ListRegistryAsync();
  foreach (var entry in registry.RegistryManagementListResponse!.Data)
  {
      Console.WriteLine($"{entry.ModelId} from {entry.Source}");
  }

  var result = await sdk.Llm.Models.DeactivateRegistryEntryAsync(id: "meetkai:functionary-pt");
  Console.WriteLine(result.DeactivateRegistryEntryResponse!.Deactivated);
  ```

  ```bash CLI theme={null}
  mka1 llm models get \
    --model-id registry

  mka1 llm models deactivate-registry-entry \
    --id meetkai:functionary-pt
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models/registry \
    --header 'Authorization: Bearer <mka1-api-key>'

  curl 'https://apigw.mka1.com/api/v1/llm/models/registry?id=meetkai:functionary-pt' \
    --request DELETE \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

Deactivation removes the name from the registry and closes any organization price override for it (the `org_override` rate in Step 6). It does not touch the catalog definition, so you can activate the entry again later, from the same source or a different one. The call is idempotent: `deactivated` is `false` when nothing was active.

Requests already in flight finish. The next request that names the model fails to resolve, and if an auto-model override pointed at it, `auto` fails for that endpoint too (see Step 5).

## Step 5 - Choose what `auto` resolves to

The gateway applies an auto policy per endpoint. With no organization override,
`model: "auto"` remains a direct alias to the cluster default. A direct
organization override aliases `auto` to one
exact active model. A balanced override asks a configured LLM judge to choose
from a configured candidate list.

The endpoint key is the model's API format:

| Endpoint key | Requests it covers |
| - | - |
| `responses` | `POST /responses` and the agents built on it. `/extract` and `/classify` honor this override too; with no override they use the YAML's `autoModels.extract` and `autoModels.classify`, or `autoModels.responses` when those are not defined. A `completions`-format target is accepted here. |
| `completions` | `POST /chat/completions` |
| `embeddings` | `POST /embeddings` |
| `images` | Image generation |
| `transcriptions` | Speech to text |
| `tts` | Text to speech |

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const overrides = await sdk.llm.models.listOrgAutoModels();
  console.log(overrides.data);

  const override = await sdk.llm.models.putOrgAutoModel({
    endpoint: 'responses',
    putOrgAutoModelRequest: { modelId: 'meetkai:functionary-pt' },
  });
  console.log(override);

  await sdk.llm.models.deleteOrgAutoModel({ endpoint: 'responses' });
  ```

  ```python Python SDK theme={null}
  overrides = sdk.llm.models.list_org_auto_models()
  print(overrides.data)

  override = sdk.llm.models.put_org_auto_model(
      endpoint="responses",
      body={"model_id": "meetkai:functionary-pt"},
  )
  print(override.model_id, override.set_by)

  sdk.llm.models.delete_org_auto_model(endpoint="responses")
  ```

  ```csharp C# SDK theme={null}
  var overrides = await sdk.Llm.Models.ListOrgAutoModelsAsync();
  foreach (var o in overrides.ListOrgAutoModelsResponseValue!.Data)
  {
      Console.WriteLine(o);
  }

  var over = await sdk.Llm.Models.PutOrgAutoModelAsync(
      endpoint: AutoEndpoint.Responses,
      body: PutOrgAutoModelRequestUnion.CreatePutOrgAutoModelRequest1(new PutOrgAutoModelRequest1 { ModelId = "meetkai:functionary-pt" })
  );
  Console.WriteLine("Auto model override updated.");

  await sdk.Llm.Models.DeleteOrgAutoModelAsync(endpoint: AutoEndpoint.Responses);
  ```

  ```bash CLI theme={null}
  mka1 llm models get \
    --model-id auto

  mka1 llm models put-org-auto-model \
    --body '{"model_id": "meetkai:functionary-pt"}' \
    --endpoint responses

  mka1 llm models delete-org-auto-model \
    --endpoint responses
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models/auto \
    --header 'Authorization: Bearer <mka1-api-key>'

  curl https://apigw.mka1.com/api/v1/llm/models/auto/responses \
    --request PUT \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --data '{"model_id": "meetkai:functionary-pt"}'

  curl https://apigw.mka1.com/api/v1/llm/models/auto/responses \
    --request DELETE \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

The SDK examples above configure the original direct policy. The following
request configures balanced routing for Responses:

```bash Bash theme={null}
curl https://apigw.mka1.com/api/v1/llm/models/auto/responses \
  --request PUT \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <mka1-api-key>' \
  --data '{
    "policy": "balanced",
    "judge_model_id": "openai:gpt-4.1-nano",
    "candidate_model_ids": [
      "meetkai:functionary-pt",
      "openai:gpt-4.1-mini"
    ]
  }'
```

The candidates illustrate how a Portuguese request can route to
`meetkai:functionary-pt`, while other requests can route to
`openai:gpt-4.1-mini`. Use a fast, non-reasoning judge that supports
structured outputs, such as `openai:gpt-4.1-nano`.

Balanced policies are supported for `responses`, `completions`,
`transcriptions`, and `tts`. The judge must be an active text LLM. Each
candidate must be active and compatible with the endpoint when the policy is
written. At request time, a revoked candidate is excluded, but an unavailable
judge causes a transparent error. One eligible candidate is selected directly;
two or more invoke the judge.

`PUT` returns `422` when any configured model is the literal `auto`, is
not active in your registry, or has an incompatible API format. A direct
override stores the canonical id and later looks up that exact name. If you
deactivate it, the override's `resolvable` flag turns `false` and `auto`
fails on that endpoint rather than moving to the cluster default. Clear the
override or activate the model again. `DELETE` is idempotent. Direct requests
use the body key `model_id`; judge-based requests use `judge_model_id` and
`candidate_model_ids`. Request and response keys are snake\_case; the older
camelCase request keys are rejected with `400`.

`GET /models/auto` reports `mode: "balanced"`,
`judge_resolvable`, and `resolvable_candidate_model_ids` for a balanced
override. [Auto routing](/docs/auto-routing) explains what
the judge weighs, including modalities, file support, pricing, gateway
latency, comparable evals, and conversation continuity.

The console shows the same overrides on **Admin → Model Registry → Auto models**.

## Step 6 - Check effective prices

`GET /models/pricing/effective` returns the rate your organization pays for every model it can call. Any bearer can read it, with no role or scope, because anyone whose requests are billed can see the rate they are billed at. Audit fields such as who set a price stay on the admin pricing routes.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const prices = await sdk.llm.models.listEffectivePrices({});
  console.log(prices.currency);
  for (const row of prices.data) {
    console.log(row.model, row.source, row.rates);
  }
  ```

  ```python Python SDK theme={null}
  prices = sdk.llm.models.list_effective_prices()
  print(prices.currency)
  for row in prices.data:
      print(row.model, row.source, row.rates)
  ```

  ```csharp C# SDK theme={null}
  var prices = await sdk.Llm.Models.ListEffectivePricesAsync();
  Console.WriteLine(prices.EffectivePricesResponse!.Currency);
  foreach (var row in prices.EffectivePricesResponse!.Data)
  {
      Console.WriteLine($"{row.Model} {row.Source}");
  }
  ```

  ```bash CLI theme={null}
  mka1 llm models list-effective-prices
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models/pricing/effective \
    --header 'Authorization: Bearer <mka1-api-key>'
  ```
</CodeGroup>

`source` is `org_override` when a price override was set for your organization through `PUT /models/pricing/org/{org_id}`, `cluster_default` when a cluster entry uses the operator's rate card, and `unpriced` (billed as 0) otherwise. An organization admin can set that override for their own organization's active `byo` entries with `setOrgPrice`, read it back with `listOrgPrices`, and remove it with `clearOrgPrice`; an override on a `cluster` entry is cluster-admin only. `currency` is the cluster-wide ISO 4217 code, or `null` until the operator sets one.

## Bring your own model

An organization admin can define a BYO entry in the catalog and then activate it with `source: "byo"`. Defining does not activate. This is the flow the [Deploy a model server](/docs/compute-deployment#step-5---register-with-the-llm-gateway) guide uses to register a vLLM endpoint. Only curl is shown; the SDKs' `addCatalogEntry` takes the same fields.

```bash Bash theme={null}
curl https://apigw.mka1.com/api/v1/llm/models/catalog \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <mka1-api-key>' \
  --data '{
    "id": "meetkai:custom-chat-v1",
    "provider": "openai-compatible",
    "modelId": "custom-chat-v1",
    "displayName": "Custom Chat v1",
    "baseUrl": "https://models.example.com/v1",
    "apiFormat": "responses",
    "apiProviderType": "openai",
    "created": 1704067200,
    "auth": {"type": "api-key", "value": "<provider-api-key>"},
    "capabilities": {"modalities": {"input": ["text"], "output": ["text"]}, "reasoning": false}
  }'
```

`id` is the name your requests send as `model`; `modelId` is the name the gateway forwards to the provider at `baseUrl`. The response is a catalog entry with the auth value redacted and `activation_state` set to `available`, or `available_name_blocked` if another source already holds the name. Activate it as in Step 3 with `source: "byo"`. `provider` is a free-form label, and entries that share one share a health result. `apiProviderType` is the wire protocol the gateway speaks to `baseUrl`; `openai` covers any OpenAI-compatible server. Add `healthCheck` to have the gateway check the provider before it sends requests (see [Provider health](#provider-health)), and `rerouteRules` to send some requests to a cluster model instead (see [Reroute rules](#reroute-rules)).

All four BYO operations need `write:registry`, the health check included.

| SDK operation | Route | Effect |
| - | - | - |
| `addCatalogEntry` | `POST /models/catalog` | Defines the entry, as above. |
| `updateCatalogEntry` | `PATCH /models/catalog` | Changes an existing entry. Send the `id` plus the fields to change. |
| `deleteCatalogEntry` | `DELETE /models/catalog` | Withdraws the definition, and deactivates the name only if this BYO entry was the one holding it. |
| `checkCatalogHealth` | `POST /models/catalog/health` | Probes the provider whether or not the entry is active. |

Entries the operator manages in `model-registry.yaml` cannot be changed through the API.

### Reroute rules

`rerouteRules` on a BYO definition sends some of its requests to a cluster model. Each rule pairs a `trigger` with a `targetModelId`:

```json theme={null}
"rerouteRules": [
  { "trigger": { "type": "input-modality", "modality": "image" }, "targetModelId": "<cluster-model-id>" },
  { "trigger": { "type": "error-fallback" }, "targetModelId": "<cluster-model-id>" }
]
```

Two triggers take effect:

* `input-modality` with `modality: "image"` sends a request that contains an image to the target.
* `error-fallback` retries a failed request on the target. An error from the provider, a timeout, and a failed [health check](#provider-health) all count.

The API also accepts `base64-input`, `context-exceeded`, and `input-modality` with `text`, `audio`, or `video`, but none of them reroutes a request. Only the first rule for each trigger is used.

The target must be a cluster catalog model, named by its canonical id rather than an alias. `POST` and `PATCH` return `422` for any other target. The target must also be active in your registry when the request runs; if it is not, an image request stays on your model and a failed request returns its original error.

Rerouting cannot loop: a request tries each model at most once. If an `error-fallback` target fails too, the gateway follows that target's own `error-fallback` rule, set by the operator.

Rules apply to Responses and Chat Completions requests and the features built on them. When `auto` resolves through an LLM judge policy, the gateway ignores reroute rules, including the selected candidate's; a direct `auto` override and an explicitly named model keep them. The request is billed at the price of the model that served it, which a Responses response names in `resolved_model`.

## For cluster admins

<Note>
  Everything in this section needs a cluster admin bearer. The four access operations, the two reads included, also need that bearer bound to the cluster organization (`org_cluster`); cluster prices and the currency need only the cluster-admin flag, from any organization. Organization admins get `403` here.
</Note>

Cluster admins decide which cluster models each organization may activate. A grant adds an entry to the organization's catalog as `available`; it activates nothing. In the console this is **Access → Organizations → Cluster**, then the organization's **Available models** tab, where you stage toggles, and **Save** sends the whole set through `replaceCatalogOrgAccess`.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const access = await sdk.llm.models.listCatalogOrgAccess({ orgId: 'org_acme' });
  console.log(access.data.map((row) => `${row.modelId}: ${row.activationState}`));

  await sdk.llm.models.addCatalogOrgAccess({
    orgId: 'org_acme',
    accessMutationRequest: { modelIds: ['meetkai:functionary-pt'] },
  });
  ```

  ```python Python SDK theme={null}
  access = sdk.llm.models.list_catalog_org_access(org_id="org_acme")
  print([f"{row.model_id}: {row.activation_state}" for row in access.data])

  sdk.llm.models.add_catalog_org_access(org_id="org_acme", model_ids=["meetkai:functionary-pt"])
  ```

  ```csharp C# SDK theme={null}
  var access = await sdk.Llm.Models.ListCatalogOrgAccessAsync(orgId: "org_acme");
  foreach (var row in access.AccessListResponse!.Data)
  {
      Console.WriteLine($"{row.ModelId}: {row.ActivationState}");
  }

  await sdk.Llm.Models.AddCatalogOrgAccessAsync(
      orgId: "org_acme",
      body: new AccessMutationRequest() { ModelIds = new List<string> { "meetkai:functionary-pt" } }
  );
  ```

  ```bash CLI theme={null}
  mka1 llm models list-catalog-org-access \
    --org-id org_acme

  mka1 llm models add-catalog-org-access \
    --body '{"model_ids": ["meetkai:functionary-pt"]}' \
    --org-id org_acme
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/models/catalog/orgs/org_acme/access \
    --header 'Authorization: Bearer <mka1-api-key>'

  curl https://apigw.mka1.com/api/v1/llm/models/catalog/orgs/org_acme/access \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --data '{"model_ids": ["meetkai:functionary-pt"]}'
  ```
</CodeGroup>

The operations, and what each one touches:

| SDK operation | Route | Effect |
| - | - | - |
| `addCatalogOrgAccess` | `POST /models/catalog/orgs/{org_id}/access` | Adds ids to the organization's access set. Idempotent; never activates or revokes. |
| `replaceCatalogOrgAccess` | `PUT /models/catalog/orgs/{org_id}/access` | Replaces the whole set (up to 1000 ids). The gateway deactivates any active cluster name that drops out of the set and closes its organization price override. The console warns before you revoke a model that another model falls back to. |
| `listCatalogOrgAccess` | `GET /models/catalog/orgs/{org_id}/access` | One organization's cluster entries with their activation state. |
| `listCatalogModelAccess` | `GET /models/catalog/access` | The reverse lookup: every organization with access to one cluster model id, and whether each has activated it. |
| `listClusterPrices`, `setClusterPrice`, `clearClusterPrice` | `GET`, `PUT`, `DELETE /models/pricing` | The cluster-default rate card that `cluster_default` prices come from. |
| `getClusterCurrency`, `setClusterCurrency` | `GET`, `PUT /models/pricing/currency` | The single cluster-wide currency. Changing it does not convert existing prices. |

The two reads require `read:registry` and the two mutations `write:registry`, as at the organization level. Both mutations reject an unknown cluster catalog id with `422 Unknown cluster catalog model id(s): ...`. The cluster organization activates its own catalog with `source: "byo"`; `"cluster"` is for tenant organizations only, meaning every organization other than `org_cluster`.

## Activation states

| State | Meaning | What to do |
| - | - | - |
| `available` | No source holds this name. | Activate it. |
| `active` | This entry holds the name. Requests resolve to it. | Deactivate it to release the name. |
| `available_name_blocked` | Another source holds the name; `blocker_source` says which. The console shows this as **name in use**. | Deactivate the holder, then activate this entry. |

## Model lifecycle

Catalog entries have no versions or retirement dates. Changes to the cluster catalog take effect when the operator deploys them. When a deploy removes a cluster model, every organization loses its access grant and activation for it, so requests that name it fail to resolve and an `auto` override that points at it reports `resolvable: false`.

A dated name such as `openai:gpt-4.1-2025-04-14` is an alias of the undated entry, here `openai:gpt-4.1`. It follows the provider's current release of that model rather than pinning the dated snapshot. The alias resolves only while the undated entry is active, `GET /models` does not list it, and `GET /models/openai:gpt-4.1-2025-04-14` returns the entry with `id: "openai:gpt-4.1"`.

## Troubleshooting

| Symptom | Cause | Fix |
| - | - | - |
| `404 model_not_found` with `Model 'x' not found. Check the model ID and try again.` from `/responses` (`/chat/completions` and agents say `Model 'x' not found or not supported`), or `404` from `GET /models/{model_id}` | The name is not active in your registry, its definition is hidden, or the key belongs to a different organization. | Find the entry in the catalog and activate it. If it is already `active`, check that the key belongs to the same organization and that the definition is not hidden (see the `hidden: true` row). |
| `auto` fails while named models work (`/extract` and `/classify` report `Model 'auto' is not available to your organization for extract.`) | No override is set and the operator's default is not active in your registry; the override's target was deactivated (`resolvable: false`); or the cluster's YAML defines no `autoModels` entry for that endpoint. | Activate the default target, set an override to an active model, or clear the stale override. With no YAML default, only an override makes `auto` work on that endpoint. |
| `409 Model 'x' is active from source 'byo'` on activation | Another source holds the name. | Deactivate that entry first, or keep it and leave this one unactivated. |
| `422 Catalog entry 'x' from source 'cluster' is not available` | No access grant for that id, the wrong `source`, or a BYO id nobody defined. | Check the catalog (Step 2) for the id and its source; ask a cluster admin for access. |
| `422 The cluster org must activate its own catalog with source 'byo' or 'serving'` | You are acting in `org_cluster`. | Use the entry's real source, or switch to the tenant organization. |
| `403 Organization admin access required` | The bearer's organization role is `member`. | Use an owner or admin key, or a console session with that role. |
| `403 API key is missing required scope(s): write:registry` | The key was minted without the scope. | Mint a key with `write:registry` (owners and admins only). |
| An entry is `active` in the catalog but missing from `GET /models`, and requests naming it return `404` | Its definition sets `hidden: true`. A hidden definition never resolves for a user request; a hidden cluster model is reachable only as the target of another model's [reroute rule](#reroute-rules). | Expected. Point requests at a non-hidden entry. |
| A cluster model disappeared from the catalog and requests to it fail | A cluster admin revoked access, which deactivates the name. | Ask for the access grant back, then activate again. |

## API reference

For the full request and response schemas, open the Models group in the [API Reference](/api-reference/models/list-available-models).

## See also

* [Getting started](/docs/platform-getting-started#enable-your-models) - the setup wizard's activation step.
* [Deploy a model server](/docs/compute-deployment) - serve your own weights and register them as a BYO entry.
* [Generate a response](/docs/generate-a-response) - call an activated model.

For application-side discovery, see [Models](/docs/models).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.