{
  "openapi": "3.1.0",
  "info": {
    "title": "mtok.market API",
    "version": "0.1.0",
    "description": "Spot market for AI inference tokens. Continuous double auction per model; input and output tokens are priced and metered separately (USD per million tokens). An order carries two prices and two caps; trades execute at the resting (maker) order's two prices. Buying: fund a wallet (x402/USDC/card), bid a block, redeem the trade's grant for a gateway key, then call /proxy/v1 — input and output usage are metered against separate budgets and settlement is automatic (seller paid minus exchange fee, unused refunded). Selling: vault a provider credential, attach it to offers. Prototype: in-memory storage; upstream provider call, payment rails, and credential verification are stubs."
  },
  "servers": [{ "url": "/api" }, { "url": "https://mtok.market/api" }, { "url": "https://tokenspot.exchange/api" }],
  "components": {
    "securitySchemes": {
      "apiKey": { "type": "apiKey", "in": "header", "name": "x-api-key" }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": { "code": { "type": "string" }, "message": { "type": "string" } }
          }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "side": { "type": "string", "enum": ["offer", "bid"] },
          "agentId": { "type": "string" },
          "model": { "type": "string" },
          "inputTokens": { "type": "integer" },
          "outputTokens": { "type": "integer" },
          "remainingIn": { "type": "integer" },
          "remainingOut": { "type": "integer" },
          "inputPricePerMTok": { "type": "number", "description": "offers; bids carry maxInputPricePerMTok" },
          "outputPricePerMTok": { "type": "number", "description": "offers; bids carry maxOutputPricePerMTok" },
          "status": { "type": "string", "enum": ["open", "filled", "cancelled", "expired"] },
          "createdAt": { "type": "integer", "description": "epoch ms" },
          "expiresAt": { "type": "integer", "description": "listing expiry, epoch ms" },
          "usableFrom": { "type": "integer", "description": "offers only: window open, epoch ms" },
          "usableUntil": { "type": "integer", "description": "offers only: window close, epoch ms" },
          "recurring": { "type": "boolean", "description": "offers only: re-lists the same window daily" }
        }
      },
      "Trade": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "model": { "type": "string" },
          "inputTokens": { "type": "integer" },
          "outputTokens": { "type": "integer" },
          "inputPricePerMTok": { "type": "number" },
          "outputPricePerMTok": { "type": "number" },
          "amountUsd": { "type": "number", "description": "in*inputPrice + out*outputPrice, per million" },
          "buyerId": { "type": "string" },
          "sellerId": { "type": "string" },
          "bidId": { "type": "string" },
          "offerId": { "type": "string" },
          "paymentId": { "type": "string" },
          "grantId": { "type": "string" },
          "executedAt": { "type": "integer" }
        }
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "provider": { "type": "string" },
          "tradeId": { "type": "string" },
          "buyerId": { "type": "string" },
          "sellerId": { "type": "string" },
          "amountUsd": { "type": "number" },
          "settledUsd": { "type": "number" },
          "feeUsd": { "type": "number" },
          "payoutUsd": { "type": "number" },
          "refundedUsd": { "type": "number" },
          "status": { "type": "string", "enum": ["held", "settled", "refunded"] }
        }
      }
    }
  },
  "paths": {
    "/agents/register": {
      "post": {
        "summary": "Register an agent and receive an API key (shown once)",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "type": "object", "properties": { "name": { "type": "string" } } }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agentId": { "type": "string" },
                    "name": { "type": "string" },
                    "apiKey": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/spot": {
      "get": {
        "summary": "Two spot prices per model — inputSpotPerMTok and outputSpotPerMTok (last trade on that side, falls back to best ask)",
        "responses": { "200": { "description": "Per-model spot, best bid/ask, last trade" } }
      }
    },
    "/book": {
      "get": {
        "summary": "Order book (open offers and bids)",
        "parameters": [
          { "name": "model", "in": "query", "schema": { "type": "string" }, "description": "Filter to one model" }
        ],
        "responses": { "200": { "description": "Offers sorted by ask asc, bids by max desc" } }
      }
    },
    "/trades": {
      "get": {
        "summary": "Recent trades, newest first",
        "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "maximum": 200 } }],
        "responses": { "200": { "description": "Trades" } }
      }
    },
    "/offers": {
      "get": { "summary": "All open offers", "responses": { "200": { "description": "Offers" } } },
      "post": {
        "summary": "Sell token capacity as a capped, dual-priced block. Input and output are priced and metered separately. usableForSeconds (required) is the window buyers can burn the block in — grants inherit it exactly. startsInSeconds delays the window; recurring re-lists it daily. If the credential has maxTokens, listed+sold (in+out) can never exceed it (409).",
        "security": [{ "apiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["model", "inputTokens", "outputTokens", "inputPricePerMTok", "outputPricePerMTok", "credentialId", "usableForSeconds"],
                "properties": {
                  "model": { "type": "string" },
                  "inputTokens": { "type": "integer", "minimum": 1 },
                  "outputTokens": { "type": "integer", "minimum": 1 },
                  "inputPricePerMTok": { "type": "number", "exclusiveMinimum": 0 },
                  "outputPricePerMTok": { "type": "number", "exclusiveMinimum": 0 },
                  "credentialId": { "type": "string" },
                  "usableForSeconds": { "type": "number", "minimum": 300, "maximum": 604800 },
                  "startsInSeconds": { "type": "number", "minimum": 0, "maximum": 604800, "default": 0 },
                  "recurring": { "type": "boolean", "default": false },
                  "expiresInSeconds": { "type": "number", "maximum": 86400, "description": "Optional listing expiry; defaults to the window end (usableUntil), never exceeds it" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "{order, tradeIds}" },
          "400": { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Missing/invalid x-api-key" }
        }
      }
    },
    "/bids": {
      "get": { "summary": "All open bids", "responses": { "200": { "description": "Bids" } } },
      "post": {
        "summary": "Buy a block of capacity (input + output, priced/capped separately); matches immediately if both prices cross a resting offer.",
        "security": [{ "apiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["model", "inputTokens", "outputTokens", "maxInputPricePerMTok", "maxOutputPricePerMTok"],
                "properties": {
                  "model": { "type": "string" },
                  "inputTokens": { "type": "integer", "minimum": 1 },
                  "outputTokens": { "type": "integer", "minimum": 1 },
                  "maxInputPricePerMTok": { "type": "number", "exclusiveMinimum": 0 },
                  "maxOutputPricePerMTok": { "type": "number", "exclusiveMinimum": 0 },
                  "maxStartDelaySeconds": { "type": "number", "minimum": 0, "maximum": 604800, "default": 0, "description": "Also match offers whose usable window opens within this delay (0 = only currently-usable offers)" },
                  "expiresInSeconds": { "type": "number", "default": 3600, "maximum": 86400 }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "{order, tradeIds}" },
          "400": { "description": "Validation error" },
          "401": { "description": "Missing/invalid x-api-key" }
        }
      }
    },
    "/offers/{id}": {
      "delete": {
        "summary": "Cancel an open offer (owner only)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Cancelled" }, "403": { "description": "Not owner" }, "404": { "description": "Not found" }, "409": { "description": "Not open" } }
      }
    },
    "/bids/{id}": {
      "delete": {
        "summary": "Cancel an open bid (owner only)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Cancelled" } }
      }
    },
    "/me": {
      "get": {
        "summary": "Your agent profile, orders, trades, grants, and payments",
        "security": [{ "apiKey": [] }],
        "responses": { "200": { "description": "Agent state" }, "401": { "description": "Unauthorized" } }
      }
    },
    "/payments/{id}": {
      "get": {
        "summary": "Payment intent status (buyer or seller only)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Payment", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Payment" } } } } }
      }
    },
    "/payments/{id}/capture": {
      "post": {
        "summary": "Capture a held payment (buyer only; stub provider in v1)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Captured" }, "403": { "description": "Not buyer" }, "409": { "description": "Wrong state" } }
      }
    },
    "/wallet": {
      "get": {
        "summary": "Your wallet balance and recent ledger entries",
        "security": [{ "apiKey": [] }],
        "responses": { "200": { "description": "{wallet:{availableUsd,reservedUsd}, ledger:[...]}" } }
      }
    },
    "/wallet/deposits": {
      "post": {
        "summary": "Fund your wallet. Rails: x402 | usdc | card. x402/usdc follow the HTTP 402 pattern: first call returns 402 + payment requirements; retry with an X-PAYMENT header containing proof. card credits instantly (Stripe stub).",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "X-PAYMENT", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Payment proof for x402/usdc rails" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amountUsd"],
                "properties": {
                  "amountUsd": { "type": "number", "exclusiveMinimum": 0, "maximum": 10000 },
                  "rail": { "type": "string", "enum": ["x402", "usdc", "card"], "default": "x402" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Credited" },
          "402": { "description": "Payment required — body.accepts[] lists asset/network/payTo; retry with X-PAYMENT" }
        }
      }
    },
    "/wallet/withdrawals": {
      "post": {
        "summary": "Withdraw available balance (stub payout). The rail's cost is passed through: response includes railFeeUsd and netUsd; amountUsd must exceed the rail fee (usdc/x402 $0.05, card $0.25).",
        "security": [{ "apiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amountUsd"],
                "properties": {
                  "amountUsd": { "type": "number", "exclusiveMinimum": 0 },
                  "rail": { "type": "string", "default": "usdc" },
                  "address": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "201": { "description": "Debited" }, "402": { "description": "insufficient_funds" } }
      }
    },
    "/credentials": {
      "get": {
        "summary": "Your vaulted seller credentials (masked)",
        "security": [{ "apiKey": [] }],
        "responses": { "200": { "description": "Credentials, secrets stripped" } }
      },
      "post": {
        "summary": "Vault a provider API key to sell against. The secret never appears in any response; only the gateway uses it to route buyers' calls upstream.",
        "security": [{ "apiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["provider", "apiKey"],
                "properties": {
                  "provider": { "type": "string", "enum": ["anthropic", "openai", "google", "openweight", "other"] },
                  "apiKey": { "type": "string", "minLength": 8 },
                  "models": { "type": "array", "items": { "type": "string" }, "description": "Models this key may serve; default ['*']" },
                  "maxTokens": { "type": "integer", "description": "Optional cap on total tokens sellable through this key" }
                }
              }
            }
          }
        },
        "responses": { "201": { "description": "Masked credential with id — pass as credentialId on offers" } }
      }
    },
    "/credentials/{id}": {
      "delete": {
        "summary": "Revoke a vaulted credential (owner only)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Revoked" } }
      }
    },
    "/grants/{id}": {
      "get": {
        "summary": "Grant status (buyer or seller)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Grant with remainingInputTokens/remainingOutputTokens/usedInputTokens/usedOutputTokens/status" } }
      }
    },
    "/grants/{id}/redeem": {
      "post": {
        "summary": "Redeem a grant for a budget-scoped gateway key (buyer only, idempotent)",
        "security": [{ "apiKey": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "{gatewayKey, baseUrl, endpoints, model, remainingInputTokens, remainingOutputTokens, expiresAt}" },
          "410": { "description": "Grant expired (unused tokens refunded)" }
        }
      }
    },
    "/proxy/v1/chat/completions": {
      "post": {
        "summary": "OpenAI-compatible completion through the gateway. Auth: Authorization: Bearer <gatewayKey>. Usage is metered against the grant; every response includes token_spot.remainingInputTokens/remainingOutputTokens. Upstream is a stub unless live mode is on.",
        "responses": {
          "200": { "description": "chat.completion with usage + token_spot metadata" },
          "400": { "description": "model_mismatch — key is scoped to one model" },
          "401": { "description": "invalid_gateway_key" },
          "402": { "description": "grant_exhausted" },
          "410": { "description": "grant_expired" }
        }
      }
    },
    "/proxy/v1/messages": {
      "post": {
        "summary": "Anthropic-compatible completion through the gateway. Auth: x-api-key: <gatewayKey>.",
        "responses": { "200": { "description": "message with usage + token_spot metadata" } }
      }
    },
    "/agents/{id}/reputation": {
      "get": {
        "summary": "Seller delivery track record: grants settled, tokens delivered, delivery failures, credential suspensions. Check before buying big.",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Reputation" }, "404": { "description": "agent_not_found" } }
      }
    },
    "/exchange/stats": {
      "get": {
        "summary": "Exchange economics: fee rate (bps), settlement fees accrued, rail fees, withdrawable house balance, volume, trade count",
        "responses": { "200": { "description": "Stats" } }
      }
    },
    "/exchange/wallet": {
      "get": {
        "summary": "House wallet + ledger (operator only: x-operator-key header)",
        "responses": { "200": { "description": "House balance and revenue ledger" }, "401": { "description": "invalid_operator_key" } }
      }
    },
    "/exchange/withdrawals": {
      "post": {
        "summary": "Pay out accrued exchange revenue (operator only: x-operator-key header; stub rail)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amountUsd"],
                "properties": {
                  "amountUsd": { "type": "number", "exclusiveMinimum": 0 },
                  "rail": { "type": "string", "default": "usdc" },
                  "address": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "201": { "description": "Debited" }, "401": { "description": "invalid_operator_key" }, "402": { "description": "insufficient_funds" } }
      }
    },
    "/health": {
      "get": { "summary": "Liveness probe", "responses": { "200": { "description": "ok" } } }
    }
  }
}
