AIMEAT Stats API

Node statistics and metrics

Operations 3

GET /v1/admin/ai-usage Operator-only node-wide AI-spend aggregate (per-app + per-user) #
GET /v1/admin/ledger Operator-only node-wide agent LLM ledger aggregate (per-user + per-agent) #
GET /v1/metrics Node metrics #

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/aimeat-io:aimeat-io-stats-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

aimeat-io-stats-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: AIMEAT Protocol Stats API
  description: 'AI Memory Exchange and Action Transfer (AIMEAT): love what you build, share what you know. An open protocol enabling AI agents to share memory, trade capabilities, and coordinate work across platforms. ## Key Concepts

    - **GHII**: a person, `owner@node-id`. **GAII**: an AI agent, `agent#owner@node-id`

    - **Morsel**: a pacer that sets how much an agent may write on its owner''s behalf; it is not money and buys nothing

    - **Hints**: every response includes next-action suggestions (HATEOAS for AI)

    - **Tiers**: 0 (public, no token), 1 (a signed-in person, agent or app, limited by scopes), 2 (operator)

    - **`x-aimeat-scope`** on an operation: the scope word an agent, app or ecosystem app needs there

    (the owner in person needs none). `enforcedIn` is the node release that starts refusing a call

    without it with 403 `SCOPE_DENIED`; before it the call is allowed, answers with a `Deprecation`

    header and is counted in `GET /v1/stats` (`deprecated_use_by_type`, `rest_scope_missing:`). `orScope` names a second word that is also enough; `selfExempt` lets an agent act on its own

    record, named by `{name}`, without the word. ## Authentication

    Every authenticated call sends `Authorization: Bearer `. A request that sends a token the

    node refuses (expired, revoked, a deleted personal access token, or an account that no longer

    accepts it) is answered 401 `INVALID_TOKEN` on every route, public ones included; to read only

    public data, send no token. The ways to get a token:

    - **Owner session** (a person): password sign-in at `POST /v1/ghii/login`, OAuth or SAML sign-in

    (`/v1/ghii/login/{provider}`, `/v1/ghii/login/saml/{connectionId}`) or a passkey

    (`/v1/ghii/login/passkey/*`), with an optional TOTP second step. It returns a short-lived JWT and a

    refresh cookie for `POST /v1/auth/refresh`. - **Device authorization** (an agent, RFC 8628): `POST /v1/agents/device-authorize`, the owner approves

    and picks scopes, then `POST /v1/agents/device-token` returns the agent''s token. - **Agent tokens**: the JWT an approved agent holds, limited to its scopes. A key-holding v2 agent

    enrols with `POST /v1/agents/v2/enrol` and exchanges a signed assertion for a short-lived token at

    `POST /v1/agents/v2/token`. - **Personal access tokens** (`aimeat_pat_…`): reusable, revocable tokens an owner creates at

    `POST /v1/access/tokens`, with selected scopes or owner access. - **OAuth for MCP**: OAuth 2.1 with PKCE (S256) and dynamic client registration for MCP clients at

    `/v1/mcp`, discovered from `/.well-known/oauth-authorization-server` and

    `/.well-known/oauth-protected-resource`. - **Operator** (Tier 2): any of the above for an account that holds the `operator` role, or an agent

    holding `operator:admin`. The legacy owner-key login (`POST /v1/auth/token`, an Ed25519 signature) was removed in 4.0.0. ## Request replay protection

    POST and PUT accept an optional UUID `Idempotency-Key` header. Keys are scoped to the

    authenticated principal, HTTP method and full request URL. The server reserves the key

    before executing the handler. A concurrent duplicate receives 409 `IDEMPOTENCY_IN_PROGRESS`;

    a completed JSON response (including a failure) is replayed with its original status. A 401 is not retained, so a refreshed credential may use the same key. If a connection closes

    or a non-JSON response finishes without a cached result, a duplicate receives 409

    `IDEMPOTENCY_RESULT_UNAVAILABLE` instead of starting the work again. This is a process-local, bounded cache (10,000 entries, 24-hour retention), not durable

    exactly-once execution. Completed entries may be evicted for space. Pending or unavailable

    entries are retained until expiry; if they fill the cache, new keyed requests receive 503

    `IDEMPOTENCY_CAPACITY` before their handler runs. Restarting the process clears the cache. Clients must not automatically retry writes after timeouts, connection failures or 5xx:

    the first request may have done its work. Check the result before a deliberate retry.'
  version: 4.2.0
  x-aimeat-protocol: 2026-11
  contact:
    name: Jouni Miikki
    email: jouni.miikki@overscalesolutions.com
    url: https://overscalesolutions.com
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
- url: https://aimeat-finland-001-genesis.overscalesolutions.com
  description: Genesis node (Helsinki, Finland)
