Gateway — Model Registry API
Base path: /api/v1/gateway/models. A model is a named deployment: a
team-chosen publicName (what callers send as model) → an upstreamModel (what
is actually sent to the provider) → a bound credential → its own pricing. A
request's model resolves here — there is no provider name-inference — so any
provider/aggregator (OpenRouter, Together, local vLLM) works once registered.
Auth: session cookie or personal/team API key (requireAnyAuth). Mutations
(POST/PATCH/DELETE) and POST /:id/test require role owner or admin;
reads (GET) allow any role.
Pricing is USD per 1M tokens. On create, omitted prices are prefilled from the
static registry for known upstream ids; unknown ids default to null (cost logged
null) and can be set manually. fallbackModelIds is an ordered list of other
registered models tried on failure.
All examples below were verified with real curl output against a running server
(dev DB, real GATEWAY_ENCRYPTION_KEY).
POST /api/v1/gateway/models
Register a model (owner/admin). Unknown upstream id → prices null.
curl -X POST $ACRUXCORE_BASE_URL/gateway/models \
-H "Content-Type: application/json" \
--cookie "connect.sid=<session>" \
-d '{"publicName":"claude-via-or","upstreamModel":"anthropic/claude-3.5-sonnet","credentialId":"48e1abec-3fad-4c4f-ad36-9968cf116baa"}'
Response (status 201):
{
"id": "ef2ccd0a-64af-498f-866b-2f27cc5e33d7",
"publicName": "claude-via-or",
"upstreamModel": "anthropic/claude-3.5-sonnet",
"credentialId": "48e1abec-3fad-4c4f-ad36-9968cf116baa",
"credentialLabel": "OpenRouter",
"provider": "openai_compatible",
"inputPricePerM": null,
"outputPricePerM": null,
"fallbacks": [],
"createdAt": "2026-07-04T12:21:51.420Z",
"updatedAt": "2026-07-04T12:21:51.420Z"
}
Errors: 400 VALIDATION_ERROR (bad body); 400 INVALID_FALLBACK (unknown/self
fallback id); 409 (duplicate publicName for the team).
GET /api/v1/gateway/models
List the team's models, newest first (any role).
curl $ACRUXCORE_BASE_URL/gateway/models \
--cookie "connect.sid=<session>"
Response (status 200):
[
{
"id": "ef2ccd0a-64af-498f-866b-2f27cc5e33d7",
"publicName": "claude-via-or",
"upstreamModel": "anthropic/claude-3.5-sonnet",
"credentialId": "48e1abec-3fad-4c4f-ad36-9968cf116baa",
"credentialLabel": "OpenRouter",
"provider": "openai_compatible",
"inputPricePerM": null,
"outputPricePerM": null,
"fallbacks": [],
"createdAt": "2026-07-04T12:21:51.420Z",
"updatedAt": "2026-07-04T12:21:51.420Z"
}
]
PATCH /api/v1/gateway/models/:id
Update in place (owner/admin). Omitted price fields are unchanged; passing
fallbackModelIds replaces the whole ordered chain. The public name stays
callable across the edit.
curl -X PATCH $ACRUXCORE_BASE_URL/gateway/models/ef2ccd0a-64af-498f-866b-2f27cc5e33d7 \
-H "Content-Type: application/json" \
--cookie "connect.sid=<session>" \
-d '{"inputPricePerM":3,"outputPricePerM":15}'
Response (status 200) — excerpt:
{ "publicName": "claude-via-or", "inputPricePerM": 3, "outputPricePerM": 15 }
DELETE /api/v1/gateway/models/:id
Delete a model (owner/admin). Blocked while it is another model's fallback.
curl -i -X DELETE $ACRUXCORE_BASE_URL/gateway/models/<fallback-id> \
--cookie "connect.sid=<session>"
Response (status 409):
{ "error": { "code": "MODEL_IS_FALLBACK", "message": "This model is used as a fallback by another model." } }
On success: 204 No Content. Deleting a credential still bound to a model is
likewise blocked: 409 CREDENTIAL_IN_USE from DELETE /gateway/connections/:id.
POST /api/v1/gateway/models/:id/test
Fire a diagnostic 1-token completion through the model's credential (owner/admin).
Never throws to the client; a provider failure returns ok:false. Does not count
against budgets or the usage log.
curl -X POST $ACRUXCORE_BASE_URL/gateway/models/ef2ccd0a-64af-498f-866b-2f27cc5e33d7/test \
--cookie "connect.sid=<session>"
Response (status 200) — a bad/placeholder key surfaces the provider error:
{ "ok": false, "error": "OpenAI error 401: {\"error\":{\"message\":\"Missing Authentication header\",\"code\":401}}" }
A working key returns { "ok": true, "latencyMs": 512 }.