Arkova Key Management API

API key lifecycle management (requires Supabase JWT)

Operations 8

GET /keys/member List member key authorizations in the selected workspace #
POST /keys/member Authorize a member to receive an immutable organization key #
POST /keys/member/{intentId}/claim Privately claim an authorized member key once #
GET /keys List API keys #
POST /keys Create API key #
GET /keys/scopes List grantable API key scopes #
PATCH /keys/{keyId} Update API key #
DELETE /keys/{keyId} Retire organization API key #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/arkova-ai:arkova-ai-key-management-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

arkova-ai-key-management-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Arkova Verification Key Management API
  version: 1.0.1
  description: Programmatic credential verification API. Verify credentials anchored on the Bitcoin network. Worker-generated v1 JSON errors expose common top-level code and message fields. The legacy error field is retained unchanged (string or object), along with details and quota context, for existing integrations. Parse code and message for new integrations. Upstream gateway errors may not be JSON. Revision 1.0.1 restores legacy label and DIPLOMA inputs and clarifies receipts and webhook payloads; the endpoint version remains v1.
  contact:
    name: Arkova Support
    url: https://arkova.ai
servers:
- url: /api/v1
  description: Verification API v1
security:
- ApiKeyBearer: []
- ApiKeyHeader: []
tags:
- name: Key Management
  description: API key lifecycle management (requires Supabase JWT)