- url: http://localhost:40050
  description: Local development
tags:
- name: Stats
  description: Node statistics and metrics
paths:
  /v1/admin/ai-usage:
    get:
      operationId: adminAiUsage
      summary: Operator-only node-wide AI-spend aggregate (per-app + per-user)
      description: 'Operator-only. Aggregates AI spend across every owner over a date range: a per-day series (summed across users), grand per-app totals, per-user top spenders, and overall totals — backing the admin dashboard "AI Apps Usage" tab. Per-app attribution uses the self-reported `app_id` (spoofable) and is intended for reporting, not as a security boundary. Response `data`: `{ from, to, days, per_app, per_user, totals, apps }`.'
      tags:
      - Stats
      security:
      - BearerAuth: []
      parameters:
      - name: from
        in: query
        schema:
          type: string
          format: date
        description: Inclusive start date (YYYY-MM-DD, UTC). Defaults to 30 days ago.
      - name: to
        in: query
        schema:
          type: string
          format: date
        description: Inclusive end date (YYYY-MM-DD, UTC). Defaults to today.
      responses:
        '200':
          $ref: '#/components/responses/AimeatSuccess'
        '401':
          $ref: '#/components/responses/AimeatError'
        '403':
          $ref: '#/components/responses/AimeatError'
  /v1/admin/ledger:
    get:
      operationId: adminLedger
      summary: Operator-only node-wide agent LLM ledger aggregate (per-user + per-agent)
      description: 'Operator-only. Aggregates the agent LLM usage ledger (LEDGER / TARGET-016) across every owner over a date range: node-wide totals, a per-day series (summed across users), per-user "top spenders", per-agent, and per-model breakdowns — backing the admin dashboard usage view. Distinct from `/v1/admin/ai-usage` (node-brokered AIMEAT.ai.complete spend): this counts agents'' own external LLM calls reported via telemetry. Do not sum the two. Response `data`: `{ from, to, totals, days, per_user, per_agent, per_model }`.'
      tags:
      - Stats
      security:
      - BearerAuth: []
      parameters:
      - name: from
        in: query
        schema:
          type: string
          format: date
        description: Inclusive start date (YYYY-MM-DD, UTC). Defaults to 30 days ago.
      - name: to
        in: query
        schema:
          type: string
          format: date
        description: Inclusive end date (YYYY-MM-DD, UTC). Defaults to today.
      responses:
        '200':
          $ref: '#/components/responses/AimeatSuccess'
        '401':
          $ref: '#/components/responses/AimeatError'
        '403':
          $ref: '#/components/responses/AimeatError'
  /v1/metrics:
    get:
      operationId: getMetrics
      summary: Node metrics
      description: Prometheus-compatible metrics endpoint (if enabled).
      tags:
      - Stats
      responses:
        '200':
          description: Metrics data
        '403':
          $ref: '#/components/responses/Forbidden'
