Skip to main content
The Management API is the provisioning surface for your organization. Creating a project, minting a runtime key for it, inviting the people who work in it, deploying a dynamic route, writing a compliance rule, and retiring any of them are the things you would otherwise do by hand in the platform UI. Here they are HTTP endpoints, so onboarding a service, rolling out a routing change or tearing down a stale environment can live in CI or Terraform instead of a runbook. It is a control-plane API: it manages the containers your model calls run in, not the calls themselves. Sending a prompt still goes through the v3 API with a runtime key.
The Management API and management keys are available on every plan. A plan limits the features, not the API: writing a rule of a kind your plan does not include answers 402 with the code plan_required (see Manage rules from code). Organizations without a card on file are capped at 60 requests per minute and 3 active management keys; adding a card lifts both.

Authentication

The Management API uses its own credential — a management key, prefixed op-mak-. Mint one in the platform UI under Settings → API keys, then send it as a bearer token:
A runtime op-… key is not accepted here — it will be rejected with 403. The two credentials are deliberately different things:
A management key can create and delete projects across your entire organization. Store it as a secret in your CI provider — never in application config alongside runtime keys, and never in a client.

Scopes

Every management key carries an explicit list of scopes, chosen when you mint it. A request whose key lacks the scope an endpoint requires is rejected with 403 and a required_scope field naming what was missing. Grant the narrowest set that does the job. A CI pipeline that only rotates keys needs apikeys:write and nothing else.

Response shape

Every response is an envelope with meta and data. Single resources leave meta empty; collections use it for total_count.

Provision a project and a runtime key

The common pipeline is two calls: create the project, then mint a key inside it.
1

Create the project

Project names are unique within an organization — reusing one returns 409. The response carries the project uuid you need for the next call.
2

Mint a runtime key inside it

The full op-… key is returned once, in this response, and is never retrievable again. Write it to your secret store in the same step that creates it. Listing keys later returns only their prefixes.
Because the secret is shown once, a blind retry is expensive: it mints a second key while the first one’s secret is already lost. Pass an idempotency_key and a retry answers 200 with the existing key’s metadata and meta.idempotency_replayed: true, minting nothing. The secret is not returned again, so if you never captured it, revoke that key and create a replacement under a new idempotency_key.
3

Use it for model calls

The minted key is an ordinary runtime key, scoped to that project. Hand it to the service as OPPER_API_KEY and call the v3 API with it.

Lifecycle notes

  • Updates are partial. PATCH a project with name, description, or both; omit a field to leave it unchanged, and send "description": null to clear it. A body with neither field is rejected rather than treated as a no-op.
  • Deleting a project deletes its keys. The project is soft-deleted, but every runtime key bound to it is permanently destroyed and stops working immediately.
  • Retention is not set here. It is a control-plane rule — see Opper retention.
  • Creates take an idempotency_key. Minting a runtime key and inviting a member both accept one, so a pipeline that never saw a response can retry without creating a second object. A replay answers 200 with the original and meta.idempotency_replayed: true; the same key with different arguments is 409, and so is a replay of something deleted since. Keys are 1 to 128 letters, digits, dots, underscores, colons or hyphens, scoped to the management key that used them.

Manage members and invitations

Projects and runtime keys cover the services that call models; members cover the people. Inviting a colleague from the same pipeline that creates their project gives them a real membership, so their usage shows up under their own name in the platform instead of behind a shared key.
1

Invite the person

Opper emails the invitation, the same one the Members settings page sends. The response also carries the acceptance token and invite_url, so a pipeline can post the link to its own channel instead. email_sent says whether the mail was queued; when it is false the invitation still exists and delivering invite_url is up to you.
token and invite_url let whoever holds them join your organization. Handle them like a secret and do not write them to build logs.
2

Check who has accepted

pending=true lists the invitations not yet accepted and pending=false the accepted ones; leave it out for both. An invitation is valid for four weeks. GET /management/v1/members lists everyone who has joined, with their effective role and an is_owner flag.

