LLM Gateway
Features

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_SECRET secret). 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:

FieldTypeDescription
namestringProject name (1–255 chars)
cachingEnabledboolean (optional)Default false
cacheDurationSecondsnumber (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):

FieldTypeDescription
namestring1–255 chars
cachingEnabledboolean
cacheDurationSecondsnumber10–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:

FieldDescription
usageLimitLifetime spend cap (null when uncapped)
usageTotal spend accrued against usageLimit over the key's lifetime
periodUsageLimitRecurring per-window spend cap (null when no windowed limit is configured)
periodUsageDurationValueLength of the window, paired with periodUsageDurationUnit
periodUsageDurationUnit"hour" | "day" | "week" | "month"
currentPeriodUsageSpend accrued in the current window ("0" when unconfigured or the window lapsed)
currentPeriodStartedAtWhen the current window began (null when unconfigured or lapsed)
currentPeriodResetAtWhen 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:

FieldTypeDescription
projectIdstringMust belong to the master key's organization
descriptionstringAPI key description (1–255 chars)
usageLimitstring (optional)Lifetime usage limit
periodUsageLimitstring (optional)Recurring period usage limit
periodUsageDurationValuenumber (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):

FieldTypeDescription
descriptionstring1–255 chars
status"active" | "inactive"
usageLimitstring | nullLifetime usage limit (null to clear)
periodUsageLimitstring | nullRecurring period limit (null to clear)
periodUsageDurationValuenumber | nullRequired 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=user

Query parameters:

ParamTypeDefaultDescription
from, toYYYY-MM-DDlast 7 daysInclusive window, interpreted as wall-clock days in timezone. Max 366 days
timezoneIANA timezoneUTCTimezone the day/hour buckets are labelled in
granularityhour | day | totaldayTime bucket. total collapses the window into one row per group. hour caps at 31 days
groupBycomma-separated: user, model, provider, project, apiKeyuser,modelDimensions to break down by. Pass an empty value for organization-wide totals
projectIdstringRestrict to one project. 404 if it is not in this organization
userIdstringRestrict to one member
apiKeyIdstringRestrict to one gateway API key
limit1–100001000Rows per page
offset≥ 00Row offset for paging
formatjson | csvjsoncsv 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/Berlin

CSV 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.csv

The 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:

ruleTypeDescription
allow_modelsOnly the listed models are permitted
deny_modelsThe listed models are blocked
allow_providersOnly the listed providers are permitted
deny_providersThe listed providers are blocked
allow_pricingOnly models matching the pricing constraint are permitted
deny_pricingModels matching the pricing constraint are blocked
allow_ip_cidrsOnly requests from the listed IPv4/IPv6 CIDRs are permitted
deny_ip_cidrsRequests 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:

FieldTypeUsed by
modelsstring[]allow_models, deny_models
providersstring[]allow_providers, deny_providers
pricingType"free" | "paid"allow_pricing, deny_pricing
maxInputPricenumberallow_pricing, deny_pricing
maxOutputPricenumberallow_pricing, deny_pricing
ipCidrsstring[]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:

FieldTypeDescription
ruleTyperule type enum (above)Required
ruleValueobject (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):

FieldTypeDescription
ruleTyperule type enum (above)Change the rule type
ruleValueobject (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:

FieldTypeDescription
namestringLowercase letters and single hyphens only. Unique per organization; used in the model string
baseUrlstringOpenAI-compatible base URL. Internal/reserved addresses are rejected
tokenstringUpstream API key. Stored server-side, never returned
customModelsOnlyboolean (optional)Restrict the provider to models defined in your custom catalog. Default false
complianceAttestationobject (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):

FieldTypeDescription
baseUrlstringRepoint the provider at a new endpoint
tokenstringRotate the upstream API key
status"active" | "inactive"Disable the provider without deleting it
customModelsOnlybooleanToggle the custom-catalog restriction
complianceAttestationobject | nullReplace 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:

FieldTypeDescription
providerKeyIdstringMust be a custom provider in the master key's organization
modelNamestringModel id sent upstream. Unique per provider
displayNamestring (optional)Human-readable label
contextSizenumber (optional)Context window in tokens
maxOutputnumber (optional)Max output tokens
inputPricestring (optional)USD per input token
outputPricestring (optional)USD per output token
cachedInputPricestring (optional)USD per cached input token
cacheReadInputPricestring (optional)USD per cache-read token
cacheWriteInputPricestring (optional)USD per cache-write token (5m TTL)
cacheWriteInputPrice1hstring (optional)USD per cache-write token (1h TTL)
requestPricestring (optional)Flat USD charged per request
webSearchPricestring (optional)USD per web search
imageInputPricestring (optional)USD per image input token
audioInputPricestring (optional)USD per audio input token
streaming"true" | "false" | "only"Streaming support ("only" = streaming-only model)
visionboolean (optional)Accepts image input
toolsboolean (optional)Supports tool calling
reasoningboolean (optional)Emits reasoning output
jsonOutputboolean (optional)Supports JSON / structured output
audioboolean (optional)Accepts audio input
supportedParametersstring[] (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

On this page

Ready for production?

Ship to production with SSO, audit logs, spend controls, and guardrails your security team will approve.

Explore Enterprise