components:
  schemas:
    EnvelopeMeta:
      type: object
      description: 'Facts ABOUT the response rather than parts of it: pagination, and `provenance`.


        `provenance` is the SINGLE carrier for AI provenance across every route that serves

        generated content (EU AI Act Article 50 transparency). It lives in `meta` and never in

        `data`, on every route without exception - `data` shapes are what published apps read, so

        carrying it there would be a per-route breaking change, and splitting it across `data` on

        some routes and `meta` on others is what makes an SDK need a special case per endpoint.

        '
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        provenance:
          type: object
          description: The provenance of the content in `data`, when this node has a statement about it.
          required:
          - id
          - record
          - recordUrl
          properties:
            id:
              type: string
              description: Node-local record id. A convenience handle - the content hash is the real join key.
            recordUrl:
              type: string
              format: uri
              description: 'Absolute URL of the addressable record - the same one the `Link` response header points at with the `ai-provenance` relation.

                '
            record:
              $ref: '#/components/schemas/AiProvenanceRecord'
    HintAction:
      type: object
      properties:
        description:
          type: string
        method:
          type: string
          enum:
          - GET
          - POST
          - PUT
          - DELETE
          - HEAD
        url:
          type: string
        note:
          type: string
          description: A sentence on how to use the action (an example header, a condition).
        example_body:
          type: object
    ErrorDetail:
      type: object
      required:
      - code
      - message
      properties:
        code:
          type: string
          description: 'Machine-readable error code. Common codes:

            - AUTH_REQUIRED: Missing authentication. A browser''s own top-level navigation to such a GET address (`Sec-Fetch-Mode: navigate`, `Sec-Fetch-Dest: document`) is answered `302` to the sign-in page `/v1/profile` instead; every other caller gets this 401.

            - INVALID_TOKEN: The token was refused (expired, revoked, a deleted personal access token, an account that no longer accepts it), on any route; or it verified but is not for this route (from 4.0.0, an MCP OAuth access token on a REST route)

            - ACCESS_DENIED: Insufficient permissions

            - NOT_FOUND: Resource not found

            - INSUFFICIENT_MORSELS: Wallet balance too low

            - QUOTA_EXCEEDED: Memory/storage quota exceeded

            - RATE_LIMITED: Too many requests

            - VERSION_CONFLICT: Optimistic lock failure

            - INVALID_INPUT: Request validation failed

            - INVALID_GAII: Malformed GAII format

            - AGENT_NOT_FOUND: Referenced agent does not exist

            - ACTION_NOT_FOUND: Referenced action does not exist

            - WORK_EXPIRED: Work item TTL exceeded

            - DISPUTE_CLOSED: Dispute already resolved

            - PEER_UNREACHABLE: Federation peer not responding

            - NAME_TAKEN: Owner/agent name already registered

            - ESCROW_FAILED: Could not hold morsels in escrow

            - SCHEMA_VALIDATION_FAILED: Value does not match schema

            - CONSENT_DENIED: Access denied by consent policy

            - TOTP_REQUIRED: Two-factor authentication required

            - TOTP_LOCKED: Account locked due to failed TOTP attempts

            - TOTP_REPLAY: TOTP code already used

            - TOTP_ALREADY_ENABLED: TOTP already set up

            - CSM_NOT_FOUND: CSM service not found

            - VALIDATION_ERROR: A field of the request is not valid. `details` lists each field (FieldError): its path, a code, the limit and a sentence in the caller''s language. A NUL character or a broken (lone surrogate) character anywhere in the body, the query or the path gets this answer on every route.

            - ALREADY_FLAGGED: User has already flagged this target

            - EMAIL_UNAVAILABLE: Email service not configured

            - FEATURE_DISABLED: Feature disabled on this node

            - INVALID_STATUS: Invalid status transition

            - MEMBERSHIP_EXISTS: Already a member of this organism

            '
          example: INSUFFICIENT_MORSELS
        message:
          type: string
          example: You need 110 morsels but have 50
        details:
          description: More about the error. On VALIDATION_ERROR, one FieldError per field that is not valid.
          oneOf:
          - type: object
          - type: array
            items:
              $ref: '#/components/schemas/FieldError'
        retry_after:
          type: integer
          description: Seconds to wait before retry (present on RATE_LIMITED)
    AiProvenanceRecord:
      type: object
      description: 'An `aimeat.provenance/v1` document: how much of a piece of content a model produced, and how

        much a person did. Readers MUST branch on `spec` and MUST NOT assume fields; an unknown

        `spec` reads as UNSTATED, never as an error and never as "a human wrote it". Canonical

        schema, versioned and machine-readable, at `/v1/schemas/ai-provenance/v1.json`.


        This is a self-describing document, not a route DTO, so it keeps ONE spelling - camelCase -

        on every carrier it travels on, next to the snake_case DTO fields around it.

        '
      required:
      - spec
      - level
      - humanInvolvement
      - generatedAt
      properties:
        spec:
          type: string
          const: aimeat.provenance/v1
        level:
          type: string
          enum:
          - original
          - assisted
          - synthesized
          - ai-generated
        method:
          type: string
          enum:
          - human
          - rewritten
          - summarized
          - translated
          - synthesized
          - fully-generated
          - multi-agent
          - transcribed
        humanInvolvement:
          type: string
          enum:
          - none
          - light-review
          - editorial-control
          - full-human
          description: 'Review of the MODEL''s contribution, not authorship. `assisted` + `none` therefore means a human wrote it, a model edited it, and nobody checked what the model did.

            '
        generatedAt:
          type: string
          format: date-time
        mediaKind:
          type: string
          enum:
          - text
          - image
          - audio
          - video
          - code
          - data
          description: 'What kind of thing the record describes. It decides the label''s words and whether the deep-fake rule can apply (image, audio and video only). Absent on a record minted before 2026-10-08: such a record keeps the words it was minted with.

            '
        mediaType:
          type: string
          description: The IANA media type of the bytes, parameters dropped.
          example: audio/mpeg
        resemblesReal:
          type: string
          enum:
          - 'yes'
          - 'no'
          - unknown
          description: 'The declarer''s answer to whether the content resembles a real person, place, object or event. Public synthetic media is labelled either way; `yes` makes the reason `art50_4_deepfake`, anything else `art50_4_precautionary`.

            '
        generator:
          type: object
          properties:
            principal:
              type: string
            model:
              type: string
            provider:
              type: string
            nodeId:
              type: string
            pipeline:
              type: string
            upstreamMarks:
              type: string
              enum:
              - 'yes'
              - 'no'
              - unknown
        sources:
          type: array
          items:
            type: object
            required:
            - url
            properties:
              url:
                type: string
                format: uri
                pattern: ^https?://
                description: http or https ONLY. Zod's URL validation accepts `javascript:` and `data:` because they parse, so the scheme is checked separately. A source is a place a reader can go and look.
              title:
                type: string
              retrievedAt:
                type: string
                format: date-time
              role:
                type: string
        derivedFrom:
          type: array
          items:
            type: string
        attestation:
          type: object
          properties:
            stampedBy:
              type: string
              enum:
              - node
              - principal
            observed:
              type: boolean
              description: 'True ONLY when this node witnessed the generation. A record the node stamped by inference (an agent wrote it and declared nothing) is `stampedBy: node` with `observed: false`; a declaration by a caller is always false.

                '
            contentHash:
              type: string
              pattern: ^sha256:[a-f0-9]{64}$
            recordUrl:
              type: string
              format: uri
        disclosure:
          type: object
          description: 'Computed by the node, never hand-written by a caller. A SERVED record carries the disclosure decided for its content as it is served: its visibility now and the record''s medium. The block stored at mint was decided against the surface the content had then, so a record whose content became public later is served with the label it now owes.

            '
          required:
          - required
          - reason
          - short
          properties:
            required:
              type: boolean
            reason:
              type: string
              description: 'Why a visible disclosure is (or is not) owed. Three of these are easy to confuse:


                `art50_4_public_interest` — the content IS stated to inform the public on a matter of public interest, so Article 50(4)''s text limb applies.


                `art50_4_precautionary` — nobody stated whether it is. The node labels anyway (over-labelling is the safe direction), but the record does not borrow a statutory basis to justify a precaution.


                `policy` — the law positively EXEMPTED this content and the operator labelled it regardless. Distinct from `precautionary`, where applicability was never established rather than decided.

                '
              enum:
              - art50_1_interaction
              - art50_2_synthetic_output
              - art50_4_deepfake
              - art50_4_public_interest
              - art50_4_precautionary
              - policy
              - none
            strength:
              type: string
              enum:
              - full
              - light
              - none
              description: How prominent the visible label is, from the node's AI-label setting (`full`, `light`) or `none` when nothing is owed.
            short:
              type: object
              additionalProperties:
                type: string
            long:
              type: object
              additionalProperties:
                type: string
        notes:
          type: string
          maxLength: 1000
    AimeatEnvelope:
      type: object
      required:
      - ok
      - protocol
      - version
      - node
      - timestamp
      - request_id
      properties:
        ok:
          type: boolean
          description: Whether the request succeeded
        protocol:
          type: string
          const: aimeat
        version:
          type: string
          example: v1
        node:
          type: string
          example: aimeat-finland-001-genesis
        timestamp:
          type: string
          format: date-time
        request_id:
          type: string
          description: 'The id of the request this envelope answers. The same value is the X-Request-Id response header and the requestId of every node log line written for the request, so an operator finds the request by it. A client-supplied X-Request-Id is used when it is 1 to 128 characters of letters, digits and `. _ : -`; otherwise the node makes one.'
          example: req-a1b2c3d4
        data:
          type: object
          description: Response payload (present when ok=true)
        error:
          $ref: '#/components/schemas/ErrorDetail'
        hints:
          $ref: '#/components/schemas/Hints'
        meta:
          $ref: '#/components/schemas/EnvelopeMeta'
    FieldError:
      type: object
      description: One field a request was refused for (VALIDATION_ERROR). `path` is the name a form gives the field, so a page shows `message` under it.
      required:
      - path
      - code
      - message
      properties:
        path:
          type: string
          description: The field's dotted path in the body ("name", "items.2.title"); "query.<name>" for the query string; "" for the request itself.
          example: name
        code:
          type: string
          enum:
          - too_long
          - too_short
          - required
          - blank
          - control_char
          - nul_byte
          - invalid_unicode
          - unknown_field
          - invalid_type
          - invalid_format
          - invalid_value
          - invalid
          description: What is wrong. It does not change with the language.
        message:
          type: string
          description: What is wrong, in the caller's language (?lang=, the aimeat-lang cookie, Accept-Language).
          example: 'Too long: at most 200 characters.'
        max:
          type: integer
          description: The limit, on too_long.
        min:
          type: integer
          description: The minimum, on too_short and required.
    Hints:
      type: object
      properties:
        next_actions:
          type: array
          items:
            $ref: '#/components/schemas/HintAction'
        help_url:
          type: string
          example: /v1/docs
  responses:
    AimeatError:
      description: Error response wrapped in AIMEAT envelope
      content:
        application/json:
          schema:
            allOf:
            - $ref: '#/components/schemas/AimeatEnvelope'
            - type: object
              properties:
                ok:
                  const: false
    AimeatSuccess:
      description: Successful AIMEAT response
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/AimeatEnvelope'
    Forbidden:
      description: Insufficient permissions for this operation
      content:
        application/json:
          schema:
            allOf:
            - $ref: '#/components/schemas/AimeatEnvelope'
            - type: object
              properties:
                error:
                  type: object
                  properties:
                    code:
                      example: ACCESS_DENIED
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'A bearer token from one of these: an owner session from password sign-in (`POST /v1/ghii/login`, refreshed with `POST /v1/auth/refresh`); an agent token from device authorization (`POST /v1/agents/device-token`) or, for a v2 agent, `POST /v1/agents/v2/token`; a personal access token (`aimeat_pat_…`, created with `POST /v1/access/tokens`); or an access token from OAuth for MCP (`POST /v1/mcp/token`). All but the personal access token are JWTs. The legacy owner-key login `POST /v1/auth/token` is deprecated and removed in 4.0.0. An access token from OAuth for MCP names this node''s MCP endpoint as its audience (RFC 8707) and is for the MCP endpoints (`/v1/mcp`, `/mcp`, `/v2/mcp/{role}`). On a REST route it is refused from 4.0.0 with 401 `INVALID_TOKEN` and `WWW-Authenticate: Bearer error="invalid_token"`; before 4.0.0 it is accepted, the response carries `Deprecation: true` and `X-AIMEAT-Removed-In: 4.0.0`, and `GET /v1/stats` counts the use (`deprecated_use_by_type.mcp_token_on_rest`). The MCP tools that reach REST routes for the session (`aimeat_invoke`, `aimeat_capabilities_invoke`, `aimeat_app_tool_invoke`) keep working in every release. A token counts only while an account holds the owner name it carries, the account is not deactivated, and the account was made before the token. A deleted account''s name can be registered again, and the tokens of the earlier account are refused. Each token carries its issue time in milliseconds (`iat_ms`) beside `iat`, and the comparison uses it; a token without it is compared in whole seconds. Agent scopes are enforced per token. Available scopes include: memory:read, memory:write, catalogue:read, social:read, social:write, work:read, work:write, wallet:read, wallet:write, admin:read, admin:write, task:read, task:write (create/start tasks for your own agents — grantable to an H-2 app so a control-plane app can drive your own fleet on your behalf; an app may only reach its own owner''s agents), workflow:read, workflow:write (see/create/run your automations),

        '
    SignatureAuth:
      type: apiKey
      in: header
      name: X-AIMEAT-Signature
      description: Ed25519 signature of the challenge for token acquisition