> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/api-reference/hosted-providers.md).

# Hosted providers

## List Hosted Providers

> List the providers this deployment serves hosted inference on.\
> \
> Stored providers and those defined in the configuration's\
> \`\`hosted\_providers:\`\` block alike, each marked with its \`\`source\`\`.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedProvidersPublic":{"description":"A page of hosted providers, and how many there are in total.","properties":{"count":{"title":"Count","type":"integer"},"data":{"items":{"$ref":"#/components/schemas/HostedProviderPublic"},"title":"Data","type":"array"}},"required":["data","count"],"title":"HostedProvidersPublic","type":"object"},"HostedProviderPublic":{"description":"One hosted provider, known by its name, its implementation and the tail of its key.\n\nNo shape in this section carries key material: a stored credential is\nknown by its last four characters and nothing else, ``client_args`` goes\nout with credential-shaped values masked, and ``api_base`` with the\npassword in its userinfo and its query values masked.\n\n``name`` is what requests address and every route takes; ``provider`` is\nthe any-llm implementation. ``source`` says where the provider is defined.\nA ``config`` provider comes from the ``hosted_providers:`` block, has no\nrow and so no ``id`` or timestamps, and is changed by editing the\nconfiguration rather than through this API; its models are administered as\na stored provider's are. ``shadows_config`` marks a stored provider that\nwins over a configuration entry of the same name, which serves again once\nthe row is deleted. ``has_api_key`` is false for one stored without a key,\nand ``decryptable`` false for one whose key or extras no configured\n``OTARI_SECRET_KEY`` reads, which serves nothing until re-entered.\n``serves_unlisted_models`` is true for one that serves a model its roster\ndoes not list. ``supported_countries`` is null for one that serves every\ncountry.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key_last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Last4"},"client_args":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Client Args"},"created_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Created At"},"decryptable":{"default":true,"title":"Decryptable","type":"boolean"},"enabled":{"title":"Enabled","type":"boolean"},"has_api_key":{"default":true,"title":"Has Api Key","type":"boolean"},"id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Id"},"name":{"title":"Name","type":"string"},"provider":{"title":"Provider","type":"string"},"restricted_to_organization_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Restricted To Organization Id"},"serves_unlisted_models":{"default":false,"title":"Serves Unlisted Models","type":"boolean"},"shadows_config":{"default":false,"title":"Shadows Config","type":"boolean"},"source":{"default":"database","enum":["database","config"],"title":"Source","type":"string"},"supported_countries":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Supported Countries"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","provider","enabled"],"title":"HostedProviderPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers":{"get":{"description":"List the providers this deployment serves hosted inference on.\n\nStored providers and those defined in the configuration's\n``hosted_providers:`` block alike, each marked with its ``source``.","operationId":"hosted-providers-list_hosted_providers","parameters":[{"in":"query","name":"skip","required":false,"schema":{"default":0,"minimum":0,"title":"Skip","type":"integer"}},{"in":"query","name":"limit","required":false,"schema":{"default":100,"maximum":1000,"minimum":1,"title":"Limit","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProvidersPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Hosted Providers","tags":["hosted-providers"]}}}}
```

## Create Hosted Provider

> Configure a provider with the key this deployment will serve it on.\
> \
> Everything the provider lists on that key is offered at once; a provider\
> that will not say yields a provider with no models, not an error. A\
> provider the configuration defines may be created too, and the stored one\
> then serves instead.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedProviderCreateRequest":{"description":"Configure a provider this deployment will serve hosted inference on.\n\n``provider`` is the any-llm implementation. ``name`` is what requests\naddress it by (``name:model``), defaulting to ``provider``; a name of its\nown lets two endpoints of one implementation coexist. ``api_key`` may be\nleft out for a backend that needs none, or for an endpoint of its own\n(``api_base``), which is called with a placeholder key.\n\n``client_args`` is whatever the provider's SDK client needs beyond the key\n(Bedrock's region and IAM pair), the same shape an organization provider\nkey takes. Values under credential-shaped names come back masked.\n``restricted_to_organization_id`` keeps the provider to one organization;\nevery other one is served as if it were not configured.\n``serves_unlisted_models`` serves a model the roster does not list, as a\n``config.yml`` instance does, and switches on every model it offers\nwhether or not something prices it. ``supported_countries`` keeps the\nprovider to requests from those ISO country codes, checked as well as\n``provider_supported_countries``; null serves every country.","properties":{"api_base":{"anyOf":[{"maxLength":1024,"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key":{"anyOf":[{"maxLength":1024,"minLength":1,"type":"string"},{"type":"null"}],"title":"Api Key"},"client_args":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Client Args"},"enabled":{"default":true,"title":"Enabled","type":"boolean"},"name":{"anyOf":[{"maxLength":64,"minLength":1,"type":"string"},{"type":"null"}],"title":"Name"},"provider":{"maxLength":64,"minLength":1,"title":"Provider","type":"string"},"restricted_to_organization_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Restricted To Organization Id"},"serves_unlisted_models":{"default":false,"title":"Serves Unlisted Models","type":"boolean"},"supported_countries":{"anyOf":[{"items":{"type":"string"},"maxItems":300,"type":"array"},{"type":"null"}],"title":"Supported Countries"}},"required":["provider"],"title":"HostedProviderCreateRequest","type":"object"},"HostedProviderPublic":{"description":"One hosted provider, known by its name, its implementation and the tail of its key.\n\nNo shape in this section carries key material: a stored credential is\nknown by its last four characters and nothing else, ``client_args`` goes\nout with credential-shaped values masked, and ``api_base`` with the\npassword in its userinfo and its query values masked.\n\n``name`` is what requests address and every route takes; ``provider`` is\nthe any-llm implementation. ``source`` says where the provider is defined.\nA ``config`` provider comes from the ``hosted_providers:`` block, has no\nrow and so no ``id`` or timestamps, and is changed by editing the\nconfiguration rather than through this API; its models are administered as\na stored provider's are. ``shadows_config`` marks a stored provider that\nwins over a configuration entry of the same name, which serves again once\nthe row is deleted. ``has_api_key`` is false for one stored without a key,\nand ``decryptable`` false for one whose key or extras no configured\n``OTARI_SECRET_KEY`` reads, which serves nothing until re-entered.\n``serves_unlisted_models`` is true for one that serves a model its roster\ndoes not list. ``supported_countries`` is null for one that serves every\ncountry.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key_last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Last4"},"client_args":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Client Args"},"created_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Created At"},"decryptable":{"default":true,"title":"Decryptable","type":"boolean"},"enabled":{"title":"Enabled","type":"boolean"},"has_api_key":{"default":true,"title":"Has Api Key","type":"boolean"},"id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Id"},"name":{"title":"Name","type":"string"},"provider":{"title":"Provider","type":"string"},"restricted_to_organization_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Restricted To Organization Id"},"serves_unlisted_models":{"default":false,"title":"Serves Unlisted Models","type":"boolean"},"shadows_config":{"default":false,"title":"Shadows Config","type":"boolean"},"source":{"default":"database","enum":["database","config"],"title":"Source","type":"string"},"supported_countries":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Supported Countries"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","provider","enabled"],"title":"HostedProviderPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers":{"post":{"description":"Configure a provider with the key this deployment will serve it on.\n\nEverything the provider lists on that key is offered at once; a provider\nthat will not say yields a provider with no models, not an error. A\nprovider the configuration defines may be created too, and the stored one\nthen serves instead.","operationId":"hosted-providers-create_hosted_provider","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProviderCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProviderPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Create Hosted Provider","tags":["hosted-providers"]}}}}
```

## Update Catalog

> Apply the reviewed prices, then refresh every provider's models, then the cleanup if asked.\
> \
> The price step comes first, so the model step prices new models at the\
> prices it applied, and a model held for want of a price is served once it\
> has one. Without a \`\`digest\`\` no price changes. A \`\`digest\`\` that no longer\
> names the pending prices answers 409 and changes nothing. \`\`cleanup\`\`\
> removes the prices of models no provider serves, with their history.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedCatalogUpdateRequest":{"description":"What to apply, as the preview showed it.","properties":{"cleanup":{"default":false,"description":"Also remove the prices of models no hosted provider serves, with their history.","title":"Cleanup","type":"boolean"},"digest":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The `digest` of the previewed prices, to apply them; 409 when the pending prices differ. Without it, no price changes.","title":"Digest"}},"title":"HostedCatalogUpdateRequest","type":"object"},"HostedCatalogUpdatePublic":{"description":"What a catalog update did.","properties":{"catalog":{"$ref":"#/components/schemas/HostedCatalogRefreshPublic"},"prices_applied":{"title":"Prices Applied","type":"boolean"}},"required":["prices_applied","catalog"],"title":"HostedCatalogUpdatePublic","type":"object"},"HostedCatalogRefreshPublic":{"description":"What a catalog sweep did, or would do.\n\n``removed`` is a sample and ``removed_count`` the total, so a deployment\nthat has never been swept does not answer one button press with a few\nthousand strings. ``orphaned_rosters`` names the providers that still have\na model list but nothing defining them, such as a removed ``config.yml``\nentry: nothing serves them, and an apply deletes their model lists.","properties":{"kept":{"items":{"$ref":"#/components/schemas/HostedCatalogKeptGroupPublic"},"title":"Kept","type":"array"},"orphaned_rosters":{"items":{"type":"string"},"title":"Orphaned Rosters","type":"array"},"providers":{"items":{"$ref":"#/components/schemas/HostedCatalogProviderRefreshPublic"},"title":"Providers","type":"array"},"removed":{"items":{"type":"string"},"title":"Removed","type":"array"},"removed_count":{"title":"Removed Count","type":"integer"},"removed_override_rows":{"title":"Removed Override Rows","type":"integer"},"removed_price_rows":{"title":"Removed Price Rows","type":"integer"}},"required":["providers","removed","removed_count","removed_price_rows","removed_override_rows","kept"],"title":"HostedCatalogRefreshPublic","type":"object"},"HostedCatalogKeptGroupPublic":{"description":"Priced models a sweep left in place, grouped by the reason it did.","properties":{"count":{"title":"Count","type":"integer"},"models":{"items":{"type":"string"},"title":"Models","type":"array"},"reason":{"title":"Reason","type":"string"}},"required":["reason","count","models"],"title":"HostedCatalogKeptGroupPublic","type":"object"},"HostedCatalogProviderRefreshPublic":{"description":"What one hosted provider newly lists, as a catalog sweep found it.\n\nPer provider rather than one total, because a dial that failed is a fact\nabout that provider, and the operator has to be told which one went quiet\nrather than read a smaller number than they expected.","properties":{"added":{"items":{"type":"string"},"title":"Added","type":"array"},"credential_unreadable":{"default":false,"title":"Credential Unreadable","type":"boolean"},"discovery_unsupported":{"default":false,"title":"Discovery Unsupported","type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"provider":{"title":"Provider","type":"string"},"repriced":{"items":{"type":"string"},"title":"Repriced","type":"array"},"source":{"default":"database","enum":["database","config"],"title":"Source","type":"string"}},"required":["provider","added"],"title":"HostedCatalogProviderRefreshPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/catalog-update":{"post":{"description":"Apply the reviewed prices, then refresh every provider's models, then the cleanup if asked.\n\nThe price step comes first, so the model step prices new models at the\nprices it applied, and a model held for want of a price is served once it\nhas one. Without a ``digest`` no price changes. A ``digest`` that no longer\nnames the pending prices answers 409 and changes nothing. ``cleanup``\nremoves the prices of models no provider serves, with their history.","operationId":"hosted-providers-update_catalog","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedCatalogUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedCatalogUpdatePublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Catalog","tags":["hosted-providers"]}}}}
```

## Preview Catalog Update

> What updating the catalog would do: the models.dev price changes, then what each provider newly lists.\
> \
> A POST, because it holds the fetched prices as pending for the apply to\
> name by their \`\`digest\`\`; nothing else is written. It dials every enabled\
> provider. A price fetch that fails is reported in \`\`price\_error\`\` and the\
> model lists are still previewed. With \`\`cleanup\`\`, it also previews which\
> prices of models no provider serves would be removed.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedCatalogUpdatePreviewRequest":{"description":"What to preview: the update alone, or with the cleanup.","properties":{"cleanup":{"default":false,"description":"Also preview removing the prices of models no hosted provider serves.","title":"Cleanup","type":"boolean"}},"title":"HostedCatalogUpdatePreviewRequest","type":"object"},"HostedCatalogUpdatePreviewPublic":{"description":"What a catalog update would do: its price changes, then what each provider newly lists.\n\n``prices`` is ``None`` when models.dev changed nothing, or when it could not\nbe fetched, which ``price_error`` then says. ``catalog`` carries the cleanup\nthe preview was asked for, and no removals otherwise.","properties":{"catalog":{"$ref":"#/components/schemas/HostedCatalogRefreshPublic"},"price_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Error"},"prices":{"anyOf":[{"$ref":"#/components/schemas/HostedCatalogPriceUpdatePublic"},{"type":"null"}]}},"required":["prices","catalog"],"title":"HostedCatalogUpdatePreviewPublic","type":"object"},"HostedCatalogRefreshPublic":{"description":"What a catalog sweep did, or would do.\n\n``removed`` is a sample and ``removed_count`` the total, so a deployment\nthat has never been swept does not answer one button press with a few\nthousand strings. ``orphaned_rosters`` names the providers that still have\na model list but nothing defining them, such as a removed ``config.yml``\nentry: nothing serves them, and an apply deletes their model lists.","properties":{"kept":{"items":{"$ref":"#/components/schemas/HostedCatalogKeptGroupPublic"},"title":"Kept","type":"array"},"orphaned_rosters":{"items":{"type":"string"},"title":"Orphaned Rosters","type":"array"},"providers":{"items":{"$ref":"#/components/schemas/HostedCatalogProviderRefreshPublic"},"title":"Providers","type":"array"},"removed":{"items":{"type":"string"},"title":"Removed","type":"array"},"removed_count":{"title":"Removed Count","type":"integer"},"removed_override_rows":{"title":"Removed Override Rows","type":"integer"},"removed_price_rows":{"title":"Removed Price Rows","type":"integer"}},"required":["providers","removed","removed_count","removed_price_rows","removed_override_rows","kept"],"title":"HostedCatalogRefreshPublic","type":"object"},"HostedCatalogKeptGroupPublic":{"description":"Priced models a sweep left in place, grouped by the reason it did.","properties":{"count":{"title":"Count","type":"integer"},"models":{"items":{"type":"string"},"title":"Models","type":"array"},"reason":{"title":"Reason","type":"string"}},"required":["reason","count","models"],"title":"HostedCatalogKeptGroupPublic","type":"object"},"HostedCatalogProviderRefreshPublic":{"description":"What one hosted provider newly lists, as a catalog sweep found it.\n\nPer provider rather than one total, because a dial that failed is a fact\nabout that provider, and the operator has to be told which one went quiet\nrather than read a smaller number than they expected.","properties":{"added":{"items":{"type":"string"},"title":"Added","type":"array"},"credential_unreadable":{"default":false,"title":"Credential Unreadable","type":"boolean"},"discovery_unsupported":{"default":false,"title":"Discovery Unsupported","type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"provider":{"title":"Provider","type":"string"},"repriced":{"items":{"type":"string"},"title":"Repriced","type":"array"},"source":{"default":"database","enum":["database","config"],"title":"Source","type":"string"}},"required":["provider","added"],"title":"HostedCatalogProviderRefreshPublic","type":"object"},"HostedCatalogPriceUpdatePublic":{"description":"The models.dev price changes a catalog update would apply.\n\n``changes`` is a sample and the counts are the totals. ``digest`` names the\nfetched prices, so applying them applies what was reviewed. ``needs_review``\nis why the scheduled update would hold them for a person under ``auto``.","properties":{"added_count":{"title":"Added Count","type":"integer"},"changed_count":{"title":"Changed Count","type":"integer"},"changes":{"items":{"$ref":"#/components/schemas/HostedCatalogPriceChangePublic"},"title":"Changes","type":"array"},"changes_truncated":{"title":"Changes Truncated","type":"boolean"},"digest":{"title":"Digest","type":"string"},"fetched_at":{"format":"date-time","title":"Fetched At","type":"string"},"needs_review":{"title":"Needs Review","type":"boolean"},"removed_count":{"title":"Removed Count","type":"integer"},"review_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Reason"}},"required":["fetched_at","added_count","changed_count","removed_count","changes","changes_truncated","digest","needs_review"],"title":"HostedCatalogPriceUpdatePublic","type":"object"},"HostedCatalogPriceChangePublic":{"description":"One model whose models.dev price a catalog update adds, changes or removes.","properties":{"change":{"enum":["added","changed","removed"],"title":"Change","type":"string"},"model_key":{"title":"Model Key","type":"string"}},"required":["model_key","change"],"title":"HostedCatalogPriceChangePublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/catalog-update/preview":{"post":{"description":"What updating the catalog would do: the models.dev price changes, then what each provider newly lists.\n\nA POST, because it holds the fetched prices as pending for the apply to\nname by their ``digest``; nothing else is written. It dials every enabled\nprovider. A price fetch that fails is reported in ``price_error`` and the\nmodel lists are still previewed. With ``cleanup``, it also previews which\nprices of models no provider serves would be removed.","operationId":"hosted-providers-preview_catalog_update","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedCatalogUpdatePreviewRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedCatalogUpdatePreviewPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Preview Catalog Update","tags":["hosted-providers"]}}}}
```

## Preview Deployment Provider Migration

> What moving the deployment providers the retired dashboard page stored onto hosted providers would do.\
> \
> The gateway moves them at startup, so this lists the ones it could not\
> move, each with the reason. Writes nothing and dials nothing.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"DeploymentProviderMigrationPublic":{"description":"The deployment providers the retired dashboard page stored, and how each moves onto a hosted provider.","properties":{"rows":{"items":{"$ref":"#/components/schemas/DeploymentProviderMigrationRowPublic"},"title":"Rows","type":"array"}},"required":["rows"],"title":"DeploymentProviderMigrationPublic","type":"object"},"DeploymentProviderMigrationRowPublic":{"description":"What moving one deployment provider onto a hosted provider does, or did.\n\n``instance`` is the deployment provider's name, which the hosted provider\nkeeps, so ``instance:model`` selectors keep working. ``outcome`` is\n``migrate`` in a preview and ``migrated`` once applied; ``conflict``,\n``unreadable``, ``invalid`` and ``failed`` leave the row where it is, with\n``detail`` saying why. ``source_removed`` says whether the deployment\nprovider was removed: it is kept while the hosted provider offers no model,\nand when it changed while the move ran. ``notes`` are what an operator\nshould check afterwards.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"instance":{"title":"Instance","type":"string"},"models":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Models"},"notes":{"items":{"type":"string"},"title":"Notes","type":"array"},"outcome":{"enum":["migrate","migrated","conflict","unreadable","invalid","failed"],"title":"Outcome","type":"string"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"source_removed":{"default":false,"title":"Source Removed","type":"boolean"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"The deployment provider's version, which an apply names to move this row as shown.","title":"Updated At"}},"required":["instance","outcome"],"title":"DeploymentProviderMigrationRowPublic","type":"object"}}},"paths":{"/api/v1/hosted-providers/migration":{"get":{"description":"What moving the deployment providers the retired dashboard page stored onto hosted providers would do.\n\nThe gateway moves them at startup, so this lists the ones it could not\nmove, each with the reason. Writes nothing and dials nothing.","operationId":"hosted-providers-preview_deployment_provider_migration","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentProviderMigrationPublic"}}},"description":"Successful Response"}},"summary":"Preview Deployment Provider Migration","tags":["hosted-providers"]}}}}
```

## Apply Deployment Provider Migration

> Move the deployment providers the retired dashboard page stored onto hosted providers of the same name.\
> \
> Each is created as a hosted provider that serves models not on its roster,\
> offering what it lists as a create does, and the deployment provider is\
> removed. One that cannot move is reported and left in place. \`\`rows\`\`\
> limits the move to the rows of the preview the caller reviewed, at the\
> versions it was shown them.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"DeploymentProviderMigrationRequest":{"description":"Which deployment providers to move: the ones the caller reviewed, as reviewed.\n\n``rows`` names the rows of the preview the caller saw, each with the\n``updated_at`` it was shown at. A row the preview did not show, or one\nchanged since, is left where it is, so nothing moves that nobody reviewed.\nOmitted, every row that can move does.","properties":{"rows":{"anyOf":[{"items":{"$ref":"#/components/schemas/DeploymentProviderMigrationReviewedRow"},"type":"array"},{"type":"null"}],"title":"Rows"}},"title":"DeploymentProviderMigrationRequest","type":"object"},"DeploymentProviderMigrationReviewedRow":{"description":"One row of the preview the caller reviewed: its instance and the version it was shown at.","properties":{"instance":{"title":"Instance","type":"string"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["instance"],"title":"DeploymentProviderMigrationReviewedRow","type":"object"},"DeploymentProviderMigrationPublic":{"description":"The deployment providers the retired dashboard page stored, and how each moves onto a hosted provider.","properties":{"rows":{"items":{"$ref":"#/components/schemas/DeploymentProviderMigrationRowPublic"},"title":"Rows","type":"array"}},"required":["rows"],"title":"DeploymentProviderMigrationPublic","type":"object"},"DeploymentProviderMigrationRowPublic":{"description":"What moving one deployment provider onto a hosted provider does, or did.\n\n``instance`` is the deployment provider's name, which the hosted provider\nkeeps, so ``instance:model`` selectors keep working. ``outcome`` is\n``migrate`` in a preview and ``migrated`` once applied; ``conflict``,\n``unreadable``, ``invalid`` and ``failed`` leave the row where it is, with\n``detail`` saying why. ``source_removed`` says whether the deployment\nprovider was removed: it is kept while the hosted provider offers no model,\nand when it changed while the move ran. ``notes`` are what an operator\nshould check afterwards.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"instance":{"title":"Instance","type":"string"},"models":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Models"},"notes":{"items":{"type":"string"},"title":"Notes","type":"array"},"outcome":{"enum":["migrate","migrated","conflict","unreadable","invalid","failed"],"title":"Outcome","type":"string"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"source_removed":{"default":false,"title":"Source Removed","type":"boolean"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"description":"The deployment provider's version, which an apply names to move this row as shown.","title":"Updated At"}},"required":["instance","outcome"],"title":"DeploymentProviderMigrationRowPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/migration":{"post":{"description":"Move the deployment providers the retired dashboard page stored onto hosted providers of the same name.\n\nEach is created as a hosted provider that serves models not on its roster,\noffering what it lists as a create does, and the deployment provider is\nremoved. One that cannot move is reported and left in place. ``rows``\nlimits the move to the rows of the preview the caller reviewed, at the\nversions it was shown them.","operationId":"hosted-providers-apply_deployment_provider_migration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentProviderMigrationRequest","default":{}}}}},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentProviderMigrationPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Apply Deployment Provider Migration","tags":["hosted-providers"]}}}}
```

