OBLETHdocs

Tenants and keys

Set up who shares the gateway, with tenants, fairshare groups and API keys, and give people a portal for their own keys.

6 min readUpdated for v2.4.0Edit on GitHub

TenantA team, course or service that gets a share
GroupTenants that share one group weight
KeyOne credential, in one tenant
WhereTenants and API keys in the dashboard

Add a tenant and a key

  1. 1

    Add the tenant

    On Tenants, choose Add tenant. Give it a name, and optionally a fairshare group and weight.

  2. 2

    Make a key

    On the tenant's page, choose New key (or use New key on API keys and pick the tenant). The secret is shown once, in a banner with Copy and Copy as OPENAI_API_KEY=…. The gateway keeps only a hash of it.

  3. 3

    Call the gateway

    Clients send the key as Authorization: Bearer sk_… or as x-api-key: sk_…, the header Anthropic SDKs use. See OpenAI-compatible API.

The same through the Management API:

curl -X POST http://localhost:9180/api/v1/tenants \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "genomics-lab", "fairshare_group": "research", "weight": 200}'

curl -X POST http://localhost:9180/api/v1/tenants/$TENANT_ID/keys \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "variant-pipeline"}'
# {"key": {...}, "secret": "sk_..."}

Tenants

A tenant is the unit fairshare divides between: every key belongs to exactly one tenant, and a tenant's weight sets its share of each model's pool against the other tenants in its group. See How fairshare works.

Tenants lists every tenant with its group and weight, keys, users, requests in the last day, spend against its budget, limits, and whether its access hours are open now. Filter by status, group, or what a tenant has set, such as a budget, access hours, a model list, guardrails, or being near a limit.

The Tenants list: seven active tenants with their group and weight, keys, 24-hour requests, spend against budget, token rate and waiting requests, and access hours

Each tenant has its own page at /tenants/<name>: requests and failures in the last day, its budget and where the current pace lands, running requests against fair share, a 30-day chart, its limits right now, the models it uses, its keys and people, and recent changes.

A tenant's page: tiles for requests, failures, budget, running requests against fair share and tokens, a 30-day chart, the limits in force right now and the models it uses

Below that, every setting sits on one page with Find a setting (⌘K) and one save bar:

SettingWhat it does
Fairshare group and weightIts share of each pool when everyone is busy. Weight defaults to 100; the page previews the resulting share
Token rate and in-flight capSee Budgets and rate limits
Access hoursWeekly hours on a grid, in the tenant's time zone, plus open-from and until dates
BudgetSpend and token caps per period
ModelsWhich models it may call. All, when the list is empty
GuardrailsContent scanning for its requests. See Guardrails
CompressionSee Boons

The status menu in the header sets the tenant to active, suspended or archived. Suspended and archived tenants are refused with 403.

Fairshare groups

A group is a set of tenants that share one weight: a department, or all teaching tenants against all research tenants. Each pool is split between active groups by group weight first, then between each group's tenants. Every tenant is in the default group until you move it.

Change group weights on Fairshare › Groups & weights, which previews every group's new share before you save. Change a tenant's group on its page. Through the API:

curl -X POST http://localhost:9180/api/v1/fairshare/groups \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "research", "weight": 300}'

curl -X PATCH http://localhost:9180/api/v1/tenants/$TENANT_ID/group \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"fairshare_group": "research"}'

Groups apply under the default hierarchical algorithm. With OBLETH_FAIRSHARE_ALGORITHM=weighted, all tenants compete directly and groups are ignored.

Keys

Give each person, application or pipeline its own key. All of a tenant's keys share its limits, budget and model list, and each key can also carry:

SettingDefaultWhat it does
Weight100Its share of the tenant's slots against the tenant's other keys
Max in flightnoneA cap on its running requests, applied in each model's pool
BudgetnoneIts own spend or token cap per period, checked before the tenant's
Per-user fairshareoffFor a key an app shares among many people: each person it names waits in line on their own. See One key, many people

API keys shows every key with its tenant, status, last use, 30-day requests and spend, budget for the current period, and weight. Tiles count keys used today, near a budget, and unused for 30 days.

The API keys page: tiles for keys used today, near a budget and unused for 30 days, above a list of keys with tenant, status, last use, requests, spend, budget this period and weight

Open a key for its 30-day use, its settings with a save bar, and Replace…, Move… and Delete…. Replace… makes a new key with the same settings; the old one keeps working until you turn it off. Select several keys to turn them off or on, give them one budget, move, export or delete them together.

Turning a key off takes effect on every gateway on its next request; the key returns 403 api key disabled. A deleted key returns 401.

Move keys to another tenant

Move… takes keys to another tenant, or to a new tenant made for them. A new tenant can copy the source tenant's weight, group, limits, hours, budget, models and policies. This gives a heavy user a share of their own without handing out a new secret. Past usage stays with the tenant it was recorded under.

curl -X PUT http://localhost:9180/api/v1/keys/$KEY_ID/tenant \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"tenant_id": "'"$NEW_TENANT_ID"'"}'

Portal users

People who sign in with SSO can manage their own tenant's keys without dashboard access. On Users, give the account the user role and link it to a tenant. They land in the portal, which has three pages:

PageWhat it shows
/portal/modelsThe models the tenant may use. Upstream URLs and keys are never sent
/portal/keysThe tenant's keys with their last day of use. Users can create, turn off and delete keys, but not turn back on a key an administrator turned off
/portal/usageThe tenant's usage and its most recent requests

See Sign-in and SSO.

How keys are stored

A key is sk_ followed by 48 hex characters. The gateway stores its SHA-256 hash and a display prefix (the first 18 characters) and returns the secret once. Set OBLETH_API_KEY_PEPPER to mix a server-side secret into every hash, so a copy of the database alone cannot be used to check guessed keys. Adding, changing or removing the pepper invalidates every key already issued; set the same value on every gateway replica.

If the gateway cannot remove a deleted or turned-off key from Redis, the change is saved but the call returns 502 naming POST /api/v1/resync. Run it, or Settings › Data and backups › Reconcile cache…, to rebuild the cache from Postgres.

Updating a key through the API

PUT /api/v1/keys/{id} replaces every editable field. name is required, and an omitted field returns to its default: weight to 100, and a budget or max_in_flight to none. Send every value you want to keep.

curl -X PUT http://localhost:9180/api/v1/keys/$KEY_ID \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "ci", "weight": 200, "max_in_flight": 4, "budget_cost_usd": 25, "budget_period": "monthly"}'

PUT /api/v1/keys/{id}/disabled with {"disabled": true} turns a key off; DELETE /api/v1/keys/{id} removes it. Keys made from verified identity tokens have kind: "identity"; see Identity tokens.

Synthetic tenants

A tenant flagged synthetic (PUT /api/v1/tenants/{id}/synthetic) is still admitted, metered and billed, but its requests are recorded as request type benchmark. Usage, cost and report queries leave them out unless you pass include_internal=true, and they never enter the daily rollup. obench marks the tenants it seeds this way, and the internal tenant behind the Playground is synthetic too.

Video jobs belong to the key

A video job can be polled, downloaded and deleted only by the key that created it, unless OBLETH_VIDEO_JOB_SCOPE=tenant. Let jobs finish before you retire a key. See Video generation.