Invite behavior

  • Inviting twice resends. A second invite for an address that already has a live invitation does not create another one. It emails the invitee again, keeps the same token (so a link already in their inbox still works), moves the expiry four weeks out and answers 200 instead of 201.
  • Retries are safe with an idempotency_key. A repeat with the same key answers 200 with the original invitation and meta.idempotency_replayed: true, sends no email and leaves the expiry alone. Reusing a key with a different address or role is 409. The key is 1 to 128 letters, digits, dots, underscores, colons or hyphens. Leave it out when you actually want to resend.
  • Existing members are refused. Inviting an address that already belongs to a member answers 409.
  • Choosing a role depends on the plan. On Control Plane and Enterprise, pass role with a built-in role (admin, developer, viewer). Custom roles are Enterprise only: on Enterprise, role can also be one of your custom roles. A role the organization does not have is 422. On other plans leave role out: sending one is refused with 403 rather than ignored. A new invitation with no role gives the invitee the plan’s default member role. A management key can assign any role up to admin. Ownership is never granted by invitation. See Roles and permissions for what each role can do and which plans can assign roles.

Remove a member

$USER_UUID is the member’s uuid from GET /management/v1/members. Removal takes effect immediately and revokes what the membership issued in this organization: the member’s runtime API keys, the OAuth authorizations behind them and their agent connections. It is not an account deletion; the person keeps their Opper account and their access to any other organization. The organization’s owner cannot be removed (409). Transfer ownership in the platform first. To withdraw an invitation that has not been accepted, delete it with DELETE /management/v1/invites/{id} and its link stops working. Deleting an accepted invitation only removes the record of it; remove the member to take away their access.
Member management needs a management key. It is deliberately not exposed to agents connected to Opper through OAuth, so an assistant acting on a person’s behalf cannot change who belongs to the organization.

Deploy a dynamic route from code

A dynamic route is a graph the gateway walks per request to pick the model. The editor and this API save the same graph, so a route built in the UI can be exported, versioned in git and redeployed from a pipeline. The smallest useful route is a Pool node with two models: the first that answers wins, and a model a compliance rule blocks is skipped.
1

Create the route with its first version

deploy: true validates the draft and publishes it as version 1 in one call. Leave it out to save a draft and deploy later with POST /management/v1/dynamic-routes/support-router/deploy.
2

Check the path a request would take

No provider is called. The response lists the nodes visited and the model that would answer.
3

Call it by name

The route is a model id: pass dynamic/support-router as model on any endpoint, with a runtime key. The response’s meta.routing says which version ran and which model served.
Every deploy is a numbered version. POST …/versions/{n}/rollback re-deploys an older graph as a new version, so the history stays linear.

Manage rules from code

Rules are rows the same API can read and write: retention, spend limits, model access, checks and routing defaults. A rule created here is enforced from the next request, exactly like one saved on the Rules page. Each rule has a kind (guard, observe, route, comply), a scope (the organization or a set of projects; a budget rule can also cap members by role or by name) and a config whose shape follows the kind; the reference documents each.
id, kind and scope are fixed once created; change anything else with PATCH. A zero-day retention rule over a scope that still holds stored files is refused with 409 until you resend with confirm_file_deletion: true. Rules are a Control Plane feature. On a plan without it, creating or updating a rule answers 402, and the message names the rule kind and your current plan:
Listing and deleting rules is never plan gated. Downgrading to the Gateway plan deletes every rule after you confirm it.

Revoke a leaked management key

If a management key is exposed, disable it with the key itself:
Possession of the token is the authorization: no scope is required and the call is never rate limited, so a leaked key can always be shut off. This disables the key the request authenticated with and every token issued under it.

Endpoints

Projects

List, create, fetch, update, and delete projects.

API keys

List, mint, and delete a project’s runtime keys.

Members

List and remove members, and send, list and revoke invitations.

Dynamic routes

Create, simulate, deploy, version and roll back routes.

Rules

List, create, update and delete rules.
Last modified on October 7, 2026