# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/quotas.yaml (runtime 0.71.2)
#  + policy: openapi/mirror-policies/quotas.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — Quotas API
  version: 1.0.0
  description: >-
    Quotas: the ceilings a project runs under — requests, tokens or cost over a rolling window or a
    calendar month, scoped to the whole project or to one key, agent or end user, and stored bytes
    against the project's whole footprint. A quota either enforces or only watches, so a limit can
    be measured before it bites. A fully runtime-backed module — this spec is generated verbatim
    from the runtime's own, re-rooted under /v1/projects/{project_id}. The project in the path is
    authorized by naturali and enforced upstream by the project's scoped credential.


    This module mirrors the upstream runtime verbatim (tier A, #304): paths are the runtime's own
    re-rooted under /v1/projects/{project_id}, and every field, method, status code and error shape
    passes through unchanged. Errors raised by the runtime arrive in its envelope; errors raised by
    naturali itself (authentication, project resolution, an unreachable runtime) use naturali's.
  contact:
    name: naturali.ai
    url: https://naturali.ai
servers:
  - url: "{baseUrl}"
    description: Host of your naturali.ai deployment; every path carries the /v1 prefix.
    variables:
      baseUrl:
        description: Base host URL.
        default: https://api.naturali.ai
tags:
  - name: Quotas
    description: Manage quotas and rate limits
security:
  - bearerAuth: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/quotas:
    get:
      tags:
        - Quotas
      summary: List quotas
      description: Returns the quotas defined in a project
      operationId: listQuotas
      parameters:
        - name: limit
          in: query
          required: false
          description: Maximum number of results to return
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          required: false
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of quotas
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Quota"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "500":
          description: Internal server error
    post:
      tags:
        - Quotas
      summary: Create a quota
      description: >-
        Creates a project-scoped quota. `requests` is valid for `scope: project`/`api_key`; `tokens`
        and `cost_usd` are valid for `scope: project`/`agent`/`actor`; `storage_bytes` is valid for
        `scope: project` only. Any other scope/metric pair is rejected with 400 (no attribution
        exists to enforce it). An `actor` quota caps one end user's spend, matched from the
        generation's session; a null `scope_ref` means one budget *per* actor rather than a pooled
        project total. A `cost_usd` quota may name one `meter_type` to cap; omitting it caps every
        priced meter. A duplicate quota (same project, scope, scope_ref, metric, window, meter_type)
        is rejected with 409.


        `storage_bytes` caps a stored total rather than a windowed one, so it takes `window:
        current` and every other metric refuses that value (400 either way). It is enforced at the
        corpus write paths — file upload and create, document create, document ingest and re-ingest,
        memory create — with `409 QUOTA_STORAGE_EXCEEDED` and no `Retry-After`, since no window
        reset clears a footprint.
      operationId: createQuota
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - scope
                - metric
                - window
                - limit
              additionalProperties: false
              properties:
                scope:
                  type: string
                  enum:
                    - project
                    - api_key
                    - agent
                    - actor
                  description: The scope the quota applies to
                scope_ref:
                  type: string
                  nullable: true
                  description: Public id of the api key / agent / actor the quota applies to. For `api_key` and
                    `agent` scope, NULL means all entities of that scope type in the project. For
                    `actor` scope, NULL means one budget *per* actor — each end user gets their own
                    allowance — rather than a pooled total across all actors.
                  example: key_V1StGXR8Z5jdHi6B
                metric:
                  type: string
                  enum:
                    - requests
                    - tokens
                    - cost_usd
                    - storage_bytes
                  description: The metric being capped
                  example: requests
                window:
                  type: string
                  enum:
                    - rolling_1m
                    - rolling_1h
                    - rolling_24h
                    - calendar_month
                    - current
                  description: The window over which the metric is aggregated. `current` is the only accepted value
                    for storage_bytes (a stored total is not aggregated over time) and is refused
                    for every other metric.
                limit:
                  type: number
                  description: The cap. Must be a positive integer for requests/tokens/storage_bytes (bytes);
                    fractional values are allowed for cost_usd.
                  example: 600
                mode:
                  type: string
                  enum:
                    - enforce
                    - monitor
                  default: enforce
                  description: enforce blocks with 429 (requests at the middleware, tokens/cost_usd at the
                    pre-generation check); monitor observes without blocking — a breach fires the
                    quota.exceeded webhook and writes a quotas:MonitorBreach audit entry, but the
                    request is let through.
                  example: enforce
                on_unpriced:
                  type: string
                  enum:
                    - block
                    - allow
                  default: block
                  description: "Only for metric cost_usd (400 on any other metric). What an enforce quota does when
                    the current window is a pricing blackout — several metered llm_tokens events,
                    none of them priced, so the aggregate is 0 however much was actually spent.
                    Platform meters such as compute_execution are read for the aggregate but never
                    for this verdict. block (the default) refuses new generations with 409
                    QUOTA_UNENFORCEABLE until pricing is configured; allow accepts the unmeasurable
                    spend explicitly. Either way a quota_unpriced exception is filed. monitor-mode
                    quotas never block regardless. A partly priced window is not a blackout: no
                    posture refuses it, it is enforced on its priced total, and it files the same
                    exception."
                  example: block
                meter_type:
                  type: string
                  enum:
                    - llm_tokens
                    - compute_execution
                    - api_request
                    - storage
                    - tool_execution
                  description: Only for metric cost_usd (400 on any other metric). The meter this cap answers for.
                    Omit it and the cap sums every priced meter, which is the existing behaviour;
                    name one and only that meter's cost counts, so an AI spend cap is not consumed
                    by platform meters the operator prices (and vice versa). Part of the quota's
                    identity, so two meter scopes can share a scope/metric/window and neither
                    conflicts with an unscoped cap. Immutable after creation — replace the quota to
                    change it.
      responses:
        "201":
          description: Quota created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Quota"
        "400":
          description: Bad request (invalid scope/metric/window/mode/limit)
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "409":
          description: A matching quota already exists
        "500":
          description: Internal server error
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/quotas/{quota_id}:
    get:
      tags:
        - Quotas
      summary: Get a quota
      description: Returns a specific quota, including current window usage
      operationId: getQuota
      x-naturali-resource:
        kind: quota
        from: quota_id
      parameters:
        - name: quota_id
          in: path
          required: true
          description: Quota ID
          schema:
            type: string
            example: quota_V1StGXR8Z5jdHi6B
      responses:
        "200":
          description: Quota details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Quota"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Quota not found
    patch:
      tags:
        - Quotas
      summary: Update a quota
      description: Updates a quota's limit and/or mode. Other fields are immutable.
      operationId: updateQuota
      x-naturali-resource:
        kind: quota
        from: quota_id
      parameters:
        - name: quota_id
          in: path
          required: true
          description: Quota ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                limit:
                  type: number
                  description: New limit
                  example: 1000
                mode:
                  type: string
                  enum:
                    - enforce
                    - monitor
                  description: New mode
                  example: monitor
                on_unpriced:
                  type: string
                  enum:
                    - block
                    - allow
                  description: New pricing posture. Only for metric cost_usd (400 on any other metric); see the create
                    operation for what block and allow mean.
                  example: allow
      responses:
        "200":
          description: Quota updated successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Quota"
        "400":
          description: Bad request
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Quota not found
    delete:
      tags:
        - Quotas
      summary: Delete a quota
      description: Deletes a quota and drops its window counters
      operationId: deleteQuota
      x-naturali-resource:
        kind: quota
        from: quota_id
      parameters:
        - name: quota_id
          in: path
          required: true
          description: Quota ID
          schema:
            type: string
      responses:
        "204":
          description: Quota deleted successfully
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Quota not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  schemas:
    Quota:
      type: object
      properties:
        id:
          type: string
          example: quota_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
        scope:
          type: string
          enum:
            - project
            - api_key
            - agent
            - actor
        scope_ref:
          type: string
          nullable: true
          description: Public id of the api key / agent / actor the quota applies to. For `api_key` and
            `agent` scope, NULL means all entities of that scope type in the project. For `actor`
            scope, NULL means one budget *per* actor rather than a pooled total across all actors.
        metric:
          type: string
          enum:
            - requests
            - tokens
            - cost_usd
            - storage_bytes
        window:
          type: string
          enum:
            - rolling_1m
            - rolling_1h
            - rolling_24h
            - calendar_month
            - current
          description: The window the metric is aggregated over; `current` on storage_bytes, which caps a
            stored total and never resets.
        limit:
          type: number
        mode:
          type: string
          enum:
            - enforce
            - monitor
        meter_type:
          type: string
          enum:
            - llm_tokens
            - compute_execution
            - api_request
            - storage
            - tool_execution
            - null
          nullable: true
          description: The meter a cost_usd cap answers for. Null is every priced meter, which is what a quota
            created without one carries.
        on_unpriced:
          type: string
          enum:
            - block
            - allow
            - null
          nullable: true
          description: Pricing posture of a cost_usd quota over an unpriced blackout — block refuses
            generations, allow lets them through (the quota_unpriced exception is filed either way,
            and for a partly priced window, which no posture refuses). Null for metrics with no
            pricing dependency.
        current_usage:
          type: object
          nullable: true
          description: Current fixed-window usage for the requests metric. Null for token/cost quotas (which
            aggregate the usage meter at check time rather than keeping a counter), null for
            storage_bytes (a stored total has no window and no counter — read the footprint from the
            storage meter), and null in list responses.
          properties:
            window_key:
              type: string
              example: 2026-07-07T12:31Z
            count:
              type: integer
              example: 42
            resets_at:
              type: string
              format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A naturali API key (nat_sk_…) or a session JWT.
    oauth2:
      type: oauth2
      description: "A connected app's OAuth access token, issued by this API's authorization server
        (discovery: /.well-known/oauth-authorization-server). Its one scope carries every operation,
        confined to the projects the user chose when approving the app."
      flows:
        authorizationCode:
          authorizationUrl: https://api.naturali.ai/authorize
          tokenUrl: https://api.naturali.ai/token
          refreshUrl: https://api.naturali.ai/token
          scopes:
            mcp:access: Every operation this API serves, on the projects the grant covers.