paths:
  /keys/member:
    parameters:
    - name: X-Org-Id
      in: header
      required: true
      schema:
        type: string
        format: uuid
      description: Exact selected organization. Requires current membership there; never falls back to the profile organization. Cannot be combined with context=personal.
    get:
      operationId: listMemberKeyAuthorizations
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      summary: List member key authorizations in the selected workspace
      description: Current owners/admins can list this organization’s authorizations. Other current members see only their own. Includes consumed authorizations for lost-response recovery; never includes raw keys or digests. Pages contain at most 50 entries in descending UUID order.
      parameters:
      - name: before
        in: query
        schema:
          type: string
          format: uuid
        description: Use next_cursor from the preceding page in the same workspace and caller view. Intents are ordered by created_at descending, then ID descending. A missing or nonvisible cursor is rejected.
      responses:
        '200':
          description: Safe authorization metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberKeyAuthorizationList'
        '400':
          description: Invalid request, workspace selector, or cursor
        '401':
          description: Verified user session required; API keys cannot authorize or claim human credentials
        '403':
          description: Inactive user, missing membership, insufficient administrator authority, or wrong claimant
        '503':
          description: Authority or persistence unavailable; no success or secret delivery is asserted. Refresh intent status before retrying.
    post:
      operationId: authorizeMemberApiKey
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      summary: Authorize a member to receive an immutable organization key
      description: An active owner/admin authorizes an exact current member. No secret is returned. Reuse request_id only with the identical payload after an uncertain response. Authorization expires after 24 hours. For rotation, the old key remains usable until the intended member successfully claims its replacement. Rotation preserves the actor and organization. To change members, revoke the old key and authorize a new key without rotate_key_id. Claim rechecks issuer and member authority under database locks.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MemberKeyAuthorizationRequest'
      responses:
        '200':
          description: Identical pending authorization already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberKeyAuthorizationReceipt'
        '201':
          description: Pending authorization created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberKeyAuthorizationReceipt'
        '400':
          description: Invalid request, workspace selector, or cursor
        '401':
          description: Verified user session required; API keys cannot authorize or claim human credentials
        '403':
          description: Inactive user, missing membership, insufficient administrator authority, or wrong claimant
        '409':
          description: Expired or consumed authorization, conflicting request ID, or unavailable rotation. Repeated claims never replay a secret.
        '429':
          description: Key creation budget reached; inspect Retry-After before another attempt
        '503':
          description: Authority or persistence unavailable; no success or secret delivery is asserted. Refresh intent status before retrying.
  /keys/member/{intentId}/claim:
    post:
      operationId: claimMemberApiKey
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      summary: Privately claim an authorized member key once
      description: Only the intended authenticated member can receive this secret. The same transaction consumes the authorization, stores a digest, records a receipt, and revokes the previous key if rotating. Parallel or repeated claims never mint a second key or replay a secret. A lost response requires checking the authorization list; if claimed, request a replacement. Secrets are never returned to another member’s administrator.
      parameters:
      - name: X-Org-Id
        in: header
        required: true
        schema:
          type: string
          format: uuid
        description: Exact selected organization. Requires current membership there; never falls back to the profile organization. Cannot be combined with context=personal.
      - name: intentId
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
      responses:
        '201':
          description: Committed key; raw secret returned once
          headers:
            Cache-Control:
              schema:
                type: string
                enum:
                - no-store
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberKeyClaimed'
        '400':
          description: Invalid request, workspace selector, or cursor
        '401':
          description: Verified user session required; API keys cannot authorize or claim human credentials
        '403':
          description: Inactive user, missing membership, insufficient administrator authority, or wrong claimant
        '404':
          description: Authorization not found in this workspace
        '409':
          description: Authorization expired/unavailable, or already claimed. member_key_secret_unavailable includes a secret-free receipt; authorize a replacement if the one-time secret was lost.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MemberKeyClaimConflict'
        '429':
          description: Key creation budget reached; inspect Retry-After before another attempt
        '503':
          description: Authority or persistence unavailable; no success or secret delivery is asserted. Refresh intent status before retrying.
  /keys:
    get:
      summary: List API keys
      description: By default, organization admins list every key in their organization and members list only keys assigned to them. Add context=personal to list the JWT owner's personal keys even after joining an organization; listing and revocation remain available if the paid plan or identity verification lapses.
      operationId: listApiKeys
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      parameters:
      - name: X-Org-Id
        in: header
        required: false
        schema:
          type: string
          format: uuid
        description: Exact selected organization. Requires current membership there; never falls back to the profile organization. Cannot be combined with context=personal.
      - name: context
        in: query
        schema:
          type: string
          enum:
          - personal
        description: Select the JWT owner's personal keys instead of the default organization context.
      responses:
        '200':
          description: List of API keys (masked)
          content:
            application/json:
              schema:
                type: object
                properties:
                  can_manage:
                    type: boolean
                    description: True for an organization admin or the owner in explicit personal context. False for a member viewing assigned organization keys.
                  keys:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKeyMasked'
                  personal_eligibility:
                    type: string
                    enum:
                    - eligible
                    - paid_plan_required
                    - identity_verification_required
                    - unavailable
                    description: Returned in personal context to explain whether a new personal key may be created; does not restrict listing or revocation.
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      summary: Create API key
      description: Create an organization API key as an organization admin, optionally assigned to a current member. Add context=personal to create a JWT-owner key requiring a current paid personal Verified plan and active canonical identity verification entitlement. The raw key is returned once. Rate limited to 10 creations per hour, shared with agent key minting.
      operationId: createApiKey
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      parameters:
      - name: X-Org-Id
        in: header
        required: false
        schema:
          type: string
          format: uuid
        description: Exact selected organization. Requires current membership there; never falls back to the profile organization. Cannot be combined with context=personal.
      - name: context
        in: query
        schema:
          type: string
          enum:
          - personal
        description: Create for the JWT owner instead of the default organization context.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 100
                scopes:
                  type: array
                  items:
                    type: string
                  x-arkova-canonical-scopes:
                  - read:records
                  - read:orgs
                  - read:search
                  - write:anchors
                  - admin:rules
                  - verify
                  - verify:batch
                  - usage:read
                  - keys:manage
                  - compliance:read
                  - compliance:write
                  - oracle:read
                  - oracle:write
                  - anchor:write
                  - anchor:read
                  - attestations:write
                  - attestations:read
                  - webhooks:manage
                  - agents:manage
                  - keys:read
                  - orgs:manage
                  - agent_actions:write
                  - ai:extract
                assigned_user_id:
                  type:
                  - string
                  - 'null'
                  format: uuid
                  description: Assign the key to this current organization member. 400 assignee_not_org_member otherwise.
      responses:
        '201':
          description: API key created (raw key shown once)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyCreated'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          description: Key creation rate limit (10 per hour per admin) reached. Retry-After is set.
  /keys/scopes:
    get:
      summary: List grantable API key scopes
      description: 'Every scope the API accepts on key creation, with its label, description and status: active (enforced), alias (an alternate name for an active scope) or reserved (accepted, no effect today).'
      operationId: listApiKeyScopes
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      responses:
        '200':
          description: Scope catalog
          content:
            application/json:
              schema:
                type: object
                properties:
                  scopes:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        label:
                          type: string
                        description:
                          type: string
                        status:
                          type: string
                          enum:
                          - active
                          - alias
                          - reserved
        '401':
          $ref: '#/components/responses/Unauthorized'
  /keys/{keyId}:
    patch:
      summary: Update API key
      description: 'Update a key name, revoke it, or change its expiry. Version 1 member identity and workspace are immutable; revoke the existing key and authorize a new key to change the acting member. Add context=personal to manage only the JWT owner''s personal key, including after joining an organization or losing paid eligibility. Expiry is set with expires_in_days (a duration from now; null removes it) — expires_at is not settable. An expiry sent alongside a revoke (is_active: false) is dropped and the revoke is honoured; an expiry cannot be combined with reactivating a key.'
      operationId: updateApiKey
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      parameters:
      - name: X-Org-Id
        in: header
        required: false
        schema:
          type: string
          format: uuid
        description: Exact selected organization. Requires current membership there; never falls back to the profile organization. Cannot be combined with context=personal.
      - name: keyId
        in: path
        required: true
        schema:
          type: string
          format: uuid
      - name: context
        in: query
        schema:
          type: string
          enum:
          - personal
        description: Manage only the JWT owner's personal key.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                scopes:
                  type: array
                  items:
                    type: string
                  x-arkova-canonical-scopes:
                  - read:records
                  - read:orgs
                  - read:search
                  - write:anchors
                  - admin:rules
                  - verify
                  - verify:batch
                  - usage:read
                  - keys:manage
                  - compliance:read
                  - compliance:write
                  - oracle:read
                  - oracle:write
                  - anchor:write
                  - anchor:read
                  - attestations:write
                  - attestations:read
                  - webhooks:manage
                  - agents:manage
                  - keys:read
                  - orgs:manage
                  - agent_actions:write
                  - ai:extract
                expires_in_days:
                  type:
                  - integer
                  - 'null'
                  minimum: 1
                  maximum: 3650
                  description: Set the expiry this many days from now, or null to remove the expiry entirely. REPLACES any existing expiry — it does not add to it — so a value earlier than the current expiry is refused with 409 api_key_expiry_would_shorten unless allow_shorten is true.
                assigned_user_id:
                  type:
                  - string
                  - 'null'
                  format: uuid
                  description: 'UAT-21: reassign the key to another current organization member, or null to unassign it. 400 assignee_not_org_member otherwise.'
                allow_shorten:
                  type: boolean
                  default: false
                  description: Acknowledge that expires_in_days moves the expiry EARLIER (or gives an unexpiring key an expiry). Required for such a change; ignored otherwise.
      responses:
        '200':
          description: Key updated
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: api_key_already_revoked — revocation is terminal, so the key cannot be reactivated or extended; or api_key_expiry_would_shorten — the requested expiry is earlier than the current one and allow_shorten was not set; or api_key_changed — expiry or revocation changed concurrently, so refresh before retrying.
    delete:
      summary: Retire organization API key
      description: 'Organization admins retain the v1 DELETE 204 response: the key is terminally revoked while its metered usage remains for billing. Missing or already retired organization keys also return 204. Personal keys do not support DELETE (409); revoke one with PATCH /keys/{keyId}?context=personal and is_active: false.'
      operationId: revokeApiKey
      tags:
      - Key Management
      security:
      - SupabaseJWT: []
      parameters:
      - name: X-Org-Id
        in: header
        required: false
        schema:
          type: string
          format: uuid
        description: Exact selected organization. Requires current membership there; never falls back to the profile organization. Cannot be combined with context=personal.
      - name: keyId
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '204':
          description: Organization key retired (or already absent/revoked); no response body.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: 'api_key_delete_unavailable for personal context — revoke with PATCH is_active: false instead.'
