Create Model Catalog Entry
Endpoint
POST /apiops/projects/{projectName}/llm-models/{modelName}/
Authentication
Requires a Personal API Access Token.
Header
Authorization: Bearer YOUR_TOKEN
Request
Headers
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer {token} | Yes |
| Content-Type | application/json | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| projectName | string | Yes | Project name |
| modelName | string | Yes | Model catalog entry name (must match the name in the body, case-insensitive) |
Query Parameters
None
Request Body
Full JSON Body Example
{
"name": "openai/gpt-5-mini",
"providerDefinitionId": "6640aa12c2e4b8000000ef01",
"providerCode": "openai",
"modelId": "gpt-5-mini",
"displayName": "GPT-5 Mini",
"contextWindow": 128000,
"maxOutputTokens": 16384,
"pricePerMillionInput": 0.25,
"pricePerMillionOutput": 2.00,
"pricePerMillionCached": 0.025,
"pricePerMillionCacheWrite": null,
"cacheWritePricesByTtl": [],
"contextTierPrices": [],
"serviceTierPrices": [],
"pricePerImage": null,
"pricePerAudioMinute": null,
"pricePerCharacterTts": null,
"capabilities": ["function_calling", "structured_output"],
"modality": "TEXT",
"isDeprecated": false,
"deprecatedAt": null,
"sunsetAt": null,
"builtIn": false
}
Request Body Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | Yes | - | Catalog name in \{providerCode\}/\{modelId\} form; must match the modelName path parameter |
| providerDefinitionId | string | No | - | Reference to the owning LLM provider definition |
| providerCode | string | Yes | - | Provider code (e.g. openai, anthropic). Required for custom entries |
| modelId | string | Yes | - | Provider-facing model identifier (e.g. gpt-5-mini). Required for custom entries |
| displayName | string | No | - | Human-readable label |
| contextWindow | integer | No | - | Context window size in tokens |
| maxOutputTokens | integer | No | - | Maximum output tokens; omit to use the provider default |
| pricePerMillionInput | number | No | - | USD per 1M input tokens |
| pricePerMillionOutput | number | No | - | USD per 1M output tokens |
| pricePerMillionCached | number | No | - | USD per 1M cached input tokens |
| pricePerMillionCacheWrite | number | No | - | USD per 1M cache-write tokens (flat rate). A matching row in cacheWritePricesByTtl overrides this for its TTL |
| cacheWritePricesByTtl | array | No | - | Cache-write price per TTL — see Cache-Write Price (cacheWritePricesByTtl) Fields |
| contextTierPrices | array | No | - | Long-context price tiers — see Context-Tier Price (contextTierPrices) Fields |
| serviceTierPrices | array | No | - | Provider service-tier price multipliers — see Service-Tier Price (serviceTierPrices) Fields |
| pricePerImage | number | No | - | USD per generated image |
| pricePerAudioMinute | number | No | - | USD per minute of audio (STT) |
| pricePerCharacterTts | number | No | - | USD per input character (TTS) |
| capabilities | array[string] | No | - | Capability tags. Normalized on every save — see Capabilities Normalization |
| modality | string | No | - | Model modality: TEXT, VISION, AUDIO, EMBEDDING, RERANK, MULTIMODAL, IMAGE |
| isDeprecated | boolean | No | false | Whether the model is deprecated |
| deprecatedAt | string | No | - | Deprecation announcement time (ISO-8601) |
| sunsetAt | string | No | - | Planned removal time (ISO-8601) |
| builtIn | boolean | No | false | Leave false for custom entries. See the built-in note below. |
Cache-Write Price (cacheWritePricesByTtl) Fields
One row per cache TTL the provider bills separately for (e.g. Anthropic prices a 5-minute cache
write at 1.25× input and a 1-hour cache write at 2× input). A row overrides
pricePerMillionCacheWrite for usage that reports that TTL.
| Field | Type | Required | Description |
|---|---|---|---|
| ttl | string | Yes | Short TTL label as the provider reports it — typically "5m" or "1h" |
| pricePerMillion | number | Yes | USD per 1M cache-write tokens at this TTL |
"cacheWritePricesByTtl": [
{ "ttl": "5m", "pricePerMillion": 3.75 },
{ "ttl": "1h", "pricePerMillion": 6.00 }
]
Context-Tier Price (contextTierPrices) Fields
Long-context pricing: above aboveTokens prompt tokens, the whole request is priced at
inputFactor × the input price and outputFactor × the output price. When several tiers match,
the one with the highest aboveTokens threshold wins.
| Field | Type | Required | Description |
|---|---|---|---|
| aboveTokens | integer | Yes | Prompt-token threshold (input + cached + cache-write). The tier applies strictly above this value |
| inputFactor | number | Yes | Multiplier applied to the input price for the whole request |
| outputFactor | number | Yes | Multiplier applied to the output price for the whole request |
"contextTierPrices": [
{ "aboveTokens": 200000, "inputFactor": 2.0, "outputFactor": 1.5 }
]
Service-Tier Price (serviceTierPrices) Fields
The gateway applies the row whose tier matches the service_tier value the provider echoed in
the response usage (e.g. OpenAI's flex / priority, or a request routed through a Batch API).
| Field | Type | Required | Description |
|---|---|---|---|
| tier | string | Yes | Service tier enum constant: BATCH, PRIORITY, FLEX or CUSTOM |
| name | string | No | Provider-reported tier value to match, used only when tier is CUSTOM (e.g. "scale"); ignored for the fixed tiers |
| inputFactor | number | Yes | Multiplier applied to the input price when the provider echoes this tier |
| outputFactor | number | Yes | Multiplier applied to the output price when the provider echoes this tier |
"serviceTierPrices": [
{ "tier": "BATCH", "name": null, "inputFactor": 0.5, "outputFactor": 0.5 },
{ "tier": "PRIORITY", "name": null, "inputFactor": 2.0, "outputFactor": 2.0 }
]
Capabilities Normalization
capabilities is normalized on every save — create, update, and when a model is registered from
an LLM provider's supportedModels. The stored value never contains a legacy spelling.
Known keys (the values capabilities normalizes to): function_calling, vision,
prompt_caching, reasoning, structured_output, streaming, web_search.
Aliases collapsed to a known key: tools, tool_use, tool_calling, function-calling →
function_calling; image_input, multimodal_input → vision; caching → prompt_caching;
json_mode, json_schema, response_schema → structured_output; search → web_search.
Dropped as modality noise (already implied by the modality field, so it carries no extra
information): chat, completion, text, embedding, embeddings, code.
Unknown values are lower-cased, converted to snake_case, and kept as a custom tag.
For example, a request body sending "capabilities": ["chat", "tool_use", "json_mode"] is stored
as ["function_calling", "structured_output"] — chat is dropped and both remaining values
collapse to their canonical key.
Notes
- Upsert: If an entry with the same
namealready exists in the project, it is updated in place (the existing id is reused). Otherwise a new entry is created. - Name match:
modelNamein the path must equal thenamein the body (case-insensitive), or a400is returned. providerCode+modelIdrequired: For custom entries (builtIn=false) both are mandatory.id/projectIdare server-managed: AnyidorprojectIdin the body is ignored — the entry is always scoped to the path project.- Built-in entries (
builtIn=true) are rejected: APIops is project-scoped, while a built-in catalog row is installation-wide, so writing one from a project context is refused with a400 Bad Request("Built-in model catalog entries are read-only through APIops."). Built-in models ARE editable in the admin UI — see Model Catalog. A body that declaresbuiltIn: trueis refused the same way, so a caller cannot reach the built-in path by claiming the flag.
Response
Success Response (200 OK)
{
"status": "SUCCESS",
"deploymentResult": {
"success": true
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
| status | string | Response status: SUCCESS or FAILURE |
| deploymentResult | object | Result of the save operation |
| deploymentResult.success | boolean | Whether the operation completed successfully |
Error Response (400 Bad Request)
{
"status": "FAILURE",
"resultMessage": "Model catalog entry name in path (openai/gpt-5-mini) does not match name in body (openai/gpt-5)!"
}
or
{
"status": "FAILURE",
"resultMessage": "providerCode and modelId are required"
}
Error Response (401 Unauthorized)
{
"status": "FAILURE",
"resultMessage": "Token is not valid!"
}
cURL Example
curl -X POST \
"https://demo.apinizer.com/apiops/projects/MyProject/llm-models/openai%2Fgpt-5-mini/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "openai/gpt-5-mini",
"providerCode": "openai",
"modelId": "gpt-5-mini",
"displayName": "GPT-5 Mini",
"contextWindow": 128000,
"maxOutputTokens": 16384,
"pricePerMillionInput": 0.25,
"pricePerMillionOutput": 2.00,
"pricePerMillionCached": 0.025,
"capabilities": ["chat", "function_calling", "json_mode"],
"modality": "TEXT",
"builtIn": false
}'
Notes and Warnings
- Upsert behaviour:
POST /{modelName}/both creates and updates. To be explicit about updating an existing entry, use Update Model Catalog Entry (PUT), which requires the entry to already exist. - Built-in models are edited in the UI, not here: A body declaring
builtIn: true, or a name that resolves to a built-in row, is refused. In the admin UI every field except the provider and the model ID is editable, and the edit survives version upgrades. - Name form: The catalog name uses the
{providerCode}/{modelId}format. URL-encode the/as%2Fin the path. - No secret fields: The catalog carries no
@SecretDatafields and no_classdiscriminator — nothing is masked, and there is no secret-preservation-on-blank behaviour to consider. - Capabilities are rewritten on save: whatever
capabilitiesvalues are sent, the stored value is the normalized form — legacy spellings and modality noise never round-trip unchanged.
Related Documentation
- Update Model Catalog Entry - Update an existing entry
- Get Model Catalog Entry - Get a single entry by name
- Model Catalog API - Resource overview
- Authentication Guide - How to obtain and use API tokens
- Error Handling - Error response formats