{
  "openapi": "3.1.0",
  "info": {
    "title": "AgentSignal Commercial API",
    "version": "1.0.0",
    "description": "Monetized endpoints for AgentSignal (agentsignal.app). Two independent payment layers exist:\n\n1. **x402 micropayments** (USDC on Base, `eip155:8453`) — pay-per-call for live signals, backtests, and per-strategy automation subscriptions. A request without payment returns HTTP 402 with an `accepts` array describing how to pay; an x402-compatible client signs an EIP-3009 authorization and retries.\n2. **Platform tier billing** (Stripe fiat, or manual on-chain USDC proof) — recurring `trader`/`alpha` account tiers, independent of per-strategy automation. A wallet with an active tier can also create custom strategies (see Strategy Management) with no session or sign-in.\n\nAll other AgentSignal endpoints (COMPASS, RADAR, Crypto RADAR, strategy list, signal history) are free and not included in this spec. See https://agentsignal.app/ai-trading-plugin for the human-readable guide. Press release submission and distribution now lives entirely at https://pressagent.app — it is not part of AgentSignal.",
    "contact": { "url": "https://agentsignal.app/ai-trading-plugin" }
  },
  "servers": [{ "url": "https://agentsignal.app" }],
  "tags": [
    { "name": "Signals", "description": "Live trading signal + backtest compute, priced per call via x402." },
    { "name": "Automation Subscriptions", "description": "Per-strategy signal-automation subscriptions (trader-side polling), gated in `strategy_subscriptions`." },
    { "name": "Platform Subscriptions", "description": "Account-level trader/alpha tier billing (Stripe or on-chain USDC), resolved via `getSignalTier`/`getSignalTierByWallet` (unions `signal_subscriptions` and `users.role`)." },
    { "name": "Strategy Management", "description": "Create custom strategies. Free to call, gated by platform tier — see Platform Subscriptions." }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "a307_*",
        "description": "AgentSignal API key (format `a307_...`), issued to alpha/trader platform-tier accounts. When valid, bypasses the x402 charge on GET /api/strategy/{id}/signal."
      },
      "WalletHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Wallet-Address",
        "description": "EVM wallet address (0x...) used to look up an active per-strategy or platform subscription, or to attribute an on-chain payment. Not itself a proof of payment — verification happens server-side against stored subscriptions or on-chain tx receipts."
      },
      "VerifiedWalletHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Wallet-Address",
        "description": "Wallet identity that is cryptographically verified, not just read from the header — used wherever a wallet grants write/ownership access rather than just a payment/tier lookup. Requires three headers together: `X-Wallet-Address` (0x...), `X-Wallet-Message` (a single-line string containing `ts:<unix_ms>`, e.g. `AgentSignal API request|wallet:0x..|ts:1712345678901` — no literal newlines, which are invalid in HTTP header values), and `X-Wallet-Signature` (an EIP-191 personal_sign of that exact message by the address's private key). The server checks the timestamp is within 5 minutes and that the signature recovers to the claimed address (same scheme as the wallet sign-in flow at POST /api/auth/wallet). A bare X-Wallet-Address with no message/signature is rejected — wallet addresses are public, non-secret values."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": { "error": { "type": "string" } },
        "required": ["error"]
      },
      "X402PaymentRequired": {
        "type": "object",
        "description": "Standard x402 protocol 402 response body. Returned when no valid payment (or subscription/API-key bypass) is present.",
        "properties": {
          "x402Version": { "type": "integer", "example": 1 },
          "error": { "type": "string", "example": "X-PAYMENT-REQUIRED" },
          "accepts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scheme": { "type": "string", "example": "exact" },
                "network": { "type": "string", "example": "eip155:8453" },
                "maxAmountRequired": { "type": "string", "description": "USDC atomic units (6 decimals)" },
                "resource": { "type": "string", "format": "uri" },
                "description": { "type": "string" },
                "mimeType": { "type": "string", "example": "application/json" },
                "payTo": { "type": "string", "example": "0x843bC7B6e3de3b2A8e47aD0d7AfCEc7e5F118EA6" },
                "maxTimeoutSeconds": { "type": "integer", "example": 60 },
                "asset": { "type": "string", "description": "USDC contract on Base", "example": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }
              }
            }
          }
        }
      },
      "ConditionResult": {
        "type": "object",
        "properties": {
          "source": { "type": "string", "example": "compass" },
          "field": { "type": "string", "example": "score" },
          "op": { "type": "string", "example": "lte" },
          "value": {},
          "result": { "type": "boolean" }
        }
      },
      "SignalResponse": {
        "type": "object",
        "properties": {
          "strategy_id": { "type": "string" },
          "strategy_name": { "type": "string" },
          "symbol": { "type": "string", "example": "BTC-USD" },
          "date": { "type": "string", "format": "date" },
          "signal": { "type": "string", "enum": ["LONG", "SHORT", "FLAT"] },
          "price": { "type": "number" },
          "compass": {
            "type": "object",
            "nullable": true,
            "properties": { "score": { "type": "integer" }, "label": { "type": "string" } }
          },
          "conditions": {
            "type": "object",
            "properties": {
              "entry": { "type": "array", "items": { "$ref": "#/components/schemas/ConditionResult" } },
              "exit": { "type": "array", "items": { "$ref": "#/components/schemas/ConditionResult" } }
            }
          }
        }
      },
      "BacktestRequest": {
        "type": "object",
        "properties": {
          "strategy": { "description": "Either a saved strategy UUID/slug (string) or an inline strategy object (universe/entry/exit/risk).", "oneOf": [{ "type": "string" }, { "type": "object" }] },
          "from": { "type": "string", "format": "date", "default": "2025-01-01" },
          "to": { "type": "string", "format": "date", "default": "today" },
          "capital": { "type": "number", "default": 10000 },
          "mode": { "type": "string", "enum": ["fixed", "compound"], "default": "fixed" },
          "symbol": { "type": "string", "description": "Only used with an inline strategy object; defaults to strategy.universe.symbol." },
          "allowShort": { "type": "boolean", "default": false }
        }
      },
      "BacktestResponse": {
        "type": "object",
        "description": "Full simulation result.",
        "properties": {
          "symbol": { "type": "string" },
          "from": { "type": "string" },
          "to": { "type": "string" },
          "capital": { "type": "number" },
          "strategyName": { "type": "string" },
          "totalReturn": { "type": "number" },
          "buyHoldReturn": { "type": "number" },
          "alpha": { "type": "number" },
          "winRate": { "type": "number" },
          "profitFactor": { "type": "number" },
          "liquidations": { "type": "integer" },
          "equityCurve": { "type": "array", "items": { "type": "object" } },
          "trades": { "type": "array", "items": { "type": "object" } }
        }
      }
    },
    "responses": {
      "PaymentRequired": {
        "description": "Payment required — no x402 payment, valid API key, or active subscription was presented.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/X402PaymentRequired" } } }
      },
      "BadRequest": {
        "description": "Invalid request parameters.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  },
  "paths": {
    "/api/strategy/{id}/signal": {
      "get": {
        "tags": ["Signals"],
        "summary": "Live LONG/SHORT/FLAT signal for a strategy",
        "description": "Computes a fresh signal against current market data. $0.01 per call via x402 (Base USDC). Bypassed for free if the caller presents a valid `a307_` API key belonging to a trader/alpha account, or an `X-Wallet-Address` with an active per-strategy subscription (see /subscribe below). Browser requests (`Accept: text/html`) are redirected to the human-readable strategy page instead of returning JSON.",
        "operationId": "getStrategySignal",
        "security": [{}, { "ApiKeyBearer": [] }, { "WalletHeader": [] }],
        "x-x402": { "price": "$0.01", "network": "eip155:8453", "asset": "USDC" },
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Strategy UUID, short ID, or slug." },
          { "name": "Accept", "in": "header", "schema": { "type": "string", "default": "application/json" }, "description": "Set to application/json to avoid the HTML redirect." }
        ],
        "responses": {
          "200": { "description": "Signal computed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SignalResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/backtest": {
      "post": {
        "tags": ["Signals"],
        "summary": "Run a full historical backtest",
        "description": "$0.05 per call via x402 (Base USDC). Same-origin requests from agentsignal.app itself bypass the charge; external/agent callers pay.",
        "operationId": "runBacktest",
        "x-x402": { "price": "$0.05", "network": "eip155:8453", "asset": "USDC" },
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BacktestRequest" } } } },
        "responses": {
          "200": { "description": "Backtest result.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BacktestResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/strategy/{id}/subscribe": {
      "post": {
        "tags": ["Automation Subscriptions"],
        "summary": "Purchase a per-strategy signal automation subscription",
        "description": "Upfront x402 payment covering a full period of automated polling at a given interval, so the trader can poll the signal endpoint without paying per call during the period. Price = `(60 / interval_minutes) × 24 × days × $0.01`, computed server-side from the query params before the x402 charge is issued.",
        "operationId": "subscribeStrategy",
        "x-x402": { "price": "dynamic — see description", "network": "eip155:8453", "asset": "USDC" },
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "interval_minutes", "in": "query", "required": true, "schema": { "type": "integer", "minimum": 1, "maximum": 1440 } },
          { "name": "period", "in": "query", "required": true, "schema": { "type": "string", "enum": ["day", "week", "month", "year"] } },
          { "name": "X-Wallet-Address", "in": "header", "required": true, "schema": { "type": "string" }, "description": "Recorded as the subscription owner once payment settles." }
        ],
        "responses": {
          "200": {
            "description": "Subscription created.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "properties": {
                "ok": { "type": "boolean" },
                "strategy_id": { "type": "string" },
                "period": { "type": "string" },
                "interval_minutes": { "type": "integer" },
                "calls_covered": { "type": "integer" },
                "expires_at": { "type": "string", "format": "date-time" },
                "tx_amount_usd": { "type": "number" }
              }
            } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "402": { "$ref": "#/components/responses/PaymentRequired" }
        }
      }
    },
    "/api/strategy/{id}/subscription/adjust": {
      "post": {
        "tags": ["Automation Subscriptions"],
        "summary": "Change the polling interval of an active automation subscription",
        "description": "Slowing down (larger interval) is a free extension — remaining budget is stretched over more time. Speeding up (smaller interval) requires an upcharge; the endpoint returns HTTP 402 with the amount owed instead of applying the change. Purchase a fresh subscription at the faster interval via /subscribe to actually upgrade.",
        "operationId": "adjustStrategySubscription",
        "security": [{ "WalletHeader": [] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "X-Wallet-Address", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "properties": { "new_interval_minutes": { "type": "integer", "minimum": 1, "maximum": 1440 } },
            "required": ["new_interval_minutes"]
          } } }
        },
        "responses": {
          "200": {
            "description": "Interval slowed down — extension applied for free.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "properties": {
                "ok": { "type": "boolean" },
                "old_interval_minutes": { "type": "integer" },
                "new_interval_minutes": { "type": "integer" },
                "old_expires_at": { "type": "string" },
                "new_expires_at": { "type": "string" },
                "remaining_hours_before": { "type": "number" },
                "remaining_hours_after": { "type": "number" }
              }
            } } }
          },
          "402": {
            "description": "Speeding up requires an upcharge — no change applied.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "properties": {
                "ok": { "type": "boolean", "example": false },
                "upcharge_required": { "type": "boolean", "example": true },
                "upcharge_usd": { "type": "number" },
                "old_interval_minutes": { "type": "integer" },
                "new_interval_minutes": { "type": "integer" },
                "remaining_hours": { "type": "number" },
                "message": { "type": "string" }
              }
            } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "description": "No active subscription found for this wallet/strategy.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/subscribe/usdc": {
      "post": {
        "tags": ["Platform Subscriptions"],
        "summary": "Purchase a trader/alpha platform tier via x402 USDC",
        "description": "x402-gated. Price is fixed per tier/period: trader $9/mo or $90/yr, alpha $29/mo or $290/yr.",
        "operationId": "subscribeUsdc",
        "x-x402": { "price": "dynamic — see description", "network": "eip155:8453", "asset": "USDC" },
        "parameters": [
          { "name": "tier", "in": "query", "required": true, "schema": { "type": "string", "enum": ["trader", "alpha"] } },
          { "name": "period", "in": "query", "required": true, "schema": { "type": "string", "enum": ["month", "year"] } },
          { "name": "X-Wallet-Address", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Tier activated.", "content": { "application/json": { "schema": {
            "type": "object",
            "properties": { "ok": { "type": "boolean" }, "tier": { "type": "string" }, "period": { "type": "string" }, "expires_at": { "type": "string", "format": "date-time" } }
          } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "402": { "$ref": "#/components/responses/PaymentRequired" }
        }
      }
    },
    "/api/subscribe/crypto": {
      "post": {
        "tags": ["Platform Subscriptions"],
        "summary": "Activate a platform tier by proving a manual on-chain USDC transfer",
        "description": "Not x402 — for wallets that send USDC directly to the AgentSignal pay-to address on Base rather than using an x402 client. The server verifies the transaction receipt on-chain (exact amount, exact recipient) before activating the tier. Fixed amounts: trader 9 USDC/mo or 90 USDC/yr, alpha 29 USDC/mo or 290 USDC/yr (6-decimal USDC units).",
        "operationId": "subscribeCryptoManual",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "properties": {
              "tier": { "type": "string", "enum": ["trader", "alpha"] },
              "period": { "type": "string", "enum": ["month", "year"] },
              "txHash": { "type": "string", "description": "0x-prefixed Base mainnet transaction hash of the USDC transfer." },
              "wallet": { "type": "string", "description": "0x-prefixed sender wallet address." }
            },
            "required": ["tier", "period", "txHash", "wallet"]
          } } }
        },
        "responses": {
          "200": { "description": "Tier activated.", "content": { "application/json": { "schema": {
            "type": "object",
            "properties": { "ok": { "type": "boolean" }, "tier": { "type": "string" }, "period": { "type": "string" }, "expires_at": { "type": "string", "format": "date-time" } }
          } } } },
          "400": { "description": "Invalid params, transaction reverted, or no matching USDC transfer found in the receipt logs.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Server error (e.g. payment address not configured).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/subscribe": {
      "post": {
        "tags": ["Platform Subscriptions"],
        "summary": "Start a trader/alpha platform tier via Stripe (fiat)",
        "description": "Creates a Stripe Checkout session for a recurring subscription. Returns a redirect URL — this is a browser-facing flow, not a direct agent payment endpoint.",
        "operationId": "subscribeStripe",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "properties": {
              "email": { "type": "string", "format": "email" },
              "tier": { "type": "string", "enum": ["trader", "alpha"] }
            },
            "required": ["email", "tier"]
          } } }
        },
        "responses": {
          "200": { "description": "Checkout session created.", "content": { "application/json": { "schema": {
            "type": "object", "properties": { "url": { "type": "string", "format": "uri" } }
          } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "503": { "description": "Billing not yet configured for this tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": { "description": "Stripe error.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/api/strategy": {
      "post": {
        "tags": ["Strategy Management"],
        "summary": "Create a custom strategy",
        "description": "Free to call, but requires an identity: a signed-in session, an `a307_` API key, or a *signature-verified* wallet (see `VerifiedWalletHeader`) for a wallet with an active `trader`/`alpha` platform tier (see `POST /api/subscribe/usdc`). A bare `X-Wallet-Address` with no `X-Wallet-Message`/`X-Wallet-Signature` is treated the same as no identity at all. A `dabbler`/unsubscribed wallet gets a 403. Per-tier save limits apply: dabbler 1, trader 10, alpha unlimited.",
        "operationId": "createStrategy",
        "security": [{}, { "ApiKeyBearer": [] }, { "VerifiedWalletHeader": [] }],
        "parameters": [
          { "name": "X-Wallet-Message", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Required alongside X-Wallet-Address/X-Wallet-Signature for the wallet auth path. Any string containing `ts:<unix_ms>`; must be within 5 minutes of the server's clock." },
          { "name": "X-Wallet-Signature", "in": "header", "required": false, "schema": { "type": "string" }, "description": "Required alongside X-Wallet-Address/X-Wallet-Message. EIP-191 personal_sign of X-Wallet-Message by the X-Wallet-Address private key." }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "description": { "type": "string" },
              "universe": { "type": "object", "properties": { "symbol": { "type": "string" } }, "required": ["symbol"] },
              "entry": { "type": "object", "description": "{ operator: \"AND\"|\"OR\", conditions: [{source, field, op, value}] }" },
              "exit": { "type": "object" },
              "long_entry": { "type": "object" },
              "long_exit": { "type": "object" },
              "short_entry": { "type": "object" },
              "short_exit": { "type": "object" },
              "risk": { "type": "object" },
              "exchange": { "type": "string", "default": "hyperliquid" },
              "is_public": { "type": "boolean" }
            },
            "required": ["name", "universe", "entry", "exit"]
          } } }
        },
        "responses": {
          "201": { "description": "Strategy created.", "content": { "application/json": { "schema": { "type": "object" } } } },
          "400": { "description": "Malformed strategy shape (bad operator, non-array conditions, unknown source/op).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "No session, API key, or verified wallet present — includes the case where X-Wallet-Address was sent without a valid X-Wallet-Message/X-Wallet-Signature.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "Wallet has no active trader/alpha subscription, or the per-tier save limit was reached.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  }
}
