Catalog

Package: agentrouter.catalog.v1 Service: CatalogService

Endpoints

List providers

Changed in v0.6.0 (new features): Provider reads have a new field, has_credential: true when the provider's platform key is stored, false when it is not, and unset when the check could not run (the read still succeeds). ListProviders and GetProvider also accept a customer_id, and optionally a project_id within it, which returns the same providers without the key suffix or internal metadata.

Changed in v0.2.0 (new features): The catalog API now carries a model's whole record. Modalities, rate and size limits, per-model backend URLs, non-per-token pricing, the cache-write rate, and free-form metadata (including the display name and the deprecation and retirement dates) can now be written and read back, where before they could only be set by editing the database. Providers likewise carry their routing metadata (upstream type, backend schema, API-key flavor and per-operation variants), which had no representation at all. Listing providers or models also accepts include_disabled, so the full catalog can be read rather than only the enabled part.

What it does: Returns the registered AI providers in one response: the list is not paginated. Every page field (page_size, page_token, filter, order_by) is ignored, and next_page_token is always empty. Naming customer_id makes this a tenant-path read: the caller is authorized at that customer and the response is redacted (see Provider.credential_suffix and Provider.metadata). The rows are the same global catalog either way.

Request fields:

FieldRequiredDescription
pagenoIgnored: ListProviders returns every provider in one response, and page_size, page_token, filter and order_by have no effect.
include_disablednoReturn disabled providers alongside enabled ones. Defaults false, which is the enabled-only view every interactive caller expects. A catalogue sync must set it: without the full picture it cannot tell a disabled provider from an absent one, so it would re-propose every disabled row as new on each run.
customer_idnoThe customer to authorize this read at. It selects the authorization target, not the rows: the provider catalog is global, and the response holds the same providers whatever the value. Set it to read the catalog with a tenant-scoped grant (for example an org read_only binding); the response is then redacted. Leave it empty for the full record, which needs a platform grant. This is a deliberate exception to ADR 070's rule against request fields the handler does not use to select data: a permission check needs a target, and a global catalog has none of its own (ADR 087).
project_idnoThe project, within customer_id, to authorize this read at. Like customer_id it selects the authorization target, not the rows. Requires customer_id.

Response fields:

