Every API here is available over the APIs.io API and to AI agents over MCP.
{
"openapi": "3.0.3",
"info": {
"title": "Organization Management",
"version": "2.0",
"description": "Your project description"
},
"paths": {
"/manage/auth-keys/": {
"get": {
"operationId": "manage_auth_keys_retrieve",
"description": "List the org's management keys (issuer + worker), never their secrets. ``manage:mint``.",
"tags": [
"auth-keys"
],
"responses": {
"200": {
"description": "No response body"
}
}
},
"post": {
"operationId": "manage_auth_keys_create",
"description": "``POST /v3/manage/auth-keys`` (manage:mint) -- an ISSUER key mints a new WORKER\n(manage:read/manage:write) key for its own org; the secret is shown ONCE. This is the\nprogrammatic middle of the service->auth->inference chain: an issuer key (born once from the\nowner-authed dashboard) mints short-lived working keys with no human in the loop, which in turn\nmint inference keys via ``POST /manage/keys``.\n\nA minted key can NEVER carry ``manage:mint`` -- an issuer cannot mint another issuer\n(no self-propagation); issuer keys are born only from the session-authed, owner-only dashboard\nendpoint. Enforced twice: the serializer's ``allowed_scopes`` restricts this endpoint to\nread/write, and ``validate_scopes`` on the model rejects any mint/worker mix regardless.",
"tags": [
"auth-keys"
],
"responses": {
"200": {
"description": "No response body"
}
}
}
},
"/manage/auth-keys/{key_id}/": {
"delete": {
"operationId": "manage_auth_keys_destroy",
"description": "``DELETE /v3/manage/auth-keys/{key_id}`` (``manage:mint``) -- an issuer revokes a WORKER key it\nmanages (terminal, idempotent). Refuses an ISSUER-key target: issuer keys are born and killed only\nfrom the owner-authed dashboard, mirroring the no-self-propagation rule on mint. Org-scoped: a\nkey_id outside the issuer's org is a 404.",
"parameters": [
{
"in": "path",
"name": "key_id",
"schema": {
"type": "string",
"format": "uuid"
},
"required": true
}
],
"tags": [
"auth-keys"
],
"responses": {
"204": {
"description": "No response body"
}
}
}
},
"/manage/groups/": {
"get": {
"operationId": "manage_groups_list",
"description": "``GET /v3/manage/groups`` -- the org's IdP-synced directory groups and their members, ordered\nby name for a stable page. ``manage:read``, org-scoped. Read-only in v1 (see module docstring).",
"parameters": [
{
"name": "limit",
"required": false,
"in": "query",
"description": "Number of results to return per page.",
"schema": {
"type": "integer"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"description": "The initial index from which to return the results.",
"schema": {
"type": "integer"
}
}
],
"tags": [
"groups"
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PaginatedManageGroupList"
}
}
},
"description": ""
}
}
}
},
"/manage/groups/{external_group_id}/": {
"get": {
"operationId": "manage_groups_retrieve",
"description": "``GET /v3/manage/groups/{external_group_id}`` -- one IdP-synced group and its members, for\nlist+detail parity with ``/keys``. ``manage:read``, org-scoped: a group id not in the calling key's\norg is a 404, never another org's group. Addressed by ``external_group_id`` (the IdP id), like the\nlist; the internal PK is never exposed. ``external_group_id`` is unique per org\n(``unique_together`` with ``organization`` on the model), so the org-scoped lookup matches at most\none row -- ``get_object`` cannot raise ``MultipleObjectsReturned``.",
"parameters": [
{
"in": "path",
"name": "external_group_id",
"schema": {
"type": "string"
},
"required": true
}
],
"tags": [
"groups"
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ManageGroup"
}
}
},
"description": ""
}
}
}
},
"/manage/keys/": {
"get": {
"operationId": "manage_keys_list",
"description": "``GET /v3/manage/keys`` (list, ``manage:read``) and ``POST /v3/manage/keys`` (mint an inference\nkey, ``manage:write``). Org-scoped through ``principals_in_org``. Balance is pending-adjusted per\npage on the list; the created secret is returned ONCE in the 201 and never by a read.",
"parameters": [
{
"name": "limit",
"required": false,
"in": "query",
"description": "Number of results to return per page.",
"schema": {
"type": "integer"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"description": "The initial index from which to return the results.",
"schema": {
"type": "integer"
}
}
],
"tags": [
"keys"
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PaginatedManagementKeyList"
}
}
},
"description": ""
}
}
},
"post": {
"operationId": "manage_keys_create",
"description": "``GET /v3/manage/keys`` (list, ``manage:read``) and ``POST /v3/manage/keys`` (mint an inference\nkey, ``manage:write``). Org-scoped through ``principals_in_org``. Balance is pending-adjusted per\npage on the list; the created secret is returned ONCE in the 201 and never by a read.",
"tags": [
"keys"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ManagementKeyRequest"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/ManagementKeyRequest"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/ManagementKeyRequest"
}
}
},
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ManagementKey"
}
}
},
"description": ""
}
}
}
},
"/manage/keys/{key_id}/": {
"get": {
"operationId": "manage_keys_retrieve",
"description": "``GET`` (read), ``PATCH`` (write, policy fields + rename), ``DELETE`` (write, IRREVERSIBLE revoke)\nfor one key at ``/v3/manage/keys/{key_id}``, org-scoped: a ``key_id`` not in the calling key's org\nis a 404, not another org's key. DELETE destroys the secret and keeps the row marked revoked (see\n``destroy``); there is no re-enable. Balance is pending-adjusted. Never returns the secret or its hash.",
"parameters": [
{
"in": "path",
"name": "key_id",
"schema": {
"type": "string",
"format": "uuid"
},
"required": true
}
],
"tags": [
"keys"
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ManagementKey"
}
}
},
"description": ""
}
}
},
"patch": {
"operationId": "manage_keys_partial_update",
"description": "``GET`` (read), ``PATCH`` (write, policy fields + rename), ``DELETE`` (write, IRREVERSIBLE revoke)\nfor one key at ``/v3/manage/keys/{key_id}``, org-scoped: a ``key_id`` not in the calling key's org\nis a 404, not another org's key. DELETE destroys the secret and keeps the row marked revoked (see\n``destroy``); there is no re-enable. Balance is pending-adjusted. Never returns the secret or its hash.",
"parameters": [
{
"in": "path",
"name": "key_id",
"schema": {
"type": "string",
"format": "uuid"
},
"required": true
}
],
"tags": [
"keys"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PatchedManagementKeyRequest"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/PatchedManagementKeyRequest"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/PatchedManagementKeyRequest"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ManagementKey"
}
}
},
"description": ""
}
}
},
"delete": {
"operationId": "manage_keys_destroy",
"description": "``GET`` (read), ``PATCH`` (write, policy fields + rename), ``DELETE`` (write, IRREVERSIBLE revoke)\nfor one key at ``/v3/manage/keys/{key_id}``, org-scoped: a ``key_id`` not in the calling key's org\nis a 404, not another org's key. DELETE destroys the secret and keeps the row marked revoked (see\n``destroy``); there is no re-enable. Balance is pending-adjusted. Never returns the secret or its hash.",
"parameters": [
{
"in": "path",
"name": "key_id",
"schema": {
"type": "string",
"format": "uuid"
},
"required": true
}
],
"tags": [
"keys"
],
"responses": {
"204": {
"description": "No response body"
}
}
}
},
"/manage/keys/{key_id}/rotate/": {
"post": {
"operationId": "manage_keys_rotate_create",
"description": "``POST /v3/manage/keys/{key_id}/rotate`` (manage:write) -- replace a key's secret in place: same\nidentity (name, key_id, budget, guardrail), a brand-new ``sk-eden`` value returned ONCE. Lets a\ncompliance pipeline roll a secret without reconfiguring the key. Distinct from DELETE: revoke KILLS\n(terminal), rotate RE-SECRETS.\n\nA REVOKED key is a 404 here, never resurrected. ``rotate_key_secret`` clears ``revoked_at`` and\nwrites fresh secret material, so rotating a revoked key would REVIVE it -- the exact one-way-door\nthat revoke depends on. The read filters a revoked (or cross-org, or unknown) key_id to a 404, and\nthe whole rotate runs in a transaction that ``select_for_update``-locks the row: a concurrent DELETE\neither commits its revoke first (``revoked_at`` then filters the row out -> 404) or blocks on the\nlock until the rotate commits and then still wins its own compare-and-set. Without the lock the read\nfilter alone is a check-then-act race -- a rotate that resolved a live key could blind-write a fresh\nsecret AFTER a concurrent revoke landed, resurrecting a terminally-revoked key. So revocation stays\nterminal.",
"parameters": [
{
"in": "path",
"name": "key_id",
"schema": {
"type": "string",
"format": "uuid"
},
"required": true
}
],
"tags": [
"keys"
],
"responses": {
"200": {
"description": "No response body"
}
}
}
},
"/manage/keys/{key_id}/usage/": {
"get": {
"operationId": "manage_keys_usage_retrieve",
"description": "``GET /v3/manage/keys/{key_id}/usage`` -- usage for one key, filtered by ``custom_token``\nIDENTITY (precise, unlike the dashboard's name filter). A ``key_id`` not in the org is a 404.\nTime-series only. ``manage:read``.",
"parameters": [
{
"in": "path",
"name": "key_id",
"schema": {
"type": "string",
"format": "uuid"
},
"required": true
}
],
"tags": [
"keys"
],
"responses": {
"200": {
"description": "No response body"
}
}
}
},
"/manage/members/": {
"get": {
"operationId": "manage_members_list",
"description": "``GET /v3/manage/members`` — the org's active members and owner. Reuses the\ndashboard's ``OrganisationUsersSerializer`` and the one ``principals_in_org``\ndefinition, so the management plane and the dashboard cannot disagree on who is a\nmember. Ordered by email for a stable page across requests.",
"parameters": [
{
"name": "limit",
"required": false,
"in": "query",
"description": "Number of results to return per page.",
"schema": {
"type": "integer"
}
},
{
"name": "offset",
"required": false,
"in": "query",
"description": "The initial index from which to return the results.",
"schema": {
"type": "integer"
}
}
],
"tags": [
"members"
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PaginatedOrganisationUsersList"
}
}
},
"description": ""
}
}
}
},
"/manage/members/{email}/role/": {
"patch": {
"operationId": "manage_members_role_partial_update",
"description": "``PATCH /v3/manage/members/{email}/role`` -- set a member's RBAC role. ``manage:write``,\norg-scoped: the member must be a principal of the calling key's org (else 404). The org owner's\nrole is immutable. Audited (actor = the management key). Returns the updated member in the same\nrepresentation as GET /manage/members.",
"parameters": [
{
"in": "path",
"name": "email",
"schema": {
"type": "string"
},
"required": true
}
],
"tags": [
"members"
],
"responses": {
"200": {
"description": "No response body"
}
}
}
},
"/manage/usage/": {
"get": {
"operationId": "manage_usage_retrieve",
"description": "``GET /v3/manage/usage`` -- organization usage aggregated over History, scoped to the calling\nkey's org (``principals_in_org``). ``manage:read``. ``end`` is exclusive at day granularity; see the\nmodule docstring for the parameter and envelope contract.",
"tags": [
"usage"
],
"responses": {
"200": {
"description": "No response body"
}
}
}
},
"/manage/whoami/": {
"get": {
"operationId": "manage_whoami_retrieve",
"description": "``GET /v3/manage/whoami`` — the calling key's own organization, scopes and\nexpiry. Requires only a valid management key (no scope), which is enough to prove\nthe auth path end to end. Never returns the secret, its hash, or last4.",
"tags": [
"whoami"
],
"responses": {
"200": {
"description": "No response body"
}
}
}
}
},
"components": {
"schemas": {
"ManageGroup": {
"type": "object",
"description": "A directory-synced group as the management plane exposes it: the stable IdP identifiers plus\nthe member emails. The internal PK is not exposed; ``external_group_id`` is the addressable id.",
"properties": {
"external_group_id": {
"type": "string",
"maxLength": 255
},
"name": {
"type": "string",
"maxLength": 255
},
"members": {
"type": "string",
"readOnly": true
}
},
"required": [
"external_group_id",
"members",
"name"
]
},
"ManagementKey": {
"type": "object",
"description": "An org's inference key as the management plane exposes it. Never the secret or\nits hash — only non-secret metadata, addressed by the stable ``key_id`` (``id``).\n\n``balance`` is the pending-adjusted LIVE sub-limit, not the raw settled column.\nFor a credit-ledger-enrolled org the stored ``Token.balance`` lags by whatever the\nbatched rollup has not settled, so a management admin must be shown available, not\nsettled (the shipped display-vs-accounting doctrine, mirrored from\n``utils/check_credits.py`` and ``manage_user_account_serializers``). The un-settled\ndeltas (≤ 0, so they ADD) are batch-fetched once per page by the view and passed in\n``context[\"pending\"]``; a non-ledger org simply has none, so the add is a no-op.\n\n``id`` is null for an un-migrated legacy key (one still stored as a JWT, no\n``key_id`` yet); such a key is listed but not addressable via ``/keys/{id}`` until\nit is regenerated onto the hashed scheme.",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"readOnly": true
},
"name": {
"type": "string",
"description": "The token name",
"maxLength": 200
},
"token_type": {
"$ref": "#/components/schemas/TokenTypeEnum"
},
"member": {
"type": "string",
"format": "email",
"readOnly": true
},
"masked": {
"type": "string",
"readOnly": true
},
"active_balance": {
"type": "boolean",
"description": "Weither to use the balance field or not."
},
"balance": {
"type": "string",
"readOnly": true
},
"expire_time": {
"type": "string",
"format": "date-time",
"nullable": true
},
"revoked": {
"type": "boolean",
"readOnly": true
},
"revoked_at": {
"type": "string",
"format": "date-time",
"nullable": true
}
},
"required": [
"balance",
"id",
"masked",
"member",
"name",
"revoked"
]
},
"ManagementKeyRequest": {
"type": "object",
"description": "An org's inference key as the management plane exposes it. Never the secret or\nits hash — only non-secret metadata, addressed by the stable ``key_id`` (``id``).\n\n``balance`` is the pending-adjusted LIVE sub-limit, not the raw settled column.\nFor a credit-ledger-enrolled org the stored ``Token.balance`` lags by whatever the\nbatched rollup has not settled, so a management admin must be shown available, not\nsettled (the shipped display-vs-accounting doctrine, mirrored from\n``utils/check_credits.py`` and ``manage_user_account_serializers``). The un-settled\ndeltas (≤ 0, so they ADD) are batch-fetched once per page by the view and passed in\n``context[\"pending\"]``; a non-ledger org simply has none, so the add is a no-op.\n\n``id`` is null for an un-migrated legacy key (one still stored as a JWT, no\n``key_id`` yet); such a key is listed but not addressable via ``/keys/{id}`` until\nit is regenerated onto the hashed scheme.",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "The token name",
"maxLength": 200
},
"token_type": {
"$ref": "#/components/schemas/TokenTypeEnum"
},
"active_balance": {
"type": "boolean",
"description": "Weither to use the balance field or not."
},
"expire_time": {
"type": "string",
"format": "date-time",
"nullable": true
},
"revoked_at": {
"type": "string",
"format": "date-time",
"nullable": true
}
},
"required": [
"name"
]
},
"OrganisationUsers": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"nullable": true,
"maxLength": 500
},
"fullname": {
"type": "string",
"pattern": "^[a-zA-Z\\'\\- ]+$",
"maxLength": 60
},
"is_confirmed": {
"type": "boolean"
},
"role": {
"type": "string",
"readOnly": true
},
"guardrail": {
"type": "string",
"readOnly": true
},
"effective_rate_limit": {
"type": "string",
"readOnly": true
},
"tokens": {
"type": "string",
"readOnly": true
},
"is_org_owner": {
"type": "boolean",
"readOnly": true
},
"directory_groups": {
"type": "string",
"readOnly": true
}
},
"required": [
"directory_groups",
"effective_rate_limit",
"fullname",
"guardrail",
"is_org_owner",
"role",
"tokens"
]
},
"PaginatedManageGroupList": {
"type": "object",
"required": [
"count",
"results"
],
"properties": {
"count": {
"type": "integer",
"example": 123
},
"next": {
"type": "string",
"nullable": true,
"format": "uri",
"example": "http://api.example.org/accounts/?offset=400&limit=100"
},
"previous": {
"type": "string",
"nullable": true,
"format": "uri",
"example": "http://api.example.org/accounts/?offset=200&limit=100"
},
"results": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ManageGroup"
}
}
}
},
"PaginatedManagementKeyList": {
"type": "object",
"required": [
"count",
"results"
],
"properties": {
"count": {
"type": "integer",
"example": 123
},
"next": {
"type": "string",
"nullable": true,
"format": "uri",
"example": "http://api.example.org/accounts/?offset=400&limit=100"
},
"previous": {
"type": "string",
"nullable": true,
"format": "uri",
"example": "http://api.example.org/accounts/?offset=200&limit=100"
},
"results": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ManagementKey"
}
}
}
},
"PaginatedOrganisationUsersList": {
"type": "object",
"required": [
"count",
"results"
],
"properties": {
"count": {
"type": "integer",
"example": 123
},
"next": {
"type": "string",
"nullable": true,
"format": "uri",
"example": "http://api.example.org/accounts/?offset=400&limit=100"
},
"previous": {
"type": "string",
"nullable": true,
"format": "uri",
"example": "http://api.example.org/accounts/?offset=200&limit=100"
},
"results": {
"type": "array"
# --- truncated at 32 KB (34 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/eden-ai/refs/heads/main/openapi/_original/eden-ai-organization-management-openapi.json