openapi: 3.1.0
info:
  title: MeterCall API
  version: "1.0.0"
  description: |
    MeterCall is the universal metered API gateway. Any module in the catalog can be
    invoked at `/v1/module/:slug/call`. Account, keys, wallet, pay, alerts, usage,
    agent identity, tracking, and contact endpoints live under `/api/*`.
  contact:
    name: MeterCall Support
    email: hi@metercall.ai
    url: https://metercall.ai/docs/
  license:
    name: Proprietary
servers:
  - url: https://metercall.ai
    description: Production
tags:
  - name: Modules
    description: Invoke any module in the catalog.
  - name: Account
  - name: Keys
  - name: Agent
  - name: Pay
  - name: Wallet
  - name: Alerts
  - name: Usage
  - name: Track
  - name: Contact
  - name: Email

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: MeterCall API key (mc_live_... / mc_test_... / mc_sess_...)
  parameters:
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      required: false
      schema: { type: string, maxLength: 128 }
    XPayment:
      in: header
      name: X-Payment
      required: false
      description: Base64-url JSON x402 payment blob.
      schema: { type: string }
  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error: { type: string, example: payment_required }
        message: { type: string }
        request_id: { type: string }
    Account:
      type: object
      properties:
        id: { type: string }
        email: { type: string, format: email }
        plan: { type: string, enum: [starter, pro, scale, enterprise] }
        balance_cents: { type: integer }
        agent_id: { type: string, nullable: true }
    Key:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        prefix: { type: string }
        scopes: { type: array, items: { type: string } }
        last_used_at: { type: string, format: date-time, nullable: true }
    KeyCreated:
      allOf:
        - $ref: "#/components/schemas/Key"
        - type: object
          properties:
            key: { type: string, description: Full key, shown once. }
    ModuleCallRequest:
      type: object
      description: Module-specific action schema. Must include an `action`.
      additionalProperties: true
      required: [action]
      properties:
        action: { type: string }
    ModuleCallResponse:
      type: object
      properties:
        ok: { type: boolean }
        module: { type: string }
        call_id: { type: string }
        cost_cents: { type: integer }
        result:
          type: object
          additionalProperties: true
        receipt:
          type: object
          properties:
            hash: { type: string }
            sig: { type: string }
    Agent:
      type: object
      properties:
        id: { type: string }
        name: { type: string }
        pubkey: { type: string }
        webhook: { type: string, format: uri, nullable: true }
    Nonce:
      type: object
      properties:
        nonce: { type: string }
        expires_at: { type: string, format: date-time }
    Session:
      type: object
      properties:
        agent_id: { type: string }
        session: { type: string }
        expires_in: { type: integer }
    PayCheckout:
      type: object
      required: [amount_cents]
      properties:
        amount_cents: { type: integer, minimum: 100 }
        product: { type: string }
        return_url: { type: string, format: uri }
    WalletBalance:
      type: object
      properties:
        credits_cents: { type: integer }
        usdc: { type: string }
        chain: { type: string }
    WalletConnect:
      type: object
      required: [chain, address, signature]
      properties:
        chain: { type: string, enum: [base, ethereum, polygon, solana] }
        address: { type: string }
        signature: { type: string }
    WalletFund:
      type: object
      required: [amount_cents, method]
      properties:
        amount_cents: { type: integer, minimum: 100 }
        method: { type: string, enum: [card, usdc, ach] }
    Alert:
      type: object
      required: [name, metric, op, threshold, window, channel, target]
      properties:
        name: { type: string }
        metric: { type: string, enum: [spend_cents, plan_pct, calls, error_rate] }
        op: { type: string, enum: [">", ">=", "<", "<="] }
        threshold: { type: number }
        window: { type: string, enum: [hour, day, week, month] }
        channel: { type: string, enum: [email, webhook, slack] }
        target: { type: string }
    UsageCurrent:
      type: object
      properties:
        period_start: { type: string, format: date-time }
        period_end: { type: string, format: date-time }
        calls: { type: integer }
        spend_cents: { type: integer }
        plan_cents: { type: integer }
    UsageByModule:
      type: object
      properties:
        by_module:
          type: array
          items:
            type: object
            properties:
              slug: { type: string }
              calls: { type: integer }
              spend_cents: { type: integer }
    TrackEvent:
      type: object
      required: [event]
      properties:
        event: { type: string }
        session_id: { type: string }
        props:
          type: object
          additionalProperties: true
    ContactForm:
      type: object
      required: [email, message]
      properties:
        name: { type: string }
        email: { type: string, format: email }
        message: { type: string }
  responses:
    PaymentRequired:
      description: Payment required (402). Retry with `X-Payment`.
      headers:
        WWW-Authenticate:
          schema: { type: string }
        X-Price-Cents:
          schema: { type: integer }
        X-Pay-To:
          schema: { type: string }
        X-Nonce:
          schema: { type: string }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Unauthorized:
      description: Missing or invalid credentials.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    RateLimited:
      description: Rate limit exceeded.
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }

paths:
  /v1/module/{slug}/call:
    post:
      tags: [Modules]
      summary: Invoke a module action
      parameters:
        - in: path
          name: slug
          required: true
          schema: { type: string }
        - $ref: "#/components/parameters/IdempotencyKey"
        - $ref: "#/components/parameters/XPayment"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ModuleCallRequest" }
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ModuleCallResponse" }
        "402": { $ref: "#/components/responses/PaymentRequired" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }

  /api/account:
    get:
      tags: [Account]
      summary: Get the authenticated account
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Account" }
        "401": { $ref: "#/components/responses/Unauthorized" }

  /api/keys:
    get:
      tags: [Keys]
      summary: List API keys
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys: { type: array, items: { $ref: "#/components/schemas/Key" } }
    post:
      tags: [Keys]
      summary: Create an API key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string }
                scopes: { type: array, items: { type: string } }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/KeyCreated" }
  /api/keys/{id}:
    delete:
      tags: [Keys]
      summary: Revoke an API key
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked: { type: boolean }
                  id: { type: string }

  /api/agent/nonce:
    get:
      tags: [Agent]
      summary: Get a signing nonce
      security: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Nonce" }
  /api/agent/signin:
    post:
      tags: [Agent]
      summary: Exchange a signed nonce for a session
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [pubkey, nonce, signature]
              properties:
                pubkey: { type: string }
                nonce: { type: string }
                signature: { type: string }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Session" }
  /api/agent/register:
    post:
      tags: [Agent]
      summary: Register a new agent identity
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/Agent" }
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Agent" }
  /api/agent/build:
    post:
      tags: [Agent]
      summary: Submit a module built by an agent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [slug, price_cents, actions]
              properties:
                slug: { type: string }
                price_cents: { type: integer, minimum: 0 }
                actions: { type: array, items: { type: string } }
                webhook: { type: string, format: uri }
      responses:
        "202":
          description: Submitted for review
  /api/agent/builds:
    get:
      tags: [Agent]
      summary: List the agent's submitted modules
      responses:
        "200":
          description: OK
  /api/agent/{id}/usage:
    get:
      tags: [Agent]
      summary: Usage for a specific agent
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: OK
  /api/agent/{id}/earnings:
    get:
      tags: [Agent]
      summary: Lifetime earnings for modules owned by the agent
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: OK
  /api/agent/leaderboard:
    get:
      tags: [Agent]
      summary: Top agents by earnings this week
      responses:
        "200":
          description: OK

  /api/pay/checkout:
    post:
      tags: [Pay]
      summary: Create a hosted checkout session
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PayCheckout" }
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  url: { type: string, format: uri }
  /api/pay/history:
    get:
      tags: [Pay]
      summary: Recent payments (last 50)
      responses:
        "200":
          description: OK
  /api/pay/method:
    post:
      tags: [Pay]
      summary: Save or update default payment method
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [payment_method_id]
              properties:
                payment_method_id: { type: string }
      responses:
        "200":
          description: OK

  /api/wallet/balance:
    get:
      tags: [Wallet]
      summary: Get wallet balances
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/WalletBalance" }
  /api/wallet/connect:
    post:
      tags: [Wallet]
      summary: Connect an on-chain wallet
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/WalletConnect" }
      responses:
        "200":
          description: OK
  /api/wallet/transactions:
    get:
      tags: [Wallet]
      summary: List wallet transactions
      responses:
        "200":
          description: OK
  /api/wallet/fund:
    post:
      tags: [Wallet]
      summary: Add credits to the wallet
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/WalletFund" }
      responses:
        "200":
          description: OK

  /api/alerts:
    get:
      tags: [Alerts]
      summary: List alerts
      responses:
        "200":
          description: OK
    post:
      tags: [Alerts]
      summary: Create an alert
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/Alert" }
      responses:
        "201":
          description: Created
  /api/alerts/{id}:
    delete:
      tags: [Alerts]
      summary: Delete an alert
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: OK
  /api/alerts/events:
    get:
      tags: [Alerts]
      summary: Recent alert firings
      responses:
        "200":
          description: OK

  /api/usage/current:
    get:
      tags: [Usage]
      summary: Usage for the current billing period
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/UsageCurrent" }
  /api/usage/by-module:
    get:
      tags: [Usage]
      summary: Usage broken out by module
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema: { $ref: "#/components/schemas/UsageByModule" }

  /api/track:
    post:
      tags: [Track]
      summary: Send a product-analytics event
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/TrackEvent" }
      responses:
        "204":
          description: Accepted
  /api/track/funnel:
    get:
      tags: [Track]
      summary: Funnel counts over the last 7 days
      responses:
        "200":
          description: OK
  /api/track/recent:
    get:
      tags: [Track]
      summary: Last 200 tracked events (admin)
      responses:
        "200":
          description: OK
  /api/track/sessions:
    get:
      tags: [Track]
      summary: Active sessions in the last hour (admin)
      responses:
        "200":
          description: OK

  /api/contact:
    post:
      tags: [Contact]
      summary: Submit a contact form
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ContactForm" }
      responses:
        "202":
          description: Queued

  /api/email/subscribe:
    post:
      tags: [Email]
      summary: Opt an address into launch updates (double opt-in)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email: { type: string, format: email }
                list: { type: string, enum: [launch, product, weekly] }
      responses:
        "202":
          description: Confirmation sent
  /api/email/unsubscribe:
    post:
      tags: [Email]
      summary: Remove an address from all lists
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email: { type: string, format: email }
      responses:
        "200":
          description: Removed