## Discard Deployment Provider

> Remove a deployment provider the move could not take, which serves nothing.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/migration/{instance}":{"delete":{"description":"Remove a deployment provider the move could not take, which serves nothing.","operationId":"hosted-providers-discard_deployment_provider","parameters":[{"in":"path","name":"instance","required":true,"schema":{"title":"Instance","type":"string"}}],"responses":{"204":{"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Discard Deployment Provider","tags":["hosted-providers"]}}}}
```

## Reencrypt Hosted Providers

> Re-encrypt every stored key and set of client arguments with the primary \`\`OTARI\_SECRET\_KEY\`\`.\
> \
> Run after rotating the key, alongside \`\`POST /api/v1/search-tools/reencrypt\`\`.\
> A provider no configured key reads is left as it is, to be re-entered.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedProvidersReencryptPublic":{"description":"What re-encrypting the stored hosted provider credentials did.\n\n``unreadable`` counts providers no configured key reads, left as they were\nto be re-entered; ``skipped`` those edited while the rotation ran, already\nwritten under the primary key.","properties":{"reencrypted":{"title":"Reencrypted","type":"integer"},"skipped":{"title":"Skipped","type":"integer"},"unreadable":{"title":"Unreadable","type":"integer"}},"required":["reencrypted","unreadable","skipped"],"title":"HostedProvidersReencryptPublic","type":"object"}}},"paths":{"/api/v1/hosted-providers/reencrypt":{"post":{"description":"Re-encrypt every stored key and set of client arguments with the primary ``OTARI_SECRET_KEY``.\n\nRun after rotating the key, alongside ``POST /api/v1/search-tools/reencrypt``.\nA provider no configured key reads is left as it is, to be re-entered.","operationId":"hosted-providers-reencrypt_hosted_providers","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProvidersReencryptPublic"}}},"description":"Successful Response"}},"summary":"Reencrypt Hosted Providers","tags":["hosted-providers"]}}}}
```

