openapi: 3.1.0
info:
  title: Modei API
  description: |
    Trust infrastructure for AI agents — Passports (identity), Gates (policy enforcement),
    Attestations (tamper-proof audit trails), and Commerce (agent-to-agent transactions).

    See https://modei.ai/docs for the full documentation.
  version: "1.0"
  contact:
    name: Modei
    url: https://modei.ai
  license:
    name: Proprietary
    url: https://modei.ai/terms

servers:
  - url: https://modei.ai/api/v1
    description: Production

security:
  - bearerAuth: []

tags:
  - name: Discovery
    description: Public endpoints for agents to discover services. No authentication required.
  - name: Passports
    description: Issue, verify, and manage agent credentials.
  - name: Gates
    description: Create and configure policy enforcement checkpoints.
  - name: Catalogs
    description: Signed, versioned Permission Catalogs published by Gates.
  - name: Enforcement
    description: Backend endpoints called by resource servers, not by agents directly.
  - name: Audit
    description: List and export signed Attestations (audit trail).

paths:
  /discover:
    get:
      tags: [Discovery]
      summary: Discover Gates offering a capability
      description: |
        Public endpoint for agents to find services before they have passports.
        Each result includes catalog_signature, catalog_content_hash, and signing_key_id
        so agents can verify results locally (trustless discovery).
      security: []
      parameters:
        - name: capability
          in: query
          required: true
          schema: { type: string }
          description: The permission_key or capability to search for.
        - name: max_price_cents
          in: query
          schema: { type: integer }
        - name: min_uptime_bp
          in: query
          schema: { type: integer, description: "Basis points (10000 = 100%)" }
        - name: max_response_time_ms
          in: query
          schema: { type: integer }
        - name: pricing_model
          in: query
          schema:
            type: string
            enum: [per_call, per_minute, subscription]
        - name: currency
          in: query
          schema: { type: string, default: USD }
        - name: min_security_profile
          in: query
          schema:
            type: string
            enum: [L1, L2, L3]
        - name: sort
          in: query
          schema:
            type: string
            enum: [price_asc, price_desc, uptime_desc, response_asc]
      responses:
        "200":
          description: Discovery results
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items: { $ref: "#/components/schemas/DiscoveryResult" }
                  total: { type: integer }
                  query: { type: object, additionalProperties: true }
        "400":
          description: Missing or invalid query parameters

  /gates/{gate_id}/catalog:
    get:
      tags: [Catalogs]
      summary: Get signed Permission Catalog for a Gate
      description: |
        Returns the latest published, signed, versioned catalog. Agents verify the
        signature against the issuer's public key before trusting the pricing and
        permissions.
      security: []
      parameters:
        - name: gate_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: The signed Permission Catalog
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PermissionCatalog" }
        "404":
          description: Gate not found or no published catalog

  /gates/{gate_id}/check:
    post:
      tags: [Enforcement]
      summary: Verify a passport against a gate (resource-server backend)
      description: |
        Called by resource servers on the backend — NOT by agents directly. Verifies
        that the passport is valid, not revoked, and matches the gate's constraints.
      parameters:
        - name: gate_id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [passport, request]
              properties:
                passport: { type: string, description: "Signed passport JWT" }
                request:
                  type: object
                  description: "Request context — permission_key, amount, etc."
                  additionalProperties: true
      responses:
        "200":
          description: Enforcement decision
          content:
            application/json:
              schema: { $ref: "#/components/schemas/EnforcementDecision" }

  /enforce:
    post:
      tags: [Enforcement]
      summary: Passport-only enforcement (resource-server backend)
      description: |
        Like /gates/:id/check but without requiring a gate_id. Used when the
        resource server maps requests to gates internally.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [passport, request]
              properties:
                passport: { type: string }
                request:
                  type: object
                  additionalProperties: true
      responses:
        "200":
          description: Enforcement decision
          content:
            application/json:
              schema: { $ref: "#/components/schemas/EnforcementDecision" }

  /passports:
    post:
      tags: [Passports]
      summary: Issue a Passport
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [issuer_id, subject, permissions]
              properties:
                issuer_id: { type: string }
                subject:
                  type: object
                  description: Identity of the agent (model, version, operator, etc.)
                  additionalProperties: true
                permissions:
                  type: array
                  items: { type: string }
                constraints:
                  type: object
                  description: Spend caps, rate limits, domain allowlists, etc.
                  additionalProperties: true
                expires_at:
                  type: string
                  format: date-time
      responses:
        "201":
          description: Passport issued
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Passport" }

  /passports/{passport_id}:
    get:
      tags: [Passports]
      summary: Get a Passport
      parameters:
        - name: passport_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: The Passport record
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Passport" }
        "404":
          description: Not found

  /passports/{passport_id}/verify:
    get:
      tags: [Passports]
      summary: Verify a Passport
      description: Returns whether the passport is valid, not revoked, and not expired.
      parameters:
        - name: passport_id
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  valid: { type: boolean }
                  revoked: { type: boolean }
                  expired: { type: boolean }
                  reason: { type: string, nullable: true }

  /gates:
    post:
      tags: [Gates]
      summary: Create a Gate
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, trust_profile]
              properties:
                name: { type: string }
                trust_profile:
                  type: string
                  enum: [L1, L2, L3]
                description: { type: string }
                catalog:
                  type: object
                  description: Permission catalog (pricing + permissions)
                  additionalProperties: true
      responses:
        "201":
          description: Gate created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Gate" }

  /attestations:
    get:
      tags: [Audit]
      summary: List attestations for your passport
      parameters:
        - name: passport_id
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, default: 50, maximum: 500 }
        - name: cursor
          in: query
          schema: { type: string }
      responses:
        "200":
          description: Page of attestations
          content:
            application/json:
              schema:
                type: object
                properties:
                  attestations:
                    type: array
                    items: { $ref: "#/components/schemas/Attestation" }
                  next_cursor: { type: string, nullable: true }

  /attestations/export:
    get:
      tags: [Audit]
      summary: Export full audit trail
      description: |
        Returns the complete hash-linked attestation chain for the caller's
        passports. Use for compliance, accounting, or regulatory review.
      parameters:
        - name: format
          in: query
          schema:
            type: string
            enum: [json, jsonl, csv]
            default: jsonl
        - name: since
          in: query
          schema: { type: string, format: date-time }
      responses:
        "200":
          description: Audit trail export
          content:
            application/json: {}
            application/x-ndjson: {}
            text/csv: {}

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key (mod_live_... / mod_test_...)

  schemas:
    DiscoveryResult:
      type: object
      properties:
        gate_id: { type: string }
        gate_name: { type: string }
        permission_key: { type: string }
        display_name: { type: string, nullable: true }
        description: { type: string, nullable: true }
        category: { type: string, nullable: true }
        risk_level: { type: string, nullable: true }
        security_profile: { type: string, enum: [L1, L2, L3] }
        pricing:
          type: object
          nullable: true
          properties:
            model: { type: string, enum: [per_call, per_minute, subscription] }
            price_cents: { type: integer }
            currency: { type: string }
        sla:
          type: object
          nullable: true
          properties:
            uptime_basis_points: { type: integer }
            response_time_ms: { type: integer }
        platform_fee:
          type: object
          nullable: true
          properties:
            basis_points: { type: integer }
        catalog_version: { type: integer }
        catalog_content_hash: { type: string }
        catalog_signature: { type: string }
        signing_key_id: { type: string }

    PermissionCatalog:
      type: object
      description: Signed, versioned snapshot of a Gate's permissions and pricing.
      properties:
        gate_id: { type: string }
        version: { type: integer }
        permissions:
          type: array
          items:
            type: object
            properties:
              key: { type: string }
              display_name: { type: string, nullable: true }
              description: { type: string, nullable: true }
              pricing:
                type: object
                nullable: true
                additionalProperties: true
        published_at: { type: string, format: date-time }
        content_hash: { type: string }
        signature: { type: string }
        signing_key_id: { type: string }

    Passport:
      type: object
      properties:
        id: { type: string }
        jwt: { type: string, description: "Signed passport JWT" }
        issuer_id: { type: string }
        subject:
          type: object
          additionalProperties: true
        permissions:
          type: array
          items: { type: string }
        constraints:
          type: object
          additionalProperties: true
        status:
          type: string
          enum: [active, suspended, revoked, expired]
        issued_at: { type: string, format: date-time }
        expires_at: { type: string, format: date-time, nullable: true }

    Gate:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        description: { type: string, nullable: true }
        trust_profile: { type: string, enum: [L1, L2, L3] }
        created_at: { type: string, format: date-time }

    EnforcementDecision:
      type: object
      properties:
        decision:
          type: string
          enum: [PERMIT, SUSPEND, BLOCK]
        reason: { type: string, nullable: true }
        attestation_id:
          type: string
          nullable: true
          description: Present when decision is PERMIT or BLOCK (SUSPEND attests after resolution).
        constraints_evaluated:
          type: array
          items: { type: string }

    Attestation:
      type: object
      description: Signed, hash-linked record of a Gate decision.
      properties:
        id: { type: string }
        passport_id: { type: string }
        gate_id: { type: string }
        decision: { type: string, enum: [PERMIT, SUSPEND, BLOCK] }
        request_hash: { type: string }
        previous_attestation_hash: { type: string, nullable: true }
        sequence: { type: integer }
        signature: { type: string }
        signing_key_id: { type: string }
        timestamp: { type: string, format: date-time }