components:
  schemas:
    MemberKeyClaimConflict:
      type: object
      required:
      - error
      properties:
        org_id:
          type: string
          format: uuid
        issuer_user_id:
          type: string
          format: uuid
        actor_user_id:
          type: string
          format: uuid
        claimed:
          type: boolean
          enum:
          - false
        idempotent:
          type: boolean
          enum:
          - true
        intent_id:
          type: string
          format: uuid
        key_id:
          type: string
          format: uuid
        rotation_receipt_id:
          type: string
          format: uuid
        principal_version:
          type: integer
          enum:
          - 1
        principal_kind:
          type: string
          enum:
          - human
        error:
          type: string
        message:
          type: string
    MemberKeyAuthorizationRequest:
      type: object
      additionalProperties: false
      required:
      - request_id
      - actor_user_id
      - name
      - scopes
      properties:
        request_id:
          type: string
          format: uuid
          description: Stable request UUID; changed-payload reuse returns 409.
        actor_user_id:
          type: string
          format: uuid
        name:
          type: string
          minLength: 1
          maxLength: 100
        scopes:
          type: array
          minItems: 1
          maxItems: 30
          items:
            type: string
            enum:
            - read:records
            - read:orgs
            - read:search
            - write:anchors
            - admin:rules
            - verify
            - verify:batch
            - usage:read
            - keys:manage
            - compliance:read
            - compliance:write
            - oracle:read
            - oracle:write
            - anchor:write
            - anchor:read
            - attestations:write
            - attestations:read
            - webhooks:manage
            - agents:manage
            - keys:read
            - orgs:manage
            - agent_actions:write
            - ai:extract
        expires_at:
          type:
          - string
          - 'null'
          format: date-time
          description: Optional absolute key expiry, validated at authorization and claim.
        rotate_key_id:
          type: string
          format: uuid
          description: Existing key for the same actor and organization to replace atomically at claim; creating authorization alone does not revoke it.
    ApiError:
      type: object
      required:
      - error
      properties:
        error:
          oneOf:
          - type: string
          - type: object
            additionalProperties: true
            properties:
              code:
                type: string
              message:
                type: string
        code:
          type: string
          description: Common machine-readable error code; legacy error remains unchanged.
        message:
          type: string
    ApiKeyMasked:
      type: object
      properties:
        id:
          type: string
          format: uuid
        key_prefix:
          type: string
        name:
          type: string
        scopes:
          type: array
          items:
            type: string
          x-arkova-canonical-scopes:
          - read:records
          - read:orgs
          - read:search
          - write:anchors
          - admin:rules
          - verify
          - verify:batch
          - usage:read
          - keys:manage
          - compliance:read
          - compliance:write
          - oracle:read
          - oracle:write
          - anchor:write
          - anchor:read
          - attestations:write
          - attestations:read
          - webhooks:manage
          - agents:manage
          - keys:read
          - orgs:manage
          - agent_actions:write
          - ai:extract
        is_active:
          type: boolean
        last_used_at:
          type:
          - string
          - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        expires_at:
          type:
          - string
          - 'null'
          format: date-time
        status:
          type: string
          enum:
          - active
          - expiring_soon
          - expired
          - revoked
          description: 'Server-derived usability. Prefer this over is_active/expires_at: is_active is a stored flag that stays true on an expired key, while authentication rejects it. expiring_soon means live and expiring within 14 days.'
        days_until_expiry:
          type:
          - integer
          - 'null'
          description: 'Whole days until expiry — 0 on the final day, negative once past, null when the key does not expire. Deliberately NOT named expires_in_days: that is the REQUEST field on POST/PATCH and means a duration to set, not a countdown to read.'
        principal_version:
          type:
          - integer
          - 'null'
          description: Version 1 binds a human actor immutably; version 0 retains legacy issuer identity.
        principal_kind:
          type:
          - string
          - 'null'
          enum:
          - legacy
          - human
          - null
        actor_user_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Immutable acting member for version 1 keys.
        issuer_user_id:
          type:
          - string
          - 'null'
          format: uuid
          description: Administrator who issued the key; audit provenance, not the member acting identity.
        assigned_user_id:
          type:
          - string
          - 'null'
          format: uuid
          description: The organization member this org-owned key is assigned to. Null when unassigned.
        assigned_user:
          type:
          - object
          - 'null'
          description: Display fields of the assignee (organization-internal). Null when unassigned.
          properties:
            id:
              type: string
              format: uuid
            full_name:
              type:
              - string
              - 'null'
            email:
              type:
              - string
              - 'null'
    ApiKeyCreated:
      type: object
      properties:
        id:
          type: string
          format: uuid
        key:
          type: string
          description: Shown only once — store it securely
        key_prefix:
          type: string
        name:
          type: string
        scopes:
          type: array
          items:
            type: string
          x-arkova-canonical-scopes:
          - read:records
          - read:orgs
          - read:search
          - write:anchors
          - admin:rules
          - verify
          - verify:batch
          - usage:read
          - keys:manage
          - compliance:read
          - compliance:write
          - oracle:read
          - oracle:write
          - anchor:write
          - anchor:read
          - attestations:write
          - attestations:read
          - webhooks:manage
          - agents:manage
          - keys:read
          - orgs:manage
          - agent_actions:write
          - ai:extract
        created_at:
          type: string
          format: date-time
    MemberKeyAuthorizationList:
      type: object
      required:
      - intents
      - can_manage
      - next_cursor
      properties:
        can_manage:
          type: boolean
        next_cursor:
          type:
          - string
          - 'null'
          format: uuid
        intents:
          type: array
          maxItems: 50
          items:
            type: object
            properties:
              org_id:
                type: string
                format: uuid
              issuer_user_id:
                type: string
                format: uuid
              actor_user_id:
                type: string
                format: uuid
              name:
                type: string
              scopes:
                type: array
                items:
                  type: string
              key_expires_at:
                type:
                - string
                - 'null'
                format: date-time
              expires_at:
                type: string
                format: date-time
              state:
                type: string
              old_key_id:
                type:
                - string
                - 'null'
                format: uuid
              id:
                type: string
                format: uuid
              created_at:
                type: string
                format: date-time
              claimed_key_id:
                type:
                - string
                - 'null'
                format: uuid
              claimed_at:
                type:
                - string
                - 'null'
                format: date-time
    MemberKeyClaimed:
      type: object
      required:
      - org_id
      - issuer_user_id
      - actor_user_id
      - claimed
      - idempotent
      - intent_id
      - key_id
      - rotation_receipt_id
      - principal_version
      - principal_kind
      - key
      - warning
      properties:
        org_id:
          type: string
          format: uuid
        issuer_user_id:
          type: string
          format: uuid
        actor_user_id:
          type: string
          format: uuid
        claimed:
          type: boolean
          enum:
          - true
        idempotent:
          type: boolean
          enum:
          - false
        intent_id:
          type: string
          format: uuid
        key_id:
          type: string
          format: uuid
        rotation_receipt_id:
          type: string
          format: uuid
        principal_version:
          type: integer
          enum:
          - 1
        principal_kind:
          type: string
          enum:
          - human
        key:
          type: string
          pattern: ^ak_live_v1_[a-f0-9]{64}$
          description: One-time private secret. Do not log or cache.
        warning:
          type: string
    MemberKeyAuthorizationReceipt:
      type: object
      required:
      - intent_id
      - org_id
      - issuer_user_id
      - actor_user_id
      - name
      - scopes
      - key_expires_at
      - expires_at
      - state
      - old_key_id
      - idempotent
      properties:
        org_id:
          type: string
          format: uuid
        issuer_user_id:
          type: string
          format: uuid
        actor_user_id:
          type: string
          format: uuid
        name:
          type: string
        scopes:
          type: array
          items:
            type: string
        key_expires_at:
          type:
          - string
          - 'null'
          format: date-time
        expires_at:
          type: string
          format: date-time
        state:
          type: string
          enum:
          - pending
        old_key_id:
          type:
          - string
          - 'null'
          format: uuid
        intent_id:
          type: string
          format: uuid
        idempotent:
          type: boolean
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      description: 'API key as Bearer token: `Authorization: Bearer ak_live_...`'
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'API key via header: `X-API-Key: ak_live_...`'
    SupabaseJWT:
      type: http
      scheme: bearer
      description: Supabase JWT for key management endpoints