Master Keys
Provision projects, gateway API keys, and custom providers/models — and pull per-member usage and cost data — programmatically with org-scoped bearer tokens (Enterprise only)
Master Keys
Master keys are org-scoped bearer tokens that let you create projects, gateway API keys, IAM rules (both per key and per organization member), and your organization's custom providers and custom models programmatically — without going through the dashboard. They are intended for server-to-server provisioning (e.g. multi-tenant onboarding from your own backend).
They also expose usage and cost reporting, so you can pull per-member and per-model spend into your own dashboards, data warehouse, or chargeback process.
Master keys are available on the Enterprise plan only. Contact us at contact@llmgateway.io to enable them for your organization.
Security
- Master keys are stored as HMAC-SHA256 hashes in the database (using the
GATEWAY_API_KEY_HASH_SECRETsecret). The plain token is shown to you only once at creation time. - Each master key is scoped to a single organization and cannot access resources in other organizations.
- Deleting or deactivating a master key revokes all programmatic access immediately.
- All creates/deletes/status changes are recorded in your organization audit log.
Limits
- Maximum 10 active master keys per organization.
- Programmatic project and API-key creation enforces the same per-org and per-project limits as the dashboard flow.
Managing master keys
In the dashboard, go to Organization → Master Keys. From there you can:
- Create a new master key (the plain token is shown once — copy it immediately).
- View the masked token, status, creator, and last-used timestamp for each existing key.
- Activate / deactivate or delete keys.
Authentication
All programmatic endpoints live under /v1/master/* and require a master key in the Authorization header:
Authorization: Bearer llmgmk_...A request with a missing, invalid, inactive, or non-enterprise master key receives a 401 / 403 response.
Endpoints
List projects
GET /v1/master/projects
Returns all non-deleted projects in the master key's organization.
curl https://internal.llmgateway.io/v1/master/projects \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"projects": [
{
"id": "proj_...",
"name": "Customer ACME",
"organizationId": "org_...",
"cachingEnabled": false,
"cacheDurationSeconds": 60,
"mode": "hybrid",
"status": "active",
"createdAt": "...",
"updatedAt": "..."
}
]
}Create a project
POST /v1/master/projects
curl -X POST https://internal.llmgateway.io/v1/master/projects \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer ACME",
"cachingEnabled": false,
"mode": "hybrid"
}'Body parameters:
| Field | Type | Description |
|---|---|---|
name | string | Project name (1–255 chars) |
cachingEnabled | boolean (optional) | Default false |
cacheDurationSeconds | number (optional) | 10–31536000, default 60 |
mode | "api-keys" | "credits" | "hybrid" (optional) | Default "hybrid" |
Response (201): the created project.
Update a project
PATCH /v1/master/projects/{id}
Updates a project owned by the master key's organization. All body fields are optional; provide only the ones you want to change.
curl -X PATCH https://internal.llmgateway.io/v1/master/projects/proj_... \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer ACME (renamed)",
"cachingEnabled": true,
"status": "inactive"
}'Body parameters (all optional, at least one required):
| Field | Type | Description |
|---|---|---|
name | string | 1–255 chars |
cachingEnabled | boolean | |
cacheDurationSeconds | number | 10–31536000 |
mode | "api-keys" | "credits" | "hybrid" | |
status | "active" | "inactive" | Toggle the project without deleting |
Response (200): the updated project.
Delete a project
DELETE /v1/master/projects/{id}
Soft-deletes a project (sets status to "deleted"). Cascades to its API keys.
curl -X DELETE https://internal.llmgateway.io/v1/master/projects/proj_... \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{ "message": "Project deleted successfully" }List gateway API keys
GET /v1/master/keys
Returns the developer-created gateway API keys in the master key's organization, each with its configured limits, the usage consumed so far, and — when a windowed limit is set — the time the current period resets. Pass an optional projectId query parameter to scope the list to a single project.
curl "https://internal.llmgateway.io/v1/master/keys?projectId=proj_..." \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"apiKeys": [
{
"id": "ak_...",
"description": "Customer ACME — production key",
"status": "active",
"projectId": "proj_...",
"createdBy": "usr_...",
"maskedToken": "llmgtwy_...abcd",
"usageLimit": "100.00",
"usage": "42.13",
"periodUsageLimit": "10.00",
"periodUsageDurationValue": 1,
"periodUsageDurationUnit": "day",
"currentPeriodUsage": "3.50",
"currentPeriodStartedAt": "2025-01-15T00:00:00.000Z",
"currentPeriodResetAt": "2025-01-16T00:00:00.000Z",
"createdAt": "...",
"updatedAt": "..."
}
]
}Limit and usage fields:
| Field | Description |
|---|---|
usageLimit | Lifetime spend cap (null when uncapped) |
usage | Total spend accrued against usageLimit over the key's lifetime |
periodUsageLimit | Recurring per-window spend cap (null when no windowed limit is configured) |
periodUsageDurationValue | Length of the window, paired with periodUsageDurationUnit |
periodUsageDurationUnit | "hour" | "day" | "week" | "month" |
currentPeriodUsage | Spend accrued in the current window ("0" when unconfigured or the window lapsed) |
currentPeriodStartedAt | When the current window began (null when unconfigured or lapsed) |
currentPeriodResetAt | When the windowed limit resets (null when unconfigured or lapsed) |
The plain token is never returned by this endpoint — only a masked form for identification.
Get a gateway API key
GET /v1/master/keys/{id}
Returns a single gateway API key in the master key's organization, with the same limit, usage, and reset-time fields as the list endpoint.
curl https://internal.llmgateway.io/v1/master/keys/ak_... \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"apiKey": {
"id": "ak_...",
"description": "Customer ACME — production key",
"status": "active",
"projectId": "proj_...",
"createdBy": "usr_...",
"maskedToken": "llmgtwy_...abcd",
"usageLimit": "100.00",
"usage": "42.13",
"periodUsageLimit": "10.00",
"periodUsageDurationValue": 1,
"periodUsageDurationUnit": "day",
"currentPeriodUsage": "3.50",
"currentPeriodStartedAt": "2025-01-15T00:00:00.000Z",
"currentPeriodResetAt": "2025-01-16T00:00:00.000Z",
"createdAt": "...",
"updatedAt": "..."
}
}Create a gateway API key
POST /v1/master/keys
curl -X POST https://internal.llmgateway.io/v1/master/keys \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectId": "proj_...",
"description": "Customer ACME — production key"
}'Body parameters:
| Field | Type | Description |
|---|---|---|
projectId | string | Must belong to the master key's organization |
description | string | API key description (1–255 chars) |
usageLimit | string (optional) | Lifetime usage limit |
periodUsageLimit | string (optional) | Recurring period usage limit |
periodUsageDurationValue | number (optional) | Required if periodUsageLimit is set |
periodUsageDurationUnit | "hour" | "day" | "week" | "month" (optional) | Required if periodUsageLimit is set |
The created gateway API key's plain token is returned in the response only once. Persist it immediately on your side.
Response (201):
{
"apiKey": {
"id": "ak_...",
"token": "llmgtwy_...",
"description": "Customer ACME — production key",
"status": "active",
"projectId": "proj_...",
"createdBy": "usr_...",
"createdAt": "...",
"updatedAt": "..."
}
}Update a gateway API key
PATCH /v1/master/keys/{id}
Updates an API key in a project owned by the master key's organization. All body fields are optional; provide only the ones you want to change.
curl -X PATCH https://internal.llmgateway.io/v1/master/keys/ak_... \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "inactive",
"usageLimit": "100.00"
}'Body parameters (all optional, at least one required):
| Field | Type | Description |
|---|---|---|
description | string | 1–255 chars |
status | "active" | "inactive" | |
usageLimit | string | null | Lifetime usage limit (null to clear) |
periodUsageLimit | string | null | Recurring period limit (null to clear) |
periodUsageDurationValue | number | null | Required if periodUsageLimit is set |
periodUsageDurationUnit | "hour" | "day" | "week" | "month" | Required if periodUsageLimit is set |
Response (200): the updated API key, including its configured limits, consumed usage / currentPeriodUsage, and the currentPeriodResetAt window-reset time (same fields as the list endpoint). The plain token is not included — it is only returned at creation.
Two dashboard-only key features have no master API equivalent yet: expiration (TTL) and rolling a key's secret. To replace a key programmatically, create a new one and delete the old one once your clients have switched over.
Delete a gateway API key
DELETE /v1/master/keys/{id}
Soft-deletes the API key (sets status to "deleted"). Any in-flight requests using the key will be rejected immediately on next auth check.
curl -X DELETE https://internal.llmgateway.io/v1/master/keys/ak_... \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{ "message": "API key deleted successfully" }The auto-generated Lounge (playground) API key cannot be deleted via the master API.
Usage and cost reporting
GET /v1/master/usage
Returns usage and cost for the master key's organization as a flat list of rows, grouped by any combination of member, model, provider, project, and API key, and bucketed hourly, daily, or not at all. This is the endpoint to point an internal reporting tool, data warehouse, or chargeback job at — it is the only usage surface that authenticates with a token rather than a dashboard session.
Like every /v1/master/* endpoint, this requires an Enterprise plan. A
master key on any other plan receives a 403.
# Cost per member for a month
curl -G https://internal.llmgateway.io/v1/master/usage \
-H "Authorization: Bearer $MASTER_KEY" \
-d from=2026-07-01 -d to=2026-07-31 \
-d granularity=total -d groupBy=userQuery parameters:
| Param | Type | Default | Description |
|---|---|---|---|
from, to | YYYY-MM-DD | last 7 days | Inclusive window, interpreted as wall-clock days in timezone. Max 366 days |
timezone | IANA timezone | UTC | Timezone the day/hour buckets are labelled in |
granularity | hour | day | total | day | Time bucket. total collapses the window into one row per group. hour caps at 31 days |
groupBy | comma-separated: user, model, provider, project, apiKey | user,model | Dimensions to break down by. Pass an empty value for organization-wide totals |
projectId | string | — | Restrict to one project. 404 if it is not in this organization |
userId | string | — | Restrict to one member |
apiKeyId | string | — | Restrict to one gateway API key |
limit | 1–10000 | 1000 | Rows per page |
offset | ≥ 0 | 0 | Row offset for paging |
format | json | csv | json | csv returns a text/csv attachment with a fixed header |
Response (200):
{
"from": "2026-07-01",
"to": "2026-07-31",
"granularity": "day",
"groupBy": ["user", "model"],
"rows": [
{
"date": "2026-07-01",
"userId": "usr_...",
"userName": "Ada Lovelace",
"userEmail": "ada@example.com",
"projectId": null,
"projectName": null,
"apiKeyId": null,
"apiKeyName": null,
"model": "gpt-5.6",
"provider": "openai",
"requestCount": 128,
"errorCount": 2,
"inputTokens": 481203,
"outputTokens": 92844,
"totalTokens": 574047,
"cachedTokens": 120000,
"reasoningTokens": 18320,
"cost": 3.4127,
"inputCost": 2.1,
"outputCost": 1.3127,
"creditsRequestCount": 128,
"apiKeysRequestCount": 0,
"creditsCost": 3.4127,
"apiKeysCost": 0
}
],
"pagination": { "limit": 1000, "offset": 0, "hasMore": false }
}Every row carries the full column set. Dimension fields you did not group by are null, and date is null when granularity=total, so the row shape stays stable for a schema-driven consumer. creditsCost / apiKeysCost split the blended cost into credit-billed and BYOK traffic.
Per-member, per-model breakdown
Combine the two dimensions to get the cross-tab most reporting tools want — one row per member, model, and day:
curl -G https://internal.llmgateway.io/v1/master/usage \
-H "Authorization: Bearer $MASTER_KEY" \
-d from=2026-07-01 -d to=2026-07-31 \
-d granularity=day -d groupBy=user,model \
-d timezone=Europe/BerlinCSV export
curl -G https://internal.llmgateway.io/v1/master/usage \
-H "Authorization: Bearer $MASTER_KEY" \
-d from=2026-07-01 -d to=2026-07-31 \
-d granularity=day -d groupBy=user,model -d format=csv \
-o usage-july.csvThe CSV header is the same fixed column set regardless of the dimensions requested, so a scheduled export never changes shape.
Usage is attributed to the member who created the API key (there is no per-caller identity on an inference request). A key shared by several people reports entirely under its creator. For accurate per-person reporting, issue one gateway API key per member — the per-member key limit under Organization → Members is the lever that enforces it.
Two more things worth knowing:
- These figures come from hourly rollups, not the request log, so they are not affected by your data retention setting — per-member and per-model history stays available even with retention turned off.
- Traffic from embeddable payments end-user keys rolls up to the member who provisioned the platform key.
IAM rules
Each gateway API key can have one or more IAM rules that restrict which models, providers, or pricing tiers it is allowed to use. Rules are evaluated at request time by the gateway. A key with no active rules has no IAM restrictions.
Rule types:
ruleType | Description |
|---|---|
allow_models | Only the listed models are permitted |
deny_models | The listed models are blocked |
allow_providers | Only the listed providers are permitted |
deny_providers | The listed providers are blocked |
allow_pricing | Only models matching the pricing constraint are permitted |
deny_pricing | Models matching the pricing constraint are blocked |
allow_ip_cidrs | Only requests from the listed IPv4/IPv6 CIDRs are permitted |
deny_ip_cidrs | Requests from the listed IPv4/IPv6 CIDRs are blocked |
The ruleValue JSON object holds the rule's parameters. The fields it accepts depend on the ruleType:
| Field | Type | Used by |
|---|---|---|
models | string[] | allow_models, deny_models |
providers | string[] | allow_providers, deny_providers |
pricingType | "free" | "paid" | allow_pricing, deny_pricing |
maxInputPrice | number | allow_pricing, deny_pricing |
maxOutputPrice | number | allow_pricing, deny_pricing |
ipCidrs | string[] | allow_ip_cidrs, deny_ip_cidrs |
IP CIDR rules
IP CIDR rules restrict gateway requests by source IP. Both IPv4 (e.g. 192.0.2.0/24) and IPv6 (e.g. 2001:db8::/32) ranges are supported, and you can mix both in a single rule. To restrict to a single address, use a /32 (IPv4) or /128 (IPv6) prefix.
The gateway reads the client IP from the first entry in the X-Forwarded-For header, which is set by the GCP load balancer.
IPv4-mapped IPv6 addresses (::ffff:1.2.3.4) are normalized to IPv4 so a single 1.2.3.0/24 rule still matches when the upstream connection happens to be IPv6.
When an allow_ip_cidrs rule is configured and the gateway cannot determine the client IP, the request is denied. Invalid CIDR syntax is rejected at rule-creation time with a 400 error.
All endpoints scope by the master key's organization: a 404 is returned if the API key (or rule) is not part of the authenticated master key's organization.
List IAM rules
GET /v1/master/keys/{id}/iam
curl https://internal.llmgateway.io/v1/master/keys/ak_.../iam \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"rules": [
{
"id": "iam_...",
"apiKeyId": "ak_...",
"ruleType": "allow_models",
"ruleValue": {
"models": ["openai/gpt-4o", "anthropic/claude-3-5-sonnet"]
},
"status": "active",
"createdAt": "...",
"updatedAt": "..."
}
]
}Create an IAM rule
POST /v1/master/keys/{id}/iam
curl -X POST https://internal.llmgateway.io/v1/master/keys/ak_.../iam \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"ruleType": "allow_models",
"ruleValue": {
"models": ["openai/gpt-4o", "anthropic/claude-3-5-sonnet"]
}
}'Body parameters:
| Field | Type | Description |
|---|---|---|
ruleType | rule type enum (above) | Required |
ruleValue | object (see table above) | Must include the fields appropriate for the chosen type |
status | "active" | "inactive" | Optional, defaults to "active" |
Restricting by source IP:
curl -X POST https://internal.llmgateway.io/v1/master/keys/ak_.../iam \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"ruleType": "allow_ip_cidrs",
"ruleValue": {
"ipCidrs": ["192.0.2.0/24", "2001:db8::/32"]
}
}'Response (201): the created IAM rule.
Update an IAM rule
PATCH /v1/master/keys/{id}/iam/{ruleId}
All body fields are optional; provide only the ones you want to change.
curl -X PATCH https://internal.llmgateway.io/v1/master/keys/ak_.../iam/iam_... \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "inactive"
}'Body parameters (all optional, at least one required):
| Field | Type | Description |
|---|---|---|
ruleType | rule type enum (above) | Change the rule type |
ruleValue | object (see table above) | Replace the rule value |
status | "active" | "inactive" | Activate or deactivate without deleting |
Response (200): the updated IAM rule.
Delete an IAM rule
DELETE /v1/master/keys/{id}/iam/{ruleId}
Permanently removes an IAM rule from the API key.
curl -X DELETE https://internal.llmgateway.io/v1/master/keys/ak_.../iam/iam_... \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{ "message": "IAM rule deleted successfully" }Member IAM rules
The same rule types can be applied to an organization member instead of a single API key. Member-level rules are an organization-wide ceiling: a request must pass both the member's rules and the key's rules, so key rules can only narrow access further, never expand it. They apply to all regular API keys created by that member. See Member-Level IAM Rules for the full semantics.
The {member} path parameter accepts either the membership id or the member's email address (matched case-insensitively). Email references must resolve to a user who is a member of the master key's organization — an email belonging to a user outside the organization, or an unknown reference, returns a 404.
The ruleType, ruleValue, and status fields are identical to the per-key IAM endpoints above.
List member IAM rules
GET /v1/master/members/{member}/iam
curl https://internal.llmgateway.io/v1/master/members/jane@example.com/iam \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"rules": [
{
"id": "iam_...",
"userOrganizationId": "uo_...",
"ruleType": "allow_providers",
"ruleValue": {
"providers": ["openai"]
},
"status": "active",
"createdAt": "...",
"updatedAt": "..."
}
]
}Create a member IAM rule
POST /v1/master/members/{member}/iam
curl -X POST https://internal.llmgateway.io/v1/master/members/jane@example.com/iam \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"ruleType": "allow_providers",
"ruleValue": {
"providers": ["openai"]
}
}'Response (201): the created member IAM rule.
Update a member IAM rule
PATCH /v1/master/members/{member}/iam/{ruleId}
All body fields are optional; provide only the ones you want to change.
curl -X PATCH https://internal.llmgateway.io/v1/master/members/uo_.../iam/iam_... \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "inactive"
}'Response (200): the updated member IAM rule.
Delete a member IAM rule
DELETE /v1/master/members/{member}/iam/{ruleId}
curl -X DELETE https://internal.llmgateway.io/v1/master/members/uo_.../iam/iam_... \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{ "message": "Member IAM rule deleted successfully" }Custom providers
Custom providers are your own OpenAI-compatible endpoints, registered as BYOK provider keys with provider: "custom". The gateway routes to them with the model string custom/<name>/<model>. See Custom Providers for the dashboard flow and routing semantics.
Only custom providers are exposed through the master API — catalog providers (OpenAI, Anthropic, …) require an interactive upstream credential check and must be added from the dashboard.
All endpoints scope by the master key's organization: a 404 is returned if the provider key is not a non-deleted custom provider in that organization.
The provider token is stored but never returned. Responses only include a
maskedToken for identification.
List custom providers
GET /v1/master/custom-providers
curl https://internal.llmgateway.io/v1/master/custom-providers \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"customProviders": [
{
"id": "pk_...",
"provider": "custom",
"name": "acme",
"baseUrl": "https://llm.acme.example.com/v1",
"maskedToken": "sk-a...cret",
"status": "active",
"customModelsOnly": true,
"complianceAttestation": null,
"organizationId": "org_...",
"createdAt": "...",
"updatedAt": "..."
}
]
}Get a custom provider
GET /v1/master/custom-providers/{id}
Returns a single custom provider in the master key's organization.
Create a custom provider
POST /v1/master/custom-providers
curl -X POST https://internal.llmgateway.io/v1/master/custom-providers \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "acme",
"baseUrl": "https://llm.acme.example.com/v1",
"token": "sk-acme-...",
"customModelsOnly": true
}'Body parameters:
| Field | Type | Description |
|---|---|---|
name | string | Lowercase letters and single hyphens only. Unique per organization; used in the model string |
baseUrl | string | OpenAI-compatible base URL. Internal/reserved addresses are rejected |
token | string | Upstream API key. Stored server-side, never returned |
customModelsOnly | boolean (optional) | Restrict the provider to models defined in your custom catalog. Default false |
complianceAttestation | object (optional) | Self-attested compliance posture. attestedAt / attestedByUserId are stamped server-side |
Response (201): the created custom provider.
Update a custom provider
PATCH /v1/master/custom-providers/{id}
All body fields are optional; provide only the ones you want to change.
curl -X PATCH https://internal.llmgateway.io/v1/master/custom-providers/pk_... \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"baseUrl": "https://llm2.acme.example.com/v1",
"token": "sk-acme-rotated-..."
}'Body parameters (all optional, at least one required):
| Field | Type | Description |
|---|---|---|
baseUrl | string | Repoint the provider at a new endpoint |
token | string | Rotate the upstream API key |
status | "active" | "inactive" | Disable the provider without deleting it |
customModelsOnly | boolean | Toggle the custom-catalog restriction |
complianceAttestation | object | null | Replace the attestation, or null to clear it |
The provider name is immutable — it is the routing segment your model strings reference. Create a new provider instead of renaming.
Response (200): the updated custom provider.
Delete a custom provider
DELETE /v1/master/custom-providers/{id}
Soft-deletes the provider (sets status to "deleted"). Its custom models stop resolving immediately.
Response (200):
{ "message": "Custom provider deleted successfully" }Custom models
Custom models are your organization's catalogue entries for a custom provider: the context window, output limit, per-token pricing, and capability flags the gateway uses to route, validate, and bill requests to that model. When a provider has customModelsOnly: true, only models in this catalogue can be called through it.
Query these endpoints to read the full catalogue you have registered — including contextSize, maxOutput, and every price field — for surfacing in your own dashboards or cost tooling.
Per-token prices are strings in USD per token. Use e-6 notation so the
coefficient reads directly as USD per million tokens — "3.0e-6" is $3.00/M.
List custom models
GET /v1/master/custom-models
Returns the non-deleted custom models in the master key's organization. Pass an optional providerKeyId query parameter to scope the list to a single custom provider.
curl "https://internal.llmgateway.io/v1/master/custom-models?providerKeyId=pk_..." \
-H "Authorization: Bearer $MASTER_KEY"Response (200):
{
"customModels": [
{
"id": "cm_...",
"providerKeyId": "pk_...",
"organizationId": "org_...",
"modelName": "acme-large",
"displayName": "Acme Large",
"contextSize": 200000,
"maxOutput": 32000,
"inputPrice": "3.0e-6",
"outputPrice": "15.0e-6",
"cachedInputPrice": "0.3e-6",
"streaming": "true",
"vision": true,
"tools": true,
"reasoning": null,
"jsonOutput": true,
"supportedParameters": ["temperature", "top_p"],
"status": "active",
"createdAt": "...",
"updatedAt": "..."
}
]
}Get a custom model
GET /v1/master/custom-models/{id}
Returns a single custom model in the master key's organization.
Create a custom model
POST /v1/master/custom-models
curl -X POST https://internal.llmgateway.io/v1/master/custom-models \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{
"providerKeyId": "pk_...",
"modelName": "acme-large",
"displayName": "Acme Large",
"contextSize": 200000,
"maxOutput": 32000,
"inputPrice": "3.0e-6",
"outputPrice": "15.0e-6",
"tools": true
}'Body parameters:
| Field | Type | Description |
|---|---|---|
providerKeyId | string | Must be a custom provider in the master key's organization |
modelName | string | Model id sent upstream. Unique per provider |
displayName | string (optional) | Human-readable label |
contextSize | number (optional) | Context window in tokens |
maxOutput | number (optional) | Max output tokens |
inputPrice | string (optional) | USD per input token |
outputPrice | string (optional) | USD per output token |
cachedInputPrice | string (optional) | USD per cached input token |
cacheReadInputPrice | string (optional) | USD per cache-read token |
cacheWriteInputPrice | string (optional) | USD per cache-write token (5m TTL) |
cacheWriteInputPrice1h | string (optional) | USD per cache-write token (1h TTL) |
requestPrice | string (optional) | Flat USD charged per request |
webSearchPrice | string (optional) | USD per web search |
imageInputPrice | string (optional) | USD per image input token |
audioInputPrice | string (optional) | USD per audio input token |
streaming | "true" | "false" | "only" | Streaming support ("only" = streaming-only model) |
vision | boolean (optional) | Accepts image input |
tools | boolean (optional) | Supports tool calling |
reasoning | boolean (optional) | Emits reasoning output |
jsonOutput | boolean (optional) | Supports JSON / structured output |
audio | boolean (optional) | Accepts audio input |
supportedParameters | string[] (optional) | Request parameters the upstream accepts |
status | "active" | "inactive" (optional) | Defaults to "active" |
Response (201): the created custom model.
Update a custom model
PATCH /v1/master/custom-models/{id}
Accepts the same fields as create (except providerKeyId), all optional — provide only the ones you want to change. Renaming to a modelName already used by another model on the same provider returns a 400.
curl -X PATCH https://internal.llmgateway.io/v1/master/custom-models/cm_... \
-H "Authorization: Bearer $MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{ "contextSize": 400000, "outputPrice": "12.0e-6" }'Response (200): the updated custom model.
Delete a custom model
DELETE /v1/master/custom-models/{id}
Soft-deletes the model (sets status to "deleted").
Response (200):
{ "message": "Custom model deleted successfully" }How is this guide?
Last updated on