## Test Hosted Provider

> Ask a provider what it lists on the given credentials, storing nothing.\
> \
> Backs the form's "Test connection". With \`\`name\`\` naming a stored provider,\
> a field left out is read from it, so an edit can be tested without typing\
> the key again; pointing the stored key at another origin still needs it.\
> Failure comes back in the body.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedProviderTestRequest":{"description":"Credentials to try against a provider without storing them.\n\nThe fields the create body takes. ``name`` naming an existing stored\nprovider lets a field left out fall back to what it stores, so an edit form\ncan test without re-entering the key; pointing the stored key at another\norigin still needs the key typed again, as an update does.","properties":{"api_base":{"anyOf":[{"maxLength":1024,"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key":{"anyOf":[{"maxLength":1024,"minLength":1,"type":"string"},{"type":"null"}],"title":"Api Key"},"client_args":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Client Args"},"name":{"anyOf":[{"maxLength":64,"minLength":1,"type":"string"},{"type":"null"}],"title":"Name"},"provider":{"maxLength":64,"minLength":1,"title":"Provider","type":"string"}},"required":["provider"],"title":"HostedProviderTestRequest","type":"object"},"HostedProviderTestPublic":{"description":"What a connection test found: whether the provider answered, and how many models it lists.\n\n``discovery_unsupported`` is set for a provider with no model listing,\nwhich a test cannot reach that way although it may still serve.","properties":{"discovery_unsupported":{"default":false,"title":"Discovery Unsupported","type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"model_count":{"default":0,"title":"Model Count","type":"integer"},"ok":{"title":"Ok","type":"boolean"}},"required":["ok"],"title":"HostedProviderTestPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/test":{"post":{"description":"Ask a provider what it lists on the given credentials, storing nothing.\n\nBacks the form's \"Test connection\". With ``name`` naming a stored provider,\na field left out is read from it, so an edit can be tested without typing\nthe key again; pointing the stored key at another origin still needs it.\nFailure comes back in the body.","operationId":"hosted-providers-test_hosted_provider","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProviderTestRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProviderTestPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Test Hosted Provider","tags":["hosted-providers"]}}}}
```

## Update Hosted Provider

> Rotate the key, repoint the base, or turn a provider off.\
> \
> A provider defined in configuration answers 409: it is changed there.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedProviderUpdateRequest":{"description":"Rotate the key, repoint the base, or turn a provider off.\n\n``provider`` and ``name`` are immutable: changing either is a delete plus a\ncreate, because the key belongs to the provider it was issued by and\nrequests address the name. Omitting ``api_key`` leaves the stored\ncredential untouched, so a toggle never re-sends a secret the caller does\nnot have; an explicit null removes it, where the provider can be served\nwithout one. ``client_args`` omitted is left alone; an explicit\nnull clears it, and an entry echoed back as the mask keeps the stored value.\n``restricted_to_organization_id`` reads the same way: omitted keeps the\nrestriction, null lifts it, and so does ``supported_countries``, where an\nempty list closes the provider to every country.","properties":{"api_base":{"anyOf":[{"maxLength":1024,"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key":{"anyOf":[{"maxLength":1024,"minLength":1,"type":"string"},{"type":"null"}],"title":"Api Key"},"client_args":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Client Args"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"restricted_to_organization_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Restricted To Organization Id"},"serves_unlisted_models":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Serves Unlisted Models"},"supported_countries":{"anyOf":[{"items":{"type":"string"},"maxItems":300,"type":"array"},{"type":"null"}],"title":"Supported Countries"}},"title":"HostedProviderUpdateRequest","type":"object"},"HostedProviderPublic":{"description":"One hosted provider, known by its name, its implementation and the tail of its key.\n\nNo shape in this section carries key material: a stored credential is\nknown by its last four characters and nothing else, ``client_args`` goes\nout with credential-shaped values masked, and ``api_base`` with the\npassword in its userinfo and its query values masked.\n\n``name`` is what requests address and every route takes; ``provider`` is\nthe any-llm implementation. ``source`` says where the provider is defined.\nA ``config`` provider comes from the ``hosted_providers:`` block, has no\nrow and so no ``id`` or timestamps, and is changed by editing the\nconfiguration rather than through this API; its models are administered as\na stored provider's are. ``shadows_config`` marks a stored provider that\nwins over a configuration entry of the same name, which serves again once\nthe row is deleted. ``has_api_key`` is false for one stored without a key,\nand ``decryptable`` false for one whose key or extras no configured\n``OTARI_SECRET_KEY`` reads, which serves nothing until re-entered.\n``serves_unlisted_models`` is true for one that serves a model its roster\ndoes not list. ``supported_countries`` is null for one that serves every\ncountry.","properties":{"api_base":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Base"},"api_key_last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key Last4"},"client_args":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Client Args"},"created_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Created At"},"decryptable":{"default":true,"title":"Decryptable","type":"boolean"},"enabled":{"title":"Enabled","type":"boolean"},"has_api_key":{"default":true,"title":"Has Api Key","type":"boolean"},"id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Id"},"name":{"title":"Name","type":"string"},"provider":{"title":"Provider","type":"string"},"restricted_to_organization_id":{"anyOf":[{"format":"uuid","type":"string"},{"type":"null"}],"title":"Restricted To Organization Id"},"serves_unlisted_models":{"default":false,"title":"Serves Unlisted Models","type":"boolean"},"shadows_config":{"default":false,"title":"Shadows Config","type":"boolean"},"source":{"default":"database","enum":["database","config"],"title":"Source","type":"string"},"supported_countries":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Supported Countries"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["name","provider","enabled"],"title":"HostedProviderPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}":{"patch":{"description":"Rotate the key, repoint the base, or turn a provider off.\n\nA provider defined in configuration answers 409: it is changed there.","operationId":"hosted-providers-update_hosted_provider","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProviderUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedProviderPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Hosted Provider","tags":["hosted-providers"]}}}}
```

