Skip to Content
Model Access

Model Access

By default every application exposes all models from its configured providers to end-users. Model Access lets you narrow this to a curated list — either at the application level or per billing plan.

Concepts

Access modes

ModeBehaviour
All ModelsEvery model from every configured provider is available. No allow-list is consulted.
RestrictedOnly models explicitly toggled on are available. Everything else returns model_not_allowed.

The mode is stored in applications.model_access_restricted (app-wide) and billing_plans.model_access_restricted (per plan).

Scope: application vs plan

You can configure access at two layers:

  1. Application-wide (application_model_access table) — applies to all end-users unless a more specific plan rule overrides it.
  2. Per billing plan (plan_model_access table) — overrides the application-wide setting for users on that plan. If the plan is also in restricted mode, app-level entries serve as the inherited baseline and plan-level entries layer on top.

Inheritance logic

When a plan is in restricted mode and the parent application is also in restricted mode, the effective allow-list is constructed as:

  1. Start with all app-level application_model_access rows for the application.
  2. Layer plan-level plan_model_access rows on top (plan entries take precedence).

When the application is in All Models mode, plan-level restrictions are ignored — the entire catalog is open.

Provider wildcards

Setting model_identifier = '*' for a provider enables all current and future models from that provider in a single entry.

What end-users see

GET /v1/models returns only models that pass the access check for the requesting user’s plan. If a user sends a request with a model that is blocked, the guard returns:

{ "error": { "message": "The model 'openai/gpt-4o' is not available for this application. Contact your administrator to enable access.", "type": "invalid_request_error", "code": "model_not_allowed", "param": "model" } }

HTTP status: 403. Affected endpoints: all AI endpoints (/v1/chat/completions, /v1/responses, /v1/messages, /v1/embeddings, /v1/images/generations, /v1/audio/*, /v1/video/generations, /v1/realtime).

Changes to model access may take up to 60 seconds to propagate due to capability caching in the guard.

Builder API

All model access configuration is done via Hasura GraphQL. Use your organization token (see API Keys).

Check current application access mode

query GetAppAccessMode($appId: uuid!) { applications_by_pk(id: $appId) { id model_access_restricted } }

Enable restricted mode for an application

mutation SetAppRestricted($appId: uuid!, $restricted: Boolean!) { update_applications_by_pk( pk_columns: { id: $appId } _set: { model_access_restricted: $restricted } ) { id model_access_restricted } }

Read the application allow-list

query GetAppModelAccess($appId: uuid!) { application_model_access( where: { application_id: { _eq: $appId } } order_by: [{ provider: asc }, { model_identifier: asc }] ) { id provider model_identifier enabled created_at updated_at } }

Upsert application allow-list entries

Use the unique constraint application_model_access_app_provider_model_unique for idempotent upserts.

mutation UpsertAppModelAccess( $objects: [application_model_access_insert_input!]! ) { insert_application_model_access( objects: $objects on_conflict: { constraint: application_model_access_app_provider_model_unique update_columns: [enabled] } ) { affected_rows } }

Example variables — allow GPT-4o and block GPT-4o-mini:

{ "objects": [ { "application_id": "<appId>", "provider": "openai", "model_identifier": "gpt-4o", "enabled": true }, { "application_id": "<appId>", "provider": "openai", "model_identifier": "gpt-4o-mini", "enabled": false } ] }

Enable an entire provider (wildcard):

{ "objects": [ { "application_id": "<appId>", "provider": "anthropic", "model_identifier": "*", "enabled": true } ] }

Per-plan access

The same pattern applies to billing_plans.model_access_restricted and the plan_model_access table.

Enable restricted mode for a plan:

mutation SetPlanRestricted($planId: uuid!, $restricted: Boolean!) { update_billing_plans_by_pk( pk_columns: { id: $planId } _set: { model_access_restricted: $restricted } ) { id model_access_restricted } }

Upsert plan allow-list entries (constraint: plan_model_access_plan_provider_model_unique):

mutation UpsertPlanModelAccess( $objects: [plan_model_access_insert_input!]! ) { insert_plan_model_access( objects: $objects on_conflict: { constraint: plan_model_access_plan_provider_model_unique update_columns: [enabled] } ) { affected_rows } }

Query available models

query GetAvailableModels { model_capabilities( order_by: [{ provider: asc }, { model_identifier: asc }] ) { provider model_identifier display_name modality capabilities pricing } }

Use these identifiers to build your allow-list entries.

Dashboard

The Model Access page in the Builder UI (application → Model Access) provides a visual editor:

  • Scope selector — toggle between application-wide and a specific billing plan.
  • Access mode — “All Models” or “Restricted” toggle with a confirmation dialog when switching to restricted with zero models enabled.
  • Preset filters — quick filter buttons (e.g. “Vision”, “Audio”) to select groups of models in bulk.
  • Provider access section — enable/disable all current and future models from a provider with a single toggle (writes a wildcard entry).
  • Virtual model table — sortable by provider, model name, modality, price per 1 M input tokens, and enabled state. Renders up to 2,500+ rows with a windowed scroll container (40 px fixed row height, ±10 overscan rows).

Changes are saved only when you click Save and take effect within 60 seconds.

Last updated on