FieldRequiredDescription
providersnoEvery matching provider, ordered by slug.
pagenoAlways empty: the list is not paginated.
{"signatures":{"go":"func (x *CatalogClient) ListProviders(ctx context.Context, req *catalogv1.ListProvidersRequest) (*catalogv1.ListProvidersResponse, error)","python":"list_providers(req: catalog_pb2.ListProvidersRequest) -\u003e ListProvidersResponse","typescript":"listProviders(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.ListProvidersRequestSchema\u003e): Promise\u003cListProvidersResponse\u003e","cli":"tare api catalog providers","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.ListProvidersRequest{}\n\n\tresp, err := client.Catalog().ListProviders(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/listproviders\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.ListProvidersRequest()\ntry:\n    result = client.catalog.list_providers(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.listProviders(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog providers","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/catalog/providers","slug":"list-providers"}

Get a provider

Changed in v0.6.0 (behaviour changes): GetProvider and the provider writes now also accept a platform role binding that grants the providers permission, such as platform_provider_admin. The admin scope still works as before.

Changed in v0.6.0 (new features): Provider reads have a new field, has_credential: true when the provider's platform key is stored, false when it is not, and unset when the check could not run (the read still succeeds). ListProviders and GetProvider also accept a customer_id, and optionally a project_id within it, which returns the same providers without the key suffix or internal metadata.

Changed in v0.2.0 (new features): The catalog API now carries a model's whole record. Modalities, rate and size limits, per-model backend URLs, non-per-token pricing, the cache-write rate, and free-form metadata (including the display name and the deprecation and retirement dates) can now be written and read back, where before they could only be set by editing the database. Providers likewise carry their routing metadata (upstream type, backend schema, API-key flavor and per-operation variants), which had no representation at all. Listing providers or models also accepts include_disabled, so the full catalog can be read rather than only the enabled part.

What it does: Returns a single provider by id. Naming customer_id makes this a tenant-path read, with the same authorization and redaction as ListProviders.

Request fields:

FieldRequiredDescription
idyesSlug of the provider to fetch, e.g. "openai".
customer_idnoThe customer to authorize this read at. It selects the authorization target, not the rows: the provider catalog is global, and the response holds the same providers whatever the value. Set it to read the catalog with a tenant-scoped grant (for example an org read_only binding); the response is then redacted. Leave it empty for the full record, which needs a platform grant. This is a deliberate exception to ADR 070's rule against request fields the handler does not use to select data: a permission check needs a target, and a global catalog has none of its own (ADR 087).
project_idnoThe project, within customer_id, to authorize this read at. Like customer_id it selects the authorization target, not the rows. Requires customer_id.

Response fields:

FieldRequiredDescription
providernoThe requested provider.
{"signatures":{"go":"func (x *CatalogClient) GetProvider(ctx context.Context, req *catalogv1.GetProviderRequest) (*catalogv1.GetProviderResponse, error)","python":"get_provider(req: catalog_pb2.GetProviderRequest) -\u003e GetProviderResponse","typescript":"getProvider(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.GetProviderRequestSchema\u003e): Promise\u003cGetProviderResponse\u003e","cli":"tare api catalog provider \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.GetProviderRequest{}\n\n\tresp, err := client.Catalog().GetProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/getprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.GetProviderRequest()\ntry:\n    result = client.catalog.get_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.getProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog provider \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/catalog/providers/{id}","slug":"get-a-provider"}

List models

Changed in v0.10.0 (behaviour changes): In single-organization installs, a Model Admin can now create, edit and delete models through the API with their own session. A user who holds the Platform Model Admin role can do the same in any install. GetModel now also accepts a platform grant of models.read, as ListModels does.

Changed in v0.10.0 (new features): ListModels and GetModel take an optional customer_id and project_id, so a user with an organization role that can read models, such as a Viewer, can read the model catalog. A read that names customer_id leaves out the aigw-headers metadata key. New REST paths: GET /v1/customers/{customer_id}/catalog/models and GET /v1/customers/{customer_id}/catalog/models/{id}.

Changed in v0.2.0 (new features): The catalog API now carries a model's whole record. Modalities, rate and size limits, per-model backend URLs, non-per-token pricing, the cache-write rate, and free-form metadata (including the display name and the deprecation and retirement dates) can now be written and read back, where before they could only be set by editing the database. Providers likewise carry their routing metadata (upstream type, backend schema, API-key flavor and per-operation variants), which had no representation at all. Listing providers or models also accepts include_disabled, so the full catalog can be read rather than only the enabled part.

What it does: Returns catalog models in one response, optionally filtered to a single provider via the provider_id query parameter. The list is not paginated: every page field is ignored, and next_page_token is always empty. Naming customer_id makes this a tenant-path read: the caller is authorized at that customer and the response is redacted (see ListModelsRequest.customer_id). The rows are the same global catalog either way.

Request fields:

FieldRequiredDescription
provider_idnoFilter to models belonging to this provider. Empty returns all models.
pagenoIgnored: ListModels returns every matching model in one response, and page_size, page_token, filter and order_by have no effect.
include_disablednoReturn disabled models alongside enabled ones. Defaults false. See ListProvidersRequest.include_disabled for why a sync requires it.
customer_idnoThe customer to authorize this read at. It selects the authorization target, not the rows: the model catalog is global, and the response holds the same models whatever the value. Set it to read the catalog with a tenant-scoped grant (for example an org read_only binding); the response is then redacted: Model.metadata has no aigw-headers key, because its header values can be credentials. Leave it empty for the full record, which needs a platform grant. This is a deliberate exception to ADR 070's rule against request fields the handler does not use to select data: a permission check needs a target, and a global catalog has none of its own (ADR 087).
project_idnoThe project, within customer_id, to authorize this read at. Like customer_id it selects the authorization target, not the rows. Requires customer_id.

Response fields:

FieldRequiredDescription
modelsnoEvery matching model, ordered by provider and name.
pagenoAlways empty: the list is not paginated.
{"signatures":{"go":"func (x *CatalogClient) ListModels(ctx context.Context, req *catalogv1.ListModelsRequest) (*catalogv1.ListModelsResponse, error)","python":"list_models(req: catalog_pb2.ListModelsRequest) -\u003e ListModelsResponse","typescript":"listModels(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.ListModelsRequestSchema\u003e): Promise\u003cListModelsResponse\u003e","cli":"tare api catalog models","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.ListModelsRequest{}\n\n\tresp, err := client.Catalog().ListModels(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/listmodels\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.ListModelsRequest()\ntry:\n    result = client.catalog.list_models(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.listModels(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog models","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/catalog/models","slug":"list-models"}

Get a model

Changed in v0.10.0 (behaviour changes): In single-organization installs, a Model Admin can now create, edit and delete models through the API with their own session. A user who holds the Platform Model Admin role can do the same in any install. GetModel now also accepts a platform grant of models.read, as ListModels does.

Changed in v0.10.0 (new features): ListModels and GetModel take an optional customer_id and project_id, so a user with an organization role that can read models, such as a Viewer, can read the model catalog. A read that names customer_id leaves out the aigw-headers metadata key. New REST paths: GET /v1/customers/{customer_id}/catalog/models and GET /v1/customers/{customer_id}/catalog/models/{id}.

Changed in v0.2.0 (new features): The catalog API now carries a model's whole record. Modalities, rate and size limits, per-model backend URLs, non-per-token pricing, the cache-write rate, and free-form metadata (including the display name and the deprecation and retirement dates) can now be written and read back, where before they could only be set by editing the database. Providers likewise carry their routing metadata (upstream type, backend schema, API-key flavor and per-operation variants), which had no representation at all. Listing providers or models also accepts include_disabled, so the full catalog can be read rather than only the enabled part.

What it does: Returns a single model by id (UUID) or by unique model name, including its pricing, context window, capabilities, modalities, and limits. Naming customer_id makes this a tenant-path read, with the same authorization and redaction as ListModels.

Request fields:

FieldRequiredDescription
idyesModel id (UUID) or unique model name. A UUID is looked up by id; any other value is resolved by model name across the visible catalog. A name that matches models from multiple providers is rejected with InvalidArgument naming the candidates.
customer_idnoThe customer to authorize this read at, with the same meaning and redaction as ListModelsRequest.customer_id. This is a deliberate exception to ADR 070's rule against request fields the handler does not use to select data (ADR 087).
project_idnoThe project, within customer_id, to authorize this read at. Like customer_id it selects the authorization target, not the rows. Requires customer_id.

Response fields:

FieldRequiredDescription
modelnoThe requested model.
{"signatures":{"go":"func (x *CatalogClient) GetModel(ctx context.Context, req *catalogv1.GetModelRequest) (*catalogv1.GetModelResponse, error)","python":"get_model(req: catalog_pb2.GetModelRequest) -\u003e GetModelResponse","typescript":"getModel(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.GetModelRequestSchema\u003e): Promise\u003cGetModelResponse\u003e","cli":"tare api catalog model \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/models/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.GetModelRequest{}\n\n\tresp, err := client.Catalog().GetModel(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/getmodel\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.GetModelRequest()\ntry:\n    result = client.catalog.get_model(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.getModel(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog model \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/models/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/catalog/models/{id}","slug":"get-a-model"}

Create a provider

Changed in v0.6.0 (new features): CreateProvider adds a provider and returns AlreadyExists if the id is taken, and DeleteProviderCredential removes a provider's platform key. UpsertProvider now needs display_name, base_url and a canonical id only when it creates the provider, so {id, is_enabled} toggles an existing one.

What it does: Adds a new provider. It never changes an existing one: when a provider with this id already exists it returns ALREADY_EXISTS and writes nothing. Use UpsertProvider to edit a provider. It is not idempotent. A client that retries after a lost response and gets ALREADY_EXISTS cannot tell its own first write from someone else's; it should read the provider with GetProvider and compare. The provider is created disabled unless is_enabled is true. Enable a provider whose auth scheme needs a key only after SetProviderCredential: the data plane fetches the credential of every enabled provider. A successful create regenerates routes, as UpsertProvider does.

Request fields:

FieldRequiredDescription
idyesProvider slug, e.g. "openai". Must be canonical lowercase alphanumeric [a-z0-9]+ (no hyphens, spaces, or uppercase).
display_namenoHuman-readable display name, e.g. "OpenAI". Defaults to id when empty.
base_urlnoProvider API base URL. Required unless the auth scheme is "aws" or "none".
supported_auth_schemesyesThe provider's auth scheme, as a list of exactly one entry, e.g. ["api_key"]. It is stored as the auth type and must be one of "api_key", "aws", "vertex", "vertex_anthropic", "none", "google_ai_key" or "azure".
metadatanoNon-secret data-path semantics, as on UpsertProviderRequest.metadata. credential and credential_suffix are ignored; every other value, null included, is stored as sent.
is_enablednoWhether the new provider is enabled. Unset means false. Leave it unset for a provider whose auth scheme needs a key, and enable it with UpsertProvider after SetProviderCredential.

Response fields:

FieldRequiredDescription
idoutput-onlyThe provider's slug, the same value as name. It is the key every provider RPC takes as id. Immutable.
nameyesMachine-readable slug, e.g. "openai", "anthropic", "awsbedrock". Unique. Canonical form is lowercase alphanumeric [a-z0-9]+ (no hyphens, spaces, or uppercase) -- it is matched hyphen-insensitively at inference time, so a write that creates a provider (CreateProvider, or UpsertProvider for a new id) requires this canonical form. An existing provider with an older slug form stays editable.
display_nameyesHuman-readable display name, e.g. "OpenAI".
base_urlyesBase URL for all models served by this provider, e.g. "https://api.openai.com".
supported_auth_schemesnoAuth schemes this provider supports. Holds at most one value, the stored auth type: "api_key", "aws", "vertex", "vertex_anthropic", "none", "google_ai_key" or "azure".
credential_suffixoutput-onlyLast 4 chars of the stored platform credential (e.g. "sk-..**4a2f"). Empty if no platform credential has been set. BYOK keys are tracked separately via aikeys.v1. Always empty on a tenant-path read (a request that names customer_id).
metadatanoNon-secret data-path semantics from ai_providers.metadata: type, backend_schema, api_key_flavor, operation_variants. The credential keys are never included. A tenant-path read (a request that names customer_id) keeps only type, backend_schema, api_key_flavor, operation_variants and first_party.
is_enablednoWhether this provider is enabled. A disabled provider is absent from the data plane's route map and its credentials are not reconciled. Only visible when the request set include_disabled -- the default list returns enabled providers only, so this always reads true there.
has_credentialoutput-onlyWhether the platform credential for this provider is stored: a live (not revoked) secret named AISECRET_<SLUG> for user "system" in the "llm-providers" realm, which is the secret the data plane reads. True regardless of which path wrote it (SetProviderCredential, the admin dashboard or a legacy-KMS import). A credential that exists only in legacy KMS and was never imported reads false. Computed by ListProviders and GetProvider (on both paths) and admin.v1.ListCatalogProviders; SetProviderCredential returns true and DeleteProviderCredential false. Unset means "not known", never "no credential": a read whose credential lookup fails still returns the providers, with this field unset, and CreateProvider and UpsertProvider do not look it up. A client must not show an unset value as "no key".
created_atoutput-onlyWhen this provider record was created.
updated_atoutput-onlyWhen this provider record was last modified.
{"signatures":{"go":"func (x *CatalogClient) CreateProvider(ctx context.Context, req *catalogv1.CreateProviderRequest) (*catalogv1.Provider, error)","python":"create_provider(req: catalog_pb2.CreateProviderRequest) -\u003e Provider","typescript":"createProvider(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.CreateProviderRequestSchema\u003e): Promise\u003cProvider\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/providers:create\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"id\": \"...\",\n    \"display_name\": \"...\",\n    \"base_url\": \"...\",\n    \"supported_auth_schemes\": [],\n    \"metadata\": {},\n    \"is_enabled\": false\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.CreateProviderRequest{}\n\n\tresp, err := client.Catalog().CreateProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/createprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.CreateProviderRequest()\ntry:\n    result = client.catalog.create_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.createProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/providers:create\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"id\": \"...\",\n    \"display_name\": \"...\",\n    \"base_url\": \"...\",\n    \"supported_auth_schemes\": [],\n    \"metadata\": {},\n    \"is_enabled\": false\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/catalog/providers:create","slug":"create-a-provider"}

Create or update a provider

Changed in v0.11.0 (bug fixes): UpsertProvider refuses to create an enabled provider with no auth scheme, and refuses to enable a provider that has none. Such a provider made API key creation fail for every tenant until it was deleted. Send supported_auth_schemes, or keep the provider disabled.

Changed in v0.6.0 (behaviour changes): GetProvider and the provider writes now also accept a platform role binding that grants the providers permission, such as platform_provider_admin. The admin scope still works as before.

Changed in v0.6.0 (new features): CreateProvider adds a provider and returns AlreadyExists if the id is taken, and DeleteProviderCredential removes a provider's platform key. UpsertProvider now needs display_name, base_url and a canonical id only when it creates the provider, so {id, is_enabled} toggles an existing one.

Changed in v0.6.0 (new features): UpsertProvider has a new field, remove_metadata_keys, that removes the named top-level keys from the provider's metadata. A null value in metadata is still stored as sent. credential and credential_suffix cannot be removed this way.

Changed in v0.5.0 (new features): Catalog audit entries now say whether a write came from a batch. A sync run's several hundred UPSERT rows carry batched: true in their metadata and interactive edits carry batched: false, so the audit view can be filtered to just the changes a person made.

Changed in v0.2.0 (behaviour changes): Creating a provider through the catalog API no longer enables it. A provider created without is_enabled now arrives disabled, and registering a model under it is refused until it is enabled: enabling a provider is what makes the data plane start fetching its credentials, so one created before it has a credential cannot serve. Existing providers are unaffected.

Changed in v0.2.0 (new features): The catalog API now carries a model's whole record. Modalities, rate and size limits, per-model backend URLs, non-per-token pricing, the cache-write rate, and free-form metadata (including the display name and the deprecation and retirement dates) can now be written and read back, where before they could only be set by editing the database. Providers likewise carry their routing metadata (upstream type, backend schema, API-key flavor and per-operation variants), which had no representation at all. Listing providers or models also accepts include_disabled, so the full catalog can be read rather than only the enabled part.

Changed in v0.2.0 (bug fixes): Editing a model or provider through the catalog API no longer switches it back on. Every write used to assert "enabled" whether or not it mentioned enablement, so an administrator's deliberate disable was reverted by the next unrelated edit (a price correction or a rename was enough) with nothing recording it. Enablement now changes only when a request actually asks it to. One consequence: a provider created through the API now arrives disabled, because enabling a provider is what makes the data plane start fetching its credentials, and one created before it has a credential cannot serve. Existing providers are unaffected, and enabling a new one is a single further call. If you script provider creation and relied on it being immediately enabled, ask for it explicitly.

Changed in v0.1.5 (bug fixes): Editing a provider's connection settings through the catalog API (base URL or credential, or removing the provider) now takes effect on projects that route on their own hostname (strict isolation). Previously only the shared route map picked up the change, so a project with its own hostname kept serving the old provider configuration until an unrelated change happened to refresh it.

What it does: Creates a provider or updates an existing one. On update, a field left empty keeps its stored value, so {id, is_enabled} alone toggles availability. The slug rule, display_name and base_url are enforced only when this call creates the provider. metadata merges key by key, and remove_metadata_keys removes keys: see UpsertProviderRequest. is_enabled is tri-state: omit it and the stored value is left alone (a provider an operator disabled stays disabled). A new provider defaults to disabled -- the data plane does not reconcile an AIServiceBackend or fetch credentials for it until it is both enabled and credentialed via SetProviderCredential.

Request fields:

FieldRequiredDescription
idyesProvider slug, e.g. "openai". Natural key; required. When this call creates the provider, it must be canonical lowercase alphanumeric [a-z0-9]+ (no hyphens, spaces, or uppercase). An existing provider with an older slug form stays editable.
display_namenoHuman-readable display name, e.g. "OpenAI". Required when this call creates the provider; empty on update keeps the stored value.
base_urlnoProvider API base URL. Required when this call creates the provider, unless its auth scheme is "aws" or "none"; empty on update keeps the stored value. The CLI flag is renamed to --provider-base-url to avoid colliding with the root command's persistent --base-url (which overrides the active profile's API URL for one invocation).
supported_auth_schemesnoThe provider's auth scheme, one entry such as api_key. Send one entry: only the first is stored as the auth type, the only scheme the data plane uses and the only one Provider.supported_auth_schemes returns. For compatibility the whole list is also copied into metadata.supported_auth_schemes, where entries after the first have no effect. Empty keeps the stored value. A new enabled provider needs it. CreateProvider accepts exactly one. The CLI flag is repeatable: --supported-auth-schemes api_key.
metadatanoNon-secret data-path semantics stored on ai_providers.metadata: type (client request shape), backend_schema (backend translator), api_key_flavor (key injection behavior), and operation_variants (per-operation provider variants). Merged key by key over the stored metadata: a top-level key set here replaces that key, and a key not named is kept. A null value is stored as sent; it does not remove the key (use remove_metadata_keys). credential and credential_suffix are reserved and ignored; use SetProviderCredential and DeleteProviderCredential. Message-typed, so protoc-gen-ia-cli skips it -- this field is for catalog sync and API clients, not interactive CLI use.
is_enablednoEnable or disable the provider. UNSET LEAVES THE STORED VALUE UNTOUCHED, and that is the default: a catalog sync must not revert an operator's deliberate disable, and a provider must not become enabled (and so reconciled by the data plane) before it has a credential. On insert, unset means false.
suppress_route_regennoSkip the synchronous default GLOBAL_ROUTES regeneration this write would otherwise trigger. For batch callers only: a bulk sync that regenerates per record blocks on one configurator round-trip each time. A caller that sets this MUST fire configurator.TriggerUpdate once when the batch completes, or the data plane keeps serving the previous route map until the next periodic sync.
remove_metadata_keysnoTop-level metadata keys to remove from the stored metadata. Applied after metadata is merged, so a key that is both set in metadata and listed here is removed. Removes whole top-level keys only; a value nested inside a kept key is not touched. A key that is not stored is a no-op. credential and credential_suffix are reserved and silently dropped from this list: they cannot be removed here (use DeleteProviderCredential).

Response fields:

FieldRequiredDescription
idoutput-onlyThe provider's slug, the same value as name. It is the key every provider RPC takes as id. Immutable.
nameyesMachine-readable slug, e.g. "openai", "anthropic", "awsbedrock". Unique. Canonical form is lowercase alphanumeric [a-z0-9]+ (no hyphens, spaces, or uppercase) -- it is matched hyphen-insensitively at inference time, so a write that creates a provider (CreateProvider, or UpsertProvider for a new id) requires this canonical form. An existing provider with an older slug form stays editable.
display_nameyesHuman-readable display name, e.g. "OpenAI".
base_urlyesBase URL for all models served by this provider, e.g. "https://api.openai.com".
supported_auth_schemesnoAuth schemes this provider supports. Holds at most one value, the stored auth type: "api_key", "aws", "vertex", "vertex_anthropic", "none", "google_ai_key" or "azure".
credential_suffixoutput-onlyLast 4 chars of the stored platform credential (e.g. "sk-..**4a2f"). Empty if no platform credential has been set. BYOK keys are tracked separately via aikeys.v1. Always empty on a tenant-path read (a request that names customer_id).
metadatanoNon-secret data-path semantics from ai_providers.metadata: type, backend_schema, api_key_flavor, operation_variants. The credential keys are never included. A tenant-path read (a request that names customer_id) keeps only type, backend_schema, api_key_flavor, operation_variants and first_party.
is_enablednoWhether this provider is enabled. A disabled provider is absent from the data plane's route map and its credentials are not reconciled. Only visible when the request set include_disabled -- the default list returns enabled providers only, so this always reads true there.
has_credentialoutput-onlyWhether the platform credential for this provider is stored: a live (not revoked) secret named AISECRET_<SLUG> for user "system" in the "llm-providers" realm, which is the secret the data plane reads. True regardless of which path wrote it (SetProviderCredential, the admin dashboard or a legacy-KMS import). A credential that exists only in legacy KMS and was never imported reads false. Computed by ListProviders and GetProvider (on both paths) and admin.v1.ListCatalogProviders; SetProviderCredential returns true and DeleteProviderCredential false. Unset means "not known", never "no credential": a read whose credential lookup fails still returns the providers, with this field unset, and CreateProvider and UpsertProvider do not look it up. A client must not show an unset value as "no key".
created_atoutput-onlyWhen this provider record was created.
updated_atoutput-onlyWhen this provider record was last modified.
{"signatures":{"go":"func (x *CatalogClient) UpsertProvider(ctx context.Context, req *catalogv1.UpsertProviderRequest) (*catalogv1.Provider, error)","python":"upsert_provider(req: catalog_pb2.UpsertProviderRequest) -\u003e Provider","typescript":"upsertProvider(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.UpsertProviderRequestSchema\u003e): Promise\u003cProvider\u003e","cli":"tare api catalog providers upsert --id $ID","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"id\": \"...\",\n    \"display_name\": \"...\",\n    \"base_url\": \"...\",\n    \"supported_auth_schemes\": [],\n    \"metadata\": {},\n    \"is_enabled\": false,\n    \"suppress_route_regen\": false,\n    \"remove_metadata_keys\": []\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.UpsertProviderRequest{}\n\n\tresp, err := client.Catalog().UpsertProvider(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/upsertprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.UpsertProviderRequest()\ntry:\n    result = client.catalog.upsert_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.upsertProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog providers upsert --id $ID","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/providers\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"id\": \"...\",\n    \"display_name\": \"...\",\n    \"base_url\": \"...\",\n    \"supported_auth_schemes\": [],\n    \"metadata\": {},\n    \"is_enabled\": false,\n    \"suppress_route_regen\": false,\n    \"remove_metadata_keys\": []\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/catalog/providers","slug":"create-or-update-a-provider"}

Create a model

Changed in v0.10.0 (behaviour changes): In single-organization installs, a Model Admin can now create, edit and delete models through the API with their own session. A user who holds the Platform Model Admin role can do the same in any install. GetModel now also accepts a platform grant of models.read, as ListModels does.

Changed in v0.10.0 (new features): CreateModel adds a model and returns ALREADY_EXISTS when the provider already has a model of that name. The provider rules run first: a create under a disabled provider that leaves is_enabled unset returns FAILED_PRECONDITION, also when the model exists. REST: POST /v1/catalog/models:create. CLI: tetrate agentrouter api catalog models create.

What it does: Adds a new model. It never changes an existing one: when a model with this (provider_id, name) already exists it returns ALREADY_EXISTS and writes nothing. Use UpsertModel to edit a model. It is not idempotent. A client that retries after a lost response and gets ALREADY_EXISTS cannot tell its own first write from someone else's; it should read the model with GetModel and compare. The provider rules, the enabled default and the route regeneration are the same as for UpsertModel. The provider rules are checked first, so a create under a disabled provider that leaves is_enabled unset returns FAILED_PRECONDITION even when the model already exists.

Request fields:

FieldRequiredDescription
provider_idyesProvider slug this model belongs to.
nameyesLogical model name, e.g. "gpt-4o". Unique within provider.
upstream_modelnoProvider-side model id (defaults to name when empty).
input_per_millionnoCost in USD per million input tokens (decimal string).
output_per_millionnoCost in USD per million output tokens (decimal string).
cache_read_per_millionnoCost in USD per million cache-read tokens. Empty if not applicable.
max_cost_per_requestnoHard cap in USD per single request. Empty means no cap.
max_context_tokensnoMaximum context window in tokens.
capabilitiesnoCapabilities this model supports, e.g. ["chat", "embeddings"].
modenoModel mode, as on UpsertModelRequest.mode. Defaults to "chat" when empty.
caching_per_millionnoCost in USD per million cache-WRITE tokens. Empty if not applicable.
input_modalitiesnoInput modalities the model accepts, e.g. ["text", "image"].
output_modalitiesnoOutput modalities the model produces, e.g. ["text"].
backend_urlsnoPer-model backend URL overrides. Empty serves the model from its provider's base_url.
limitsnoRate and size caps, as on UpsertModelRequest.limits.
additional_price_per_millionnoNon-per-token pricing, as on UpsertModelRequest.additional_price_per_million.
metadatanoFree-form model metadata, as on UpsertModelRequest.metadata. upstream_model and max_cost_per_request are ignored here: use the dedicated fields.
is_enablednoWhether the new model is enabled. Unset means true.

Response fields:

FieldRequiredDescription
idoutput-onlyModel id, a UUID. Server-assigned; immutable.
provider_idyesSlug of the provider this model belongs to.
nameyesLogical name used in routing and billing, e.g. "gpt-4o". Unique within a provider.
upstream_modelyesProvider-side model id sent as body.model to upstream, e.g. "gpt-4o-2024-11-20".
pricingnoPer-token pricing rates.
max_context_tokensnoMaximum context window in tokens.
capabilitiesnoCapabilities this model supports, e.g. "chat", "embeddings", "images".
modenoModel mode, e.g. "chat", "embedding", "image_generation". Surfaces the persisted value so callers can confirm what UpsertModel stored; reads default it to "chat" when the stored value is unset.
modalitiesnoInput/output modalities from ai_models.modalities.
limitsnoRate limits from ai_models.limits, narrowed to rpm/tpm. Retained for compatibility; prefer limits_json, which carries the column whole.
backend_urlsnoPer-model backend URL overrides (ai_models.backend_urls). Empty means the model is served from its provider's base_url.
additional_price_per_millionnoNon-per-token pricing (ai_models.additional_price_per_million).
metadatanoFull model metadata (ai_models.metadata), including the lifecycle fields a catalogue consumer needs: display_name, deprecated, deprecated_date, retirement_date, replaced_by, release_date, aliases. replaced_by is the name (Model.name, the value a caller puts in a request's model field, not the mdl_ id) of the model to move to once retirement_date passes; the data plane names it in its model_retired reply when a model of that name is enabled.
limits_jsonnoai_models.limits in full. The limits field above models only rpm and tpm, so it cannot represent a real catalogue entry -- max_output_tokens, max_images_per_prompt, rpd and ~10 more live only here. Read this one when round-tripping.
is_enablednoWhether this model is enabled. A disabled model is absent from the data plane's /v1/models. Only meaningful when the request set include_disabled -- the default list returns enabled models only.
created_atoutput-onlyWhen this model record was created.
updated_atoutput-onlyWhen this model record was last modified.
{"signatures":{"go":"func (x *CatalogClient) CreateModel(ctx context.Context, req *catalogv1.CreateModelRequest) (*catalogv1.Model, error)","python":"create_model(req: catalog_pb2.CreateModelRequest) -\u003e Model","typescript":"createModel(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.CreateModelRequestSchema\u003e): Promise\u003cModel\u003e","cli":"tare api catalog models create --provider-id $PROVIDER_ID --name $NAME","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/models:create\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider_id\": \"...\",\n    \"name\": \"...\",\n    \"upstream_model\": \"...\",\n    \"input_per_million\": \"...\",\n    \"output_per_million\": \"...\",\n    \"cache_read_per_million\": \"...\",\n    \"max_cost_per_request\": \"...\",\n    \"max_context_tokens\": 0,\n    \"capabilities\": [],\n    \"mode\": \"...\",\n    \"caching_per_million\": \"...\",\n    \"input_modalities\": [],\n    \"output_modalities\": [],\n    \"backend_urls\": [],\n    \"limits\": {},\n    \"additional_price_per_million\": {},\n    \"metadata\": {},\n    \"is_enabled\": false\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.CreateModelRequest{}\n\n\tresp, err := client.Catalog().CreateModel(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/createmodel\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.CreateModelRequest()\ntry:\n    result = client.catalog.create_model(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.createModel(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog models create --provider-id $PROVIDER_ID --name $NAME","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/models:create\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider_id\": \"...\",\n    \"name\": \"...\",\n    \"upstream_model\": \"...\",\n    \"input_per_million\": \"...\",\n    \"output_per_million\": \"...\",\n    \"cache_read_per_million\": \"...\",\n    \"max_cost_per_request\": \"...\",\n    \"max_context_tokens\": 0,\n    \"capabilities\": [],\n    \"mode\": \"...\",\n    \"caching_per_million\": \"...\",\n    \"input_modalities\": [],\n    \"output_modalities\": [],\n    \"backend_urls\": [],\n    \"limits\": {},\n    \"additional_price_per_million\": {},\n    \"metadata\": {},\n    \"is_enabled\": false\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/catalog/models:create","slug":"create-a-model"}

Create or update a model

Changed in v0.10.0 (behaviour changes): In single-organization installs, a Model Admin can now create, edit and delete models through the API with their own session. A user who holds the Platform Model Admin role can do the same in any install. GetModel now also accepts a platform grant of models.read, as ListModels does.

Changed in v0.10.0 (new features): UpsertModel takes clear_fields, to clear stored values such as a price, the context window or the backend URLs, and remove_metadata_keys, to remove metadata keys. A request that sets neither works as before.

Changed in v0.5.0 (new features): Catalog audit entries now say whether a write came from a batch. A sync run's several hundred UPSERT rows carry batched: true in their metadata and interactive edits carry batched: false, so the audit view can be filtered to just the changes a person made.

Changed in v0.2.0 (new features): The catalog API now carries a model's whole record. Modalities, rate and size limits, per-model backend URLs, non-per-token pricing, the cache-write rate, and free-form metadata (including the display name and the deprecation and retirement dates) can now be written and read back, where before they could only be set by editing the database. Providers likewise carry their routing metadata (upstream type, backend schema, API-key flavor and per-operation variants), which had no representation at all. Listing providers or models also accepts include_disabled, so the full catalog can be read rather than only the enabled part.

Changed in v0.2.0 (bug fixes): Editing a model or provider through the catalog API no longer switches it back on. Every write used to assert "enabled" whether or not it mentioned enablement, so an administrator's deliberate disable was reverted by the next unrelated edit (a price correction or a rename was enough) with nothing recording it. Enablement now changes only when a request actually asks it to. One consequence: a provider created through the API now arrives disabled, because enabling a provider is what makes the data plane start fetching its credentials, and one created before it has a credential cannot serve. Existing providers are unaffected, and enabling a new one is a single further call. If you script provider creation and relied on it being immediately enabled, ask for it explicitly.

Changed in v0.2.0 (bug fixes): A partial edit through the catalog API no longer blanks the fields it did not mention. Changing one property of a model (its mode, say) cleared its capabilities, per-token prices and context window, because an omitted value was written over the stored one rather than skipped. Fields a request does not carry are now left as they are. The model's modalities, limits, backend URLs and pricing extras follow the same rule now that the API can carry them at all; they were never reachable before, so nothing was lost through them.

Changed in v0.1.5 (bug fixes): Editing or removing a model through the catalog API (its upstream name, capabilities, or pricing) now takes effect on projects that route on their own hostname (strict isolation). Previously only the shared route map picked up the change, so a project with its own hostname kept serving the old model configuration until an unrelated change happened to refresh it. Companion to the provider-config fix above.

What it does: Creates or fully replaces a model record. The provider must already exist (FAILED_PRECONDITION otherwise), and must be enabled if this model is going to be enabled -- an enabled model under a disabled provider could never become routable, yet would read as available. Writing an explicitly DISABLED model under a disabled provider is allowed: neither appears in any route map, so nothing is misrepresented, and it is how a catalog sync stages a provider's models before an operator turns the provider on. A successful write propagates to the gateway automatically (no separate publish step): the model appears in the data plane's /v1/models and becomes promptable once the refreshed configuration lands. Poll /v1/models to confirm. The exception is suppress_route_regen, which defers that propagation to the caller's own configurator.TriggerUpdate. is_enabled is tri-state: omit it and the stored value is left alone, so a model an operator disabled is not silently re-enabled by a later write. A new model defaults to enabled. On update, a field left empty keeps its stored value. To clear a stored value, name the field in clear_fields; to remove a metadata key, name it in remove_metadata_keys. A request that sets neither writes exactly as before.

Request fields:

FieldRequiredDescription
provider_idyesProvider slug this model belongs to.
nameyesLogical model name, e.g. "gpt-4o". Unique within provider.
upstream_modelnoProvider-side model id (defaults to name when empty).
input_per_millionnoCost in USD per million input tokens (decimal string).
output_per_millionnoCost in USD per million output tokens (decimal string).
cache_read_per_millionnoCost in USD per million cache-read tokens. Empty if not applicable.
max_cost_per_requestnoHard cap in USD per single request. Empty means no cap.
max_context_tokensnoMaximum context window in tokens.
capabilitiesnoCapabilities this model supports, e.g. ["chat", "embeddings"].
modenoModel mode. One of: chat, completion, embedding, image_generation, moderation, rerank, responses, audio_speech, audio_transcription. Defaults to "chat" when empty. Required downstream: the data plane route map cannot be built for a model with an unset mode.
caching_per_millionnoCost in USD per million cache-WRITE tokens (ai_models.caching_tokens_price_per_million). Distinct from cache_read_per_million above. Empty if not applicable.
input_modalitiesnoInput modalities the model accepts, e.g. ["text", "image"]. Persisted as ai_models.modalities->'input'. Scalar so the CLI can flag it.
output_modalitiesnoOutput modalities the model produces, e.g. ["text"]. Persisted as ai_models.modalities->'output'.
backend_urlsnoPer-model backend URL overrides (ai_models.backend_urls). Empty leaves the model served from its provider's base_url.
limitsnoRate and size caps (ai_models.limits). Open-ended by design -- rpm, tpm, max_output_tokens, max_images_per_prompt and ~10 more. A Struct rather than the Limits message on the read path, which models only rpm/tpm and so cannot round-trip a real catalog entry.
additional_price_per_millionnoNon-per-token pricing (ai_models.additional_price_per_million): per-image, per-second, per-character, batch and cache-creation rates.
metadatanoFree-form model metadata (ai_models.metadata), merged over any existing value rather than replacing it. Carries the lifecycle and presentation fields the catalog depends on: display_name, deprecated, deprecated_date, retirement_date, replaced_by, release_date, aliases, source_url. The server always writes upstream_model and max_cost_per_request into this same jsonb, so keys set here must not collide with those two.
is_enablednoEnable or disable the model. UNSET LEAVES THE STORED VALUE UNTOUCHED, and that is the default -- see UpsertProviderRequest for the reasoning. On insert, unset means true: an explicitly-created model with no opinion stated is routable, which preserves the behavior every existing caller relies on.
suppress_route_regennoSkip the synchronous default GLOBAL_ROUTES regeneration. Batch callers only; see UpsertProviderRequest.suppress_route_regen for the flush obligation this creates.
clear_fieldsnoFields of this request whose stored value to clear, by field name. Allowed: input_per_million, output_per_million, cache_read_per_million, caching_per_million, max_cost_per_request, max_context_tokens, capabilities, input_modalities, output_modalities, backend_urls, limits, additional_price_per_million and upstream_model. A cleared upstream_model makes the model name the upstream id again. mode cannot be cleared: the data plane cannot route a model without one. An unknown name, or a field that this request also sets, is INVALID_ARGUMENT. Clearing a value that is not stored is a no-op.
remove_metadata_keysnoTop-level metadata keys to remove from the stored metadata. Applied after metadata is merged, so a key that is both set in metadata and listed here is removed. Removes whole top-level keys only. A key that is not stored is a no-op. upstream_model and max_cost_per_request are INVALID_ARGUMENT here: use clear_fields for them.

Response fields:

FieldRequiredDescription
idoutput-onlyModel id, a UUID. Server-assigned; immutable.
provider_idyesSlug of the provider this model belongs to.
nameyesLogical name used in routing and billing, e.g. "gpt-4o". Unique within a provider.
upstream_modelyesProvider-side model id sent as body.model to upstream, e.g. "gpt-4o-2024-11-20".
pricingnoPer-token pricing rates.
max_context_tokensnoMaximum context window in tokens.
capabilitiesnoCapabilities this model supports, e.g. "chat", "embeddings", "images".
modenoModel mode, e.g. "chat", "embedding", "image_generation". Surfaces the persisted value so callers can confirm what UpsertModel stored; reads default it to "chat" when the stored value is unset.
modalitiesnoInput/output modalities from ai_models.modalities.
limitsnoRate limits from ai_models.limits, narrowed to rpm/tpm. Retained for compatibility; prefer limits_json, which carries the column whole.
backend_urlsnoPer-model backend URL overrides (ai_models.backend_urls). Empty means the model is served from its provider's base_url.
additional_price_per_millionnoNon-per-token pricing (ai_models.additional_price_per_million).
metadatanoFull model metadata (ai_models.metadata), including the lifecycle fields a catalogue consumer needs: display_name, deprecated, deprecated_date, retirement_date, replaced_by, release_date, aliases. replaced_by is the name (Model.name, the value a caller puts in a request's model field, not the mdl_ id) of the model to move to once retirement_date passes; the data plane names it in its model_retired reply when a model of that name is enabled.
limits_jsonnoai_models.limits in full. The limits field above models only rpm and tpm, so it cannot represent a real catalogue entry -- max_output_tokens, max_images_per_prompt, rpd and ~10 more live only here. Read this one when round-tripping.
is_enablednoWhether this model is enabled. A disabled model is absent from the data plane's /v1/models. Only meaningful when the request set include_disabled -- the default list returns enabled models only.
created_atoutput-onlyWhen this model record was created.
updated_atoutput-onlyWhen this model record was last modified.
{"signatures":{"go":"func (x *CatalogClient) UpsertModel(ctx context.Context, req *catalogv1.UpsertModelRequest) (*catalogv1.Model, error)","python":"upsert_model(req: catalog_pb2.UpsertModelRequest) -\u003e Model","typescript":"upsertModel(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.UpsertModelRequestSchema\u003e): Promise\u003cModel\u003e","cli":"tare api catalog models upsert --provider-id $PROVIDER_ID --name $NAME","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider_id\": \"...\",\n    \"name\": \"...\",\n    \"upstream_model\": \"...\",\n    \"input_per_million\": \"...\",\n    \"output_per_million\": \"...\",\n    \"cache_read_per_million\": \"...\",\n    \"max_cost_per_request\": \"...\",\n    \"max_context_tokens\": 0,\n    \"capabilities\": [],\n    \"mode\": \"...\",\n    \"caching_per_million\": \"...\",\n    \"input_modalities\": [],\n    \"output_modalities\": [],\n    \"backend_urls\": [],\n    \"limits\": {},\n    \"additional_price_per_million\": {},\n    \"metadata\": {},\n    \"is_enabled\": false,\n    \"suppress_route_regen\": false,\n    \"clear_fields\": [],\n    \"remove_metadata_keys\": []\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.UpsertModelRequest{}\n\n\tresp, err := client.Catalog().UpsertModel(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/upsertmodel\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.UpsertModelRequest()\ntry:\n    result = client.catalog.upsert_model(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.upsertModel(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog models upsert --provider-id $PROVIDER_ID --name $NAME","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider_id\": \"...\",\n    \"name\": \"...\",\n    \"upstream_model\": \"...\",\n    \"input_per_million\": \"...\",\n    \"output_per_million\": \"...\",\n    \"cache_read_per_million\": \"...\",\n    \"max_cost_per_request\": \"...\",\n    \"max_context_tokens\": 0,\n    \"capabilities\": [],\n    \"mode\": \"...\",\n    \"caching_per_million\": \"...\",\n    \"input_modalities\": [],\n    \"output_modalities\": [],\n    \"backend_urls\": [],\n    \"limits\": {},\n    \"additional_price_per_million\": {},\n    \"metadata\": {},\n    \"is_enabled\": false,\n    \"suppress_route_regen\": false,\n    \"clear_fields\": [],\n    \"remove_metadata_keys\": []\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/catalog/models","slug":"create-or-update-a-model"}

Set a provider credential

Changed in v0.6.0 (behaviour changes): GetProvider and the provider writes now also accept a platform role binding that grants the providers permission, such as platform_provider_admin. The admin scope still works as before.

Changed in v0.1.5 (bug fixes): Editing a provider's connection settings through the catalog API (base URL or credential, or removing the provider) now takes effect on projects that route on their own hostname (strict isolation). Previously only the shared route map picked up the change, so a project with its own hostname kept serving the old provider configuration until an unrelated change happened to refresh it.

What it does: Stores or rotates the credential used to authenticate requests to this provider. The plaintext is never returned; the provider's credential_suffix reflects the last 4 characters.

Request fields:

FieldRequiredDescription
idyesProvider id / slug.
credentialyesPlaintext credential (API key, bearer token, etc.). Stored server-side under envelope encryption and never returned. Provider.credential_suffix reflects the last 4 chars.

Response fields:

FieldRequiredDescription
idoutput-onlyThe provider's slug, the same value as name. It is the key every provider RPC takes as id. Immutable.
nameyesMachine-readable slug, e.g. "openai", "anthropic", "awsbedrock". Unique. Canonical form is lowercase alphanumeric [a-z0-9]+ (no hyphens, spaces, or uppercase) -- it is matched hyphen-insensitively at inference time, so a write that creates a provider (CreateProvider, or UpsertProvider for a new id) requires this canonical form. An existing provider with an older slug form stays editable.
display_nameyesHuman-readable display name, e.g. "OpenAI".
base_urlyesBase URL for all models served by this provider, e.g. "https://api.openai.com".
supported_auth_schemesnoAuth schemes this provider supports. Holds at most one value, the stored auth type: "api_key", "aws", "vertex", "vertex_anthropic", "none", "google_ai_key" or "azure".
credential_suffixoutput-onlyLast 4 chars of the stored platform credential (e.g. "sk-..**4a2f"). Empty if no platform credential has been set. BYOK keys are tracked separately via aikeys.v1. Always empty on a tenant-path read (a request that names customer_id).
metadatanoNon-secret data-path semantics from ai_providers.metadata: type, backend_schema, api_key_flavor, operation_variants. The credential keys are never included. A tenant-path read (a request that names customer_id) keeps only type, backend_schema, api_key_flavor, operation_variants and first_party.
is_enablednoWhether this provider is enabled. A disabled provider is absent from the data plane's route map and its credentials are not reconciled. Only visible when the request set include_disabled -- the default list returns enabled providers only, so this always reads true there.
has_credentialoutput-onlyWhether the platform credential for this provider is stored: a live (not revoked) secret named AISECRET_<SLUG> for user "system" in the "llm-providers" realm, which is the secret the data plane reads. True regardless of which path wrote it (SetProviderCredential, the admin dashboard or a legacy-KMS import). A credential that exists only in legacy KMS and was never imported reads false. Computed by ListProviders and GetProvider (on both paths) and admin.v1.ListCatalogProviders; SetProviderCredential returns true and DeleteProviderCredential false. Unset means "not known", never "no credential": a read whose credential lookup fails still returns the providers, with this field unset, and CreateProvider and UpsertProvider do not look it up. A client must not show an unset value as "no key".
created_atoutput-onlyWhen this provider record was created.
updated_atoutput-onlyWhen this provider record was last modified.
{"signatures":{"go":"func (x *CatalogClient) SetProviderCredential(ctx context.Context, req *catalogv1.SetProviderCredentialRequest) (*catalogv1.Provider, error)","python":"set_provider_credential(req: catalog_pb2.SetProviderCredentialRequest) -\u003e Provider","typescript":"setProviderCredential(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.SetProviderCredentialRequestSchema\u003e): Promise\u003cProvider\u003e","cli":"tare api catalog providers set-credential \u003cid\u003e --credential $CREDENTIAL","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H.../credential\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"credential\": \"...\"\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.SetProviderCredentialRequest{}\n\n\tresp, err := client.Catalog().SetProviderCredential(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/setprovidercredential\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.SetProviderCredentialRequest()\ntry:\n    result = client.catalog.set_provider_credential(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.setProviderCredential(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog providers set-credential \u003cid\u003e --credential $CREDENTIAL","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H.../credential\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"credential\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/catalog/providers/{id}/credential","slug":"set-a-provider-credential"}

Delete a provider credential

Changed in v0.6.0 (new features): CreateProvider adds a provider and returns AlreadyExists if the id is taken, and DeleteProviderCredential removes a provider's platform key. UpsertProvider now needs display_name, base_url and a canonical id only when it creates the provider, so {id, is_enabled} toggles an existing one.

What it does: Removes the provider's stored platform credential. Afterwards has_credential is false, credential_suffix is empty, and the data plane stops sending the key. Idempotent: calling it for a provider with no credential succeeds and changes nothing. Refuses with FAILED_PRECONDITION while the provider is enabled and its auth type needs a key; disable the provider first. aws (the data plane falls back to its IAM role) and none are exempt.

Request fields:

FieldRequiredDescription
idyesSlug of the provider whose credential to delete, e.g. "openai".

Response fields:

FieldRequiredDescription
idoutput-onlyThe provider's slug, the same value as name. It is the key every provider RPC takes as id. Immutable.
nameyesMachine-readable slug, e.g. "openai", "anthropic", "awsbedrock". Unique. Canonical form is lowercase alphanumeric [a-z0-9]+ (no hyphens, spaces, or uppercase) -- it is matched hyphen-insensitively at inference time, so a write that creates a provider (CreateProvider, or UpsertProvider for a new id) requires this canonical form. An existing provider with an older slug form stays editable.
display_nameyesHuman-readable display name, e.g. "OpenAI".
base_urlyesBase URL for all models served by this provider, e.g. "https://api.openai.com".
supported_auth_schemesnoAuth schemes this provider supports. Holds at most one value, the stored auth type: "api_key", "aws", "vertex", "vertex_anthropic", "none", "google_ai_key" or "azure".
credential_suffixoutput-onlyLast 4 chars of the stored platform credential (e.g. "sk-..**4a2f"). Empty if no platform credential has been set. BYOK keys are tracked separately via aikeys.v1. Always empty on a tenant-path read (a request that names customer_id).
metadatanoNon-secret data-path semantics from ai_providers.metadata: type, backend_schema, api_key_flavor, operation_variants. The credential keys are never included. A tenant-path read (a request that names customer_id) keeps only type, backend_schema, api_key_flavor, operation_variants and first_party.
is_enablednoWhether this provider is enabled. A disabled provider is absent from the data plane's route map and its credentials are not reconciled. Only visible when the request set include_disabled -- the default list returns enabled providers only, so this always reads true there.
has_credentialoutput-onlyWhether the platform credential for this provider is stored: a live (not revoked) secret named AISECRET_<SLUG> for user "system" in the "llm-providers" realm, which is the secret the data plane reads. True regardless of which path wrote it (SetProviderCredential, the admin dashboard or a legacy-KMS import). A credential that exists only in legacy KMS and was never imported reads false. Computed by ListProviders and GetProvider (on both paths) and admin.v1.ListCatalogProviders; SetProviderCredential returns true and DeleteProviderCredential false. Unset means "not known", never "no credential": a read whose credential lookup fails still returns the providers, with this field unset, and CreateProvider and UpsertProvider do not look it up. A client must not show an unset value as "no key".
created_atoutput-onlyWhen this provider record was created.
updated_atoutput-onlyWhen this provider record was last modified.
{"signatures":{"go":"func (x *CatalogClient) DeleteProviderCredential(ctx context.Context, req *catalogv1.DeleteProviderCredentialRequest) (*catalogv1.Provider, error)","python":"delete_provider_credential(req: catalog_pb2.DeleteProviderCredentialRequest) -\u003e Provider","typescript":"deleteProviderCredential(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.DeleteProviderCredentialRequestSchema\u003e): Promise\u003cProvider\u003e","cli":"tare api catalog providers delete-credential \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H.../credential\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.DeleteProviderCredentialRequest{}\n\n\tresp, err := client.Catalog().DeleteProviderCredential(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/deleteprovidercredential\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.DeleteProviderCredentialRequest()\ntry:\n    result = client.catalog.delete_provider_credential(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.deleteProviderCredential(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog providers delete-credential \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H.../credential\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/catalog/providers/{id}/credential","slug":"delete-a-provider-credential"}

Delete a provider

Changed in v0.6.0 (behaviour changes): GetProvider and the provider writes now also accept a platform role binding that grants the providers permission, such as platform_provider_admin. The admin scope still works as before.

Changed in v0.1.5 (bug fixes): Editing a provider's connection settings through the catalog API (base URL or credential, or removing the provider) now takes effect on projects that route on their own hostname (strict isolation). Previously only the shared route map picked up the change, so a project with its own hostname kept serving the old provider configuration until an unrelated change happened to refresh it.

What it does: Removes a provider, along with the project assignments that granted access to it. Refuses while any model still references it, while any BYOK API key is registered against it (revoke those keys first), or when it is the only provider assigned to a project that still has models assigned. On success, any revoked BYOK keys for this provider and their stored secrets are permanently removed, and the provider's own stored credential is retired.

Request fields:

FieldRequiredDescription
idyesSlug of the provider to delete, e.g. "openai".
{"signatures":{"go":"func (x *CatalogClient) DeleteProvider(ctx context.Context, req *catalogv1.DeleteProviderRequest) error","python":"delete_provider(req: catalog_pb2.DeleteProviderRequest) -\u003e None","typescript":"deleteProvider(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.DeleteProviderRequestSchema\u003e): Promise\u003cvoid\u003e","cli":"tare api catalog providers delete \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.DeleteProviderRequest{}\n\n\tif err := client.Catalog().DeleteProvider(ctx, req); err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Println(\"ok\")\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/deleteprovider\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.DeleteProviderRequest()\ntry:\n    result = client.catalog.delete_provider(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.deleteProvider(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog providers delete \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/catalog/providers/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/catalog/providers/{id}","slug":"delete-a-provider"}

Delete a model

Changed in v0.10.0 (behaviour changes): Deleting a model now also removes its project assignments, in the same change. The audit record counts them in metadata.project_assignments_removed.

Changed in v0.10.0 (behaviour changes): In single-organization installs, a Model Admin can now create, edit and delete models through the API with their own session. A user who holds the Platform Model Admin role can do the same in any install. GetModel now also accepts a platform grant of models.read, as ListModels does.

Changed in v0.1.5 (bug fixes): Editing or removing a model through the catalog API (its upstream name, capabilities, or pricing) now takes effect on projects that route on their own hostname (strict isolation). Previously only the shared route map picked up the change, so a project with its own hostname kept serving the old model configuration until an unrelated change happened to refresh it. Companion to the provider-config fix above.

What it does: Removes a model entry, along with its project assignments, in one transaction.

Request fields:

FieldRequiredDescription
idyesId of the model to delete, a UUID.
{"signatures":{"go":"func (x *CatalogClient) DeleteModel(ctx context.Context, req *catalogv1.DeleteModelRequest) error","python":"delete_model(req: catalog_pb2.DeleteModelRequest) -\u003e None","typescript":"deleteModel(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.DeleteModelRequestSchema\u003e): Promise\u003cvoid\u003e","cli":"tare api catalog models delete \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/catalog/models/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.DeleteModelRequest{}\n\n\tif err := client.Catalog().DeleteModel(ctx, req); err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Println(\"ok\")\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/deletemodel\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.DeleteModelRequest()\ntry:\n    result = client.catalog.delete_model(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.deleteModel(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api catalog models delete \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/catalog/models/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/catalog/models/{id}","slug":"delete-a-model"}

Assign a model to a project

Changed in v0.11.0 (new features): AssignModelToProject and UnassignModelFromProject now accept a caller who holds the project.model.edit permission for the project, also when the caller is not one of the project's users. The permission can come from an organization role, a role on the project, or a management key's own role. An organization admin no longer has to be added to a project to manage its models. A management key uses only its own roles, not the roles of the user who owns it.

Changed in v0.8.0 (bug fixes): Adding or removing a model from a project now updates keys that carry their own settings (BYOK, fallback, traffic split, or a rate limit), so they no longer keep routing against the old model list. A key created with CreateApiKey or IssueMyKey now gets its own routes instead of waiting on an unrelated change. Both updates are sent in the background, so the call returns without waiting for them.

What it does: Grants a project access to a catalog model. Idempotent: re-assigning a model is a no-op. The model's provider must be enabled (FAILED_PRECONDITION otherwise) -- a model under a disabled provider is absent from the route map and could not be served. Propagates automatically to any Project Gateway attached to the project (per-gateway route maps serve only assigned models); no separate publish step is needed.

Request fields:

FieldRequiredDescription
customer_idnoDefaults to the caller's session customer when empty.
project_idnoDefaults to the caller's session project when empty.
model_idyesCatalog model id (uuid).

Response fields:

FieldRequiredDescription
entrynoThe created (or already-existing) project-to-model assignment.
{"signatures":{"go":"func (x *CatalogClient) AssignModelToProject(ctx context.Context, req *catalogv1.AssignModelToProjectRequest) (*catalogv1.AssignModelToProjectResponse, error)","python":"assign_model_to_project(req: catalog_pb2.AssignModelToProjectRequest) -\u003e AssignModelToProjectResponse","typescript":"assignModelToProject(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.AssignModelToProjectRequestSchema\u003e): Promise\u003cAssignModelToProjectResponse\u003e","cli":"tare api project models add --model-id $MODEL_ID","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model_id\": \"...\"\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.AssignModelToProjectRequest{}\n\n\tresp, err := client.Catalog().AssignModelToProject(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/assignmodeltoproject\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.AssignModelToProjectRequest()\ntry:\n    result = client.catalog.assign_model_to_project(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.assignModelToProject(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api project models add --model-id $MODEL_ID","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"model_id\": \"...\"\n  }'"},"persona":"Authenticated (API key or session token)","httpVerb":"POST","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/models","slug":"assign-a-model-to-a-project"}

List project models

What it does: Returns every catalog model assigned to a project.

Request fields:

FieldRequiredDescription
customer_idnoDefaults to the caller's session customer when empty.
project_idnoDefaults to the caller's session project when empty.

Response fields:

FieldRequiredDescription
entriesnoEvery model assigned to the project, each joined with its catalog Model.
{"signatures":{"go":"func (x *CatalogClient) ListProjectModels(ctx context.Context, req *catalogv1.ListProjectModelsRequest) (*catalogv1.ListProjectModelsResponse, error)","python":"list_project_models(req: catalog_pb2.ListProjectModelsRequest) -\u003e ListProjectModelsResponse","typescript":"listProjectModels(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.ListProjectModelsRequestSchema\u003e): Promise\u003cListProjectModelsResponse\u003e","cli":"tare api project models list","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.ListProjectModelsRequest{}\n\n\tresp, err := client.Catalog().ListProjectModels(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/listprojectmodels\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.ListProjectModelsRequest()\ntry:\n    result = client.catalog.list_project_models(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.listProjectModels(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api project models list","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../models\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Authenticated (API key or session token)","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/models","slug":"list-project-models"}

Unassign a model from a project

Changed in v0.11.0 (new features): AssignModelToProject and UnassignModelFromProject now accept a caller who holds the project.model.edit permission for the project, also when the caller is not one of the project's users. The permission can come from an organization role, a role on the project, or a management key's own role. An organization admin no longer has to be added to a project to manage its models. A management key uses only its own roles, not the roles of the user who owns it.

Changed in v0.8.0 (bug fixes): Adding or removing a model from a project now updates keys that carry their own settings (BYOK, fallback, traffic split, or a rate limit), so they no longer keep routing against the old model list. A key created with CreateApiKey or IssueMyKey now gets its own routes instead of waiting on an unrelated change. Both updates are sent in the background, so the call returns without waiting for them.

What it does: Removes a project's access to a catalog model.

Request fields:

FieldRequiredDescription
customer_idnoDefaults to the caller's session customer when empty.
project_idnoDefaults to the caller's session project when empty.
model_idyesCatalog model id (uuid) to unassign from the project.

Response fields:

FieldRequiredDescription
successnoTrue when the assignment was removed.
{"signatures":{"go":"func (x *CatalogClient) UnassignModelFromProject(ctx context.Context, req *catalogv1.UnassignModelFromProjectRequest) (*catalogv1.UnassignModelFromProjectResponse, error)","python":"unassign_model_from_project(req: catalog_pb2.UnassignModelFromProjectRequest) -\u003e UnassignModelFromProjectResponse","typescript":"unassignModelFromProject(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.UnassignModelFromProjectRequestSchema\u003e): Promise\u003cUnassignModelFromProjectResponse\u003e","cli":"tare api project models remove --model-id $MODEL_ID","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../models/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.UnassignModelFromProjectRequest{}\n\n\tresp, err := client.Catalog().UnassignModelFromProject(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/unassignmodelfromproject\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.UnassignModelFromProjectRequest()\ntry:\n    result = client.catalog.unassign_model_from_project(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.unassignModelFromProject(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"cli":"tare api project models remove --model-id $MODEL_ID","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../models/01H...\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Authenticated (API key or session token)","httpVerb":"DELETE","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/models/{model_id}","slug":"unassign-a-model-from-a-project"}

Record a catalog sync run

Changed in v0.5.0 (new features): The catalog API can now record and read a management plane's catalog sync state: which published catalog version it last read, which one it is actually on, when each happened and who ran it. The same pair is on the admin API, so a scheduled job authenticating with a plane's admin key writes the same record an operator's own run does.

What it does: Persists what the last catalog sync saw and did. A sync runs in the client: it fetches a published catalog, compares it with this management plane and writes the difference. The catalog version that drove a run is only known to that client, and a report-only run changes nothing the server could observe, so a run reports itself here. How the record advances: - checked_at / checked_version advance on every recorded run, including report-only ones. They say when this plane last looked at a published catalog, and which one. - applied_at / applied_version advance only when applied is set, which means the run wrote the catalog and left nothing unwritten. They say which published catalog this plane is actually on. The two together are the drift an operator acts on: the published catalog is at version X, this plane is on version Y, applied N days ago.

Request fields:

FieldRequiredDescription
catalog_versionnoexportedAt of the catalogue this run read, verbatim from the document envelope. May be empty: the field is optional in a published catalogue, and an absent version is reported as absent rather than guessed at.
appliednoTrue only for an --apply run that finished with zero per-entry failures. A dry run and a partial apply both leave applied_version alone.
apply_modenoWhether this run wrote or only reported, independent of whether it succeeded. Records the run's INTENT, so a partial apply is still distinguishable from a dry run in the state.
sourcesnoThe documents read, in the order the run read them.
providers_writtennoProviders written by this run. Ignored unless applied is set.
models_writtennoModels written by this run. Ignored unless applied is set.

Response fields:

FieldRequiredDescription
statenoThe merged record as persisted.
{"signatures":{"go":"func (x *CatalogClient) RecordCatalogSync(ctx context.Context, req *catalogv1.RecordCatalogSyncRequest) (*catalogv1.RecordCatalogSyncResponse, error)","python":"record_catalog_sync(req: catalog_pb2.RecordCatalogSyncRequest) -\u003e RecordCatalogSyncResponse","typescript":"recordCatalogSync(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.RecordCatalogSyncRequestSchema\u003e): Promise\u003cRecordCatalogSyncResponse\u003e","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/sync-state\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"catalog_version\": \"...\",\n    \"applied\": false,\n    \"apply_mode\": false,\n    \"sources\": [],\n    \"providers_written\": 0,\n    \"models_written\": 0\n  }'"},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.RecordCatalogSyncRequest{}\n\n\tresp, err := client.Catalog().RecordCatalogSync(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/recordcatalogsync\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.RecordCatalogSyncRequest()\ntry:\n    result = client.catalog.record_catalog_sync(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.recordCatalogSync(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/catalog/sync-state\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"catalog_version\": \"...\",\n    \"applied\": false,\n    \"apply_mode\": false,\n    \"sources\": [],\n    \"providers_written\": 0,\n    \"models_written\": 0\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/catalog/sync-state","slug":"record-a-catalog-sync-run"}

Get the catalog sync state

Changed in v0.5.0 (new features): The catalog API can now record and read a management plane's catalog sync state: which published catalog version it last read, which one it is actually on, when each happened and who ran it. The same pair is on the admin API, so a scheduled job authenticating with a plane's admin key writes the same record an operator's own run does.

What it does: Returns this plane's catalog sync record. Never NotFound: a plane that has never recorded a sync returns a state with both timestamps unset, which is itself the answer -- "no sync has ever run here" is a different and more actionable report than an error.

Request body: None.

Response fields:

FieldRequiredDescription
statenoNever null. A plane that has never recorded a sync returns a state with both timestamps unset.
{"signatures":{"go":"func (x *CatalogClient) GetCatalogSyncState(ctx context.Context, req *catalogv1.GetCatalogSyncStateRequest) (*catalogv1.GetCatalogSyncStateResponse, error)","python":"get_catalog_sync_state(req: catalog_pb2.GetCatalogSyncStateRequest) -\u003e GetCatalogSyncStateResponse","typescript":"getCatalogSyncState(req: MessageInitShape\u003ctypeof tars_catalog_v1_catalog_pb.GetCatalogSyncStateRequestSchema\u003e): Promise\u003cGetCatalogSyncStateResponse\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/sync-state\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"examples":{"go":{"files":[{"name":"main.go","content":"// Command example is a runnable example for the AgentRouter Go SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then `go run .`.\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\tagentrouter \"github.com/tetrateio/agentrouter-go\"\n\tcatalogv1 \"github.com/tetrateio/agentrouter-go/genapi/api/tars/catalog/v1\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tclient, err := agentrouter.New(ctx,\n\t\tagentrouter.WithBaseURL(os.Getenv(\"AGENTROUTER_BASE_URL\")),\n\t\tagentrouter.WithAPIKey(os.Getenv(\"AGENTROUTER_API_KEY\")),\n\t)\n\tif err != nil {\n\t\tlog.Fatalf(\"client: %v\", err)\n\t}\n\n\t// Populate the request fields -- see the \"Request fields\" table above for\n\t// the available fields and which are required.\n\treq := \u0026catalogv1.GetCatalogSyncStateRequest{}\n\n\tresp, err := client.Catalog().GetCatalogSyncState(ctx, req)\n\tif err != nil {\n\t\tlog.Fatalf(\"call: %v\", err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", resp)\n}\n"},{"name":"go.mod","content":"module github.com/tetrateio/agentrouter-go-examples/catalog/getcatalogsyncstate\n\ngo 1.26\n\nrequire github.com/tetrateio/agentrouter-go v0.1.1\n\n// Point this at the directory you extracted the downloaded Go SDK tarball into.\n// The directory name matches the tarball stem on the Download SDK page.\nreplace github.com/tetrateio/agentrouter-go =\u003e ./third_party/agentrouter-go-0.1.1\n"}]},"python":{"files":[{"name":"main.py","content":"\"\"\"Runnable example for the AgentRouter Python SDK.\n\nSet AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `python main.py`.\n\"\"\"\nimport os\n\nfrom tars.catalog.v1 import catalog_pb2\n\nfrom agentrouter_sdk import Client\n\nclient = Client(\n    base_url=os.environ[\"AGENTROUTER_BASE_URL\"],\n    api_key=os.environ[\"AGENTROUTER_API_KEY\"],\n)\n\n# Populate the request fields -- see the \"Request fields\" table above\n# for the available fields and which are required.\nreq = catalog_pb2.GetCatalogSyncStateRequest()\ntry:\n    result = client.catalog.get_catalog_sync_state(req)\n    print(result)\nexcept Exception as err:\n    print(\"Error:\", err)\n"},{"name":"requirements.txt","content":"# Point this at the directory you extracted the downloaded Python SDK tarball\n# into. The directory name matches the tarball stem on the Download SDK page.\n# To install instead from PyPI once published, replace the line below with:\n#   agentrouter-sdk\u003e=0.1.0\nagentrouter-sdk @ file:./third_party/agentrouter-python-0.1.1\n"}]},"typescript":{"files":[{"name":"index.ts","content":"// Runnable example for the AgentRouter TypeScript SDK.\n// Set AGENTROUTER_BASE_URL and AGENTROUTER_API_KEY in the environment, then run `npm install \u0026\u0026 npx tsx index.ts`.\nimport { Client } from '@tetrate/agentrouter-sdk'\n\nconst client = new Client({\n  baseUrl: process.env.AGENTROUTER_BASE_URL,\n  apiKey: process.env.AGENTROUTER_API_KEY,\n})\n\n// Populate the request fields -- see the \"Request fields\" table above\n// for the available fields and which are required.\nconst req = {}\ntry {\n  const result = await client.catalog.getCatalogSyncState(req)\n  console.log(result)\n} catch (err) {\n  console.error('Error:', err)\n}\n"},{"name":"package.json","content":"{\n  \"name\": \"catalog\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"@tetrate/agentrouter-sdk\": \"file:./third_party/agentrouter-typescript-0.1.1\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^20.0.0\",\n    \"typescript\": \"^5.4.0\"\n  }\n}\n"},{"name":"tsconfig.json","content":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  }\n}\n"}]},"curl":"curl \"${AGENTROUTER_BASE_URL}/v1/catalog/sync-state\" \\\n  -H \"Authorization: Bearer ${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/catalog/sync-state","slug":"get-the-catalog-sync-state"}