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, prefixedop-mak-. Mint one in the platform UI under Settings → API keys, then send it
as a bearer token:
op-… key is not accepted here — it will be rejected with 403. The
two credentials are deliberately different things:
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 with403
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 withmeta 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
409.
The response carries the project uuid you need for the next call.2
Mint a runtime key inside it
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.
PATCHa project withname,description, or both; omit a field to leave it unchanged, and send"description": nullto 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 answers200with the original andmeta.idempotency_replayed: true; the same key with different arguments is409, 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
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.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
200instead of201. - Retries are safe with an
idempotency_key. A repeat with the same key answers200with the original invitation andmeta.idempotency_replayed: true, sends no email and leaves the expiry alone. Reusing a key with a different address or role is409. 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
rolewith a built-in role (admin,developer,viewer). Custom roles are Enterprise only: on Enterprise,rolecan also be one of your custom roles. A role the organization does not have is422. On other plans leaveroleout: sending one is refused with403rather than ignored. A new invitation with norolegives the invitee the plan’s default member role. A management key can assign any role up toadmin. 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
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.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 akind (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:
Revoke a leaked management key
If a management key is exposed, disable it with the key itself: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.