## Delete Hosted Provider

> Remove a provider and its roster, leaving the runtime with no hosted path for it.\
> \
> A provider defined in configuration answers 409. A stored provider that\
> overrides a configured one keeps the roster, which the configured one\
> serves again.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"Message":{"description":"A human-readable acknowledgment for an operation with nothing to return.","properties":{"message":{"description":"What happened.","title":"Message","type":"string"}},"required":["message"],"title":"Message","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}":{"delete":{"description":"Remove a provider and its roster, leaving the runtime with no hosted path for it.\n\nA provider defined in configuration answers 409. A stored provider that\noverrides a configured one keeps the roster, which the configured one\nserves again.","operationId":"hosted-providers-delete_hosted_provider","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Delete Hosted Provider","tags":["hosted-providers"]}}}}
```

## List Available Models

> Ask the provider what it serves on its credential.\
> \
> Dials the upstream on every call rather than caching: the caller is the\
> admin form's model picker, opened rarely and entitled to a current answer.\
> Failure comes back in the body, so the picker can fall back to a text box.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedAvailableModelsPublic":{"description":"What the provider says it serves on the stored credential.\n\nFailure is a field rather than a status: an unreachable upstream, or a\nprovider with no model listing, is an answer about the provider rather than\nabout this request, and the form still has to render a plain text box when\nthe list cannot be fetched.","properties":{"discovery_unsupported":{"default":false,"title":"Discovery Unsupported","type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"models":{"items":{"type":"string"},"title":"Models","type":"array"},"provider":{"title":"Provider","type":"string"}},"required":["provider"],"title":"HostedAvailableModelsPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}/available-models":{"get":{"description":"Ask the provider what it serves on its credential.\n\nDials the upstream on every call rather than caching: the caller is the\nadmin form's model picker, opened rarely and entitled to a current answer.\nFailure comes back in the body, so the picker can fall back to a text box.","operationId":"hosted-providers-list_available_models","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedAvailableModelsPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Available Models","tags":["hosted-providers"]}}}}
```

## List Hosted Models

> List the models offered on one provider, with the price each currently serves at.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedModelsPublic":{"description":"A page of one hosted provider's offered models, and how many there are in total.","properties":{"count":{"title":"Count","type":"integer"},"data":{"items":{"$ref":"#/components/schemas/HostedModelPublic"},"title":"Data","type":"array"}},"required":["data","count"],"title":"HostedModelsPublic","type":"object"},"HostedModelPublic":{"description":"One offered model, with the price the deployment currently serves it at.\n\n``price_source`` says which rung of the deployment's ladder answered:\n``deployment`` for a rate an operator set, ``defaults`` for the\ncommunity-maintained rate, whether this surface stored it or the fallback\nsupplies it, and None when nothing prices the model yet. ``unit`` says what\nthe rates are per, so a per-request rate is not read as a per-token one.\n``enabled`` is the serving switch; a model offered without a discoverable\nprice starts off.","properties":{"cache_read_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cache Read Price Per Million"},"cache_write_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cache Write Price Per Million"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"enabled":{"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"input_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Input Price Per Million"},"model":{"title":"Model","type":"string"},"output_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Output Price Per Million"},"price_source":{"anyOf":[{"enum":["organization","deployment","defaults"],"type":"string"},{"type":"null"}],"title":"Price Source"},"provider":{"title":"Provider","type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["id","provider","model","enabled","created_at"],"title":"HostedModelPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}/models":{"get":{"description":"List the models offered on one provider, with the price each currently serves at.","operationId":"hosted-providers-list_hosted_models","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}},{"in":"query","name":"skip","required":false,"schema":{"default":0,"minimum":0,"title":"Skip","type":"integer"}},{"in":"query","name":"limit","required":false,"schema":{"default":500,"maximum":1000,"minimum":1,"title":"Limit","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedModelsPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"List Hosted Models","tags":["hosted-providers"]}}}}
```

## Add Hosted Model

> Offer a model on a provider, optionally at a custom price.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedModelCreateRequest":{"description":"Offer a model on a hosted provider, optionally at a custom price.\n\nOmitting the rates means the deployment serves the model at whatever the\nprice ladder already answers: a stored deployment price, else the community\ndefault, which the offer stores as the deployment's own rate. ``unit`` is\nwhat the rates are per, as on ``POST /pricing``: a request or image rate is\nstill stored per million.","properties":{"cache_read_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Cache Read Price Per Million"},"cache_write_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Cache Write Price Per Million"},"input_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Input Price Per Million"},"model":{"maxLength":255,"minLength":1,"title":"Model","type":"string"},"output_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Output Price Per Million"},"unit":{"anyOf":[{"enum":["tokens","requests","images"],"type":"string"},{"type":"null"}],"title":"Unit"}},"required":["model"],"title":"HostedModelCreateRequest","type":"object"},"HostedModelPublic":{"description":"One offered model, with the price the deployment currently serves it at.\n\n``price_source`` says which rung of the deployment's ladder answered:\n``deployment`` for a rate an operator set, ``defaults`` for the\ncommunity-maintained rate, whether this surface stored it or the fallback\nsupplies it, and None when nothing prices the model yet. ``unit`` says what\nthe rates are per, so a per-request rate is not read as a per-token one.\n``enabled`` is the serving switch; a model offered without a discoverable\nprice starts off.","properties":{"cache_read_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cache Read Price Per Million"},"cache_write_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cache Write Price Per Million"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"enabled":{"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"input_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Input Price Per Million"},"model":{"title":"Model","type":"string"},"output_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Output Price Per Million"},"price_source":{"anyOf":[{"enum":["organization","deployment","defaults"],"type":"string"},{"type":"null"}],"title":"Price Source"},"provider":{"title":"Provider","type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["id","provider","model","enabled","created_at"],"title":"HostedModelPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}/models":{"post":{"description":"Offer a model on a provider, optionally at a custom price.","operationId":"hosted-providers-add_hosted_model","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedModelCreateRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedModelPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Add Hosted Model","tags":["hosted-providers"]}}}}
```

## Refresh Hosted Models

> Ask the provider again and offer whatever is newly listed.\
> \
> Nothing already offered is removed, even when the provider no longer lists\
> it. New models follow the offer rule, seeded with the community default\
> price and off when nothing prices them. A seeded price nobody changed moves\
> with the default. An offered model whose stored price has since been\
> deleted is seeded again, or switched off where the default does not price\
> it, even one an operator switched on: a refresh never switches a model on.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedModelsRefreshPublic":{"description":"What a refresh did: the models newly offered, the seeded rates it moved, and the list's new size.\n\nFailure is a field rather than a status, for the reason\n``HostedAvailableModelsPublic`` gives: the list is still standing, and the\npanel renders the reason beside it.","properties":{"added":{"items":{"type":"string"},"title":"Added","type":"array"},"count":{"title":"Count","type":"integer"},"discovery_unsupported":{"default":false,"title":"Discovery Unsupported","type":"boolean"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"repriced":{"items":{"type":"string"},"title":"Repriced","type":"array"}},"required":["added","repriced","count"],"title":"HostedModelsRefreshPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}/models/refresh":{"post":{"description":"Ask the provider again and offer whatever is newly listed.\n\nNothing already offered is removed, even when the provider no longer lists\nit. New models follow the offer rule, seeded with the community default\nprice and off when nothing prices them. A seeded price nobody changed moves\nwith the default. An offered model whose stored price has since been\ndeleted is seeded again, or switched off where the default does not price\nit, even one an operator switched on: a refresh never switches a model on.","operationId":"hosted-providers-refresh_hosted_models","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedModelsRefreshPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Refresh Hosted Models","tags":["hosted-providers"]}}}}
```

## Update Hosted Model

> Reprice one offered model, toggle whether it is served, or both.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"HostedModelUpdateRequest":{"description":"Reprice an offered model, toggle whether it is served, or both.\n\nA price is the input/output pair or nothing: sending the pair writes a new\npricing version, and the cache rates then follow ``POST /pricing``'s own\nsemantics (a field the caller omits inherits the most recent stored value,\nan explicit null clears it). An omitted ``unit`` carries the stored one\nforward. ``enabled`` travels alone freely, so a toggle never re-sends rates\nit did not change.","properties":{"cache_read_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Cache Read Price Per Million"},"cache_write_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Cache Write Price Per Million"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"input_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Input Price Per Million"},"output_price_per_million":{"anyOf":[{"minimum":0,"type":"number"},{"type":"null"}],"title":"Output Price Per Million"},"unit":{"anyOf":[{"enum":["tokens","requests","images"],"type":"string"},{"type":"null"}],"title":"Unit"}},"title":"HostedModelUpdateRequest","type":"object"},"HostedModelPublic":{"description":"One offered model, with the price the deployment currently serves it at.\n\n``price_source`` says which rung of the deployment's ladder answered:\n``deployment`` for a rate an operator set, ``defaults`` for the\ncommunity-maintained rate, whether this surface stored it or the fallback\nsupplies it, and None when nothing prices the model yet. ``unit`` says what\nthe rates are per, so a per-request rate is not read as a per-token one.\n``enabled`` is the serving switch; a model offered without a discoverable\nprice starts off.","properties":{"cache_read_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cache Read Price Per Million"},"cache_write_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cache Write Price Per Million"},"created_at":{"format":"date-time","title":"Created At","type":"string"},"enabled":{"title":"Enabled","type":"boolean"},"id":{"format":"uuid","title":"Id","type":"string"},"input_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Input Price Per Million"},"model":{"title":"Model","type":"string"},"output_price_per_million":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Output Price Per Million"},"price_source":{"anyOf":[{"enum":["organization","deployment","defaults"],"type":"string"},{"type":"null"}],"title":"Price Source"},"provider":{"title":"Provider","type":"string"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"updated_at":{"anyOf":[{"format":"date-time","type":"string"},{"type":"null"}],"title":"Updated At"}},"required":["id","provider","model","enabled","created_at"],"title":"HostedModelPublic","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}/models/{model_id}":{"patch":{"description":"Reprice one offered model, toggle whether it is served, or both.","operationId":"hosted-providers-update_hosted_model","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}},{"in":"path","name":"model_id","required":true,"schema":{"format":"uuid","title":"Model Id","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedModelUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostedModelPublic"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Update Hosted Model","tags":["hosted-providers"]}}}}
```

## Remove Hosted Model

> Stop offering a model on a provider. Its pricing history stays.

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"Message":{"description":"A human-readable acknowledgment for an operation with nothing to return.","properties":{"message":{"description":"What happened.","title":"Message","type":"string"}},"required":["message"],"title":"Message","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/hosted-providers/{provider}/models/{model_id}":{"delete":{"description":"Stop offering a model on a provider. Its pricing history stays.","operationId":"hosted-providers-remove_hosted_model","parameters":[{"in":"path","name":"provider","required":true,"schema":{"title":"Provider","type":"string"}},{"in":"path","name":"model_id","required":true,"schema":{"format":"uuid","title":"Model Id","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Remove Hosted Model","tags":["hosted-providers"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/api-reference/hosted-providers.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
