{
  "openapi": "3.1.0",
  "info": {
    "title": "FirmTape SPX Dealer Positioning & Quantitative LAB API",
    "description": "Public SPX session discovery plus the authenticated FirmTape LAB API. LAB develops a saved idea through chat, then backtests and optimizes that idea; the same token runs the OPTIONS strategy tester, which prices an SPX 0DTE structure (up to four legs) across the archived sessions on real quotes and settlement. Neither provides trading signals or investment advice.",
    "version": "1.1.0",
    "contact": {
      "name": "FirmTape Support",
      "url": "https://firmtape.com/about",
      "email": "support@firmtape.com"
    }
  },
  "servers": [
    {
      "url": "https://firmtape.com",
      "description": "Production Server"
    }
  ],
  "paths": {
    "/api/days": {
      "get": {
        "summary": "List Archived Trading Sessions",
        "description": "Returns a JSON array of all available historical SPX trading sessions back to 2022.",
        "operationId": "listDays",
        "responses": {
          "200": {
            "description": "List of trading days",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "date",
                        "example": "2026-08-28"
                      }
                    },
                    "step": {
                      "type": "integer",
                      "example": 1,
                      "description": "Per-second grid step, in seconds, of the archived session build"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/session/{day}": {
      "get": {
        "operationId": "getArchivedSession",
        "summary": "Get public closing measurements for one finished SPX session",
        "description": "Returns FirmTape's derived closing summary for a finished session. It does not expose the licensed raw options tape or provide a trading recommendation.",
        "parameters": [{
          "name": "day",
          "in": "path",
          "required": true,
          "description": "Finished SPX trading day",
          "schema": { "type": "string", "format": "date", "example": "2026-08-28" }
        }],
        "responses": {
          "200": {
            "description": "Public derived session summary",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["day", "url", "open", "close", "high", "low", "source"],
                  "properties": {
                    "day": { "type": "string", "format": "date" },
                    "url": { "type": "string", "format": "uri" },
                    "previous_day": { "type": ["string", "null"], "format": "date" },
                    "next_day": { "type": ["string", "null"], "format": "date" },
                    "open": { "type": "number" },
                    "close": { "type": "number" },
                    "high": { "type": "number" },
                    "low": { "type": "number" },
                    "vwap_close": { "type": ["number", "null"] },
                    "zero_gamma_flip": { "type": ["number", "null"] },
                    "call_resistance": { "type": ["number", "null"] },
                    "put_support": { "type": ["number", "null"] },
                    "expected_hold_band": {
                      "type": ["object", "null"],
                      "properties": { "low": { "type": "number" }, "high": { "type": "number" } }
                    },
                    "atm_iv_open": { "type": ["number", "null"] },
                    "net_gamma_percentile": { "type": ["number", "null"] },
                    "flip_crossings": { "type": "integer" },
                    "source": { "type": "string" }
                  }
                }
              }
            }
          },
          "404": { "description": "Unknown, live, or unavailable session" }
        }
      }
    },
    "/api/struct/run": {
      "post": {
        "summary": "Test an SPX 0DTE options structure over the archive",
        "description": "The OPTIONS strategy tester. Prices a structure of up to four SPX 0DTE legs across the archived sessions on real quotes (short legs at the bid, long legs at the ask, $1.25 per contract per leg) and real settlement, as a parameter sweep around the given geometry. Asynchronous: the response names a job to poll at /api/struct/job. Each run spends LAB credits (10 by default); a run the engine cannot complete is refunded.",
        "operationId": "runOptionsStructure",
        "security": [
          {
            "LabBearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["spec"],
                "properties": {
                  "spec": {
                    "type": "object",
                    "required": ["legs", "entry"],
                    "properties": {
                      "legs": { "type": "array", "description": "One to four SPX 0DTE legs, as the terminal's OPTIONS pane builds them", "items": { "type": "object" } },
                      "entry": { "type": "string", "description": "Entry time, HH:MM ET", "example": "10:00" }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Job accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": { "type": "string", "description": "Poll /api/struct/job?id=<job>" },
                    "spent": { "type": "integer", "description": "LAB credits taken for this run" },
                    "state": { "type": "object", "description": "The account's LAB state after the charge" }
                  }
                }
              }
            }
          },
          "400": { "description": "No spec, or a spec without legs and entry" },
          "401": { "description": "Missing, unknown, or revoked LAB token" },
          "402": { "description": "Not enough LAB credits for a run" }
        }
      }
    },
    "/api/struct/job": {
      "get": {
        "summary": "Poll an options structure run",
        "operationId": "getOptionsStructureJob",
        "security": [
          {
            "LabBearerAuth": []
          }
        ],
        "parameters": [{
          "name": "id",
          "in": "query",
          "required": true,
          "description": "The job named by /api/struct/run",
          "schema": { "type": "string" }
        }],
        "responses": {
          "200": {
            "description": "The job's state: running, done with its result, or error with the credits refunded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "state": { "type": "string", "enum": ["running", "done", "error"] },
                    "result": { "type": "object", "description": "Present when done: the sweep's cells and the summary the OPTIONS pane reads" },
                    "error": { "type": "string", "description": "Present on error" },
                    "refunded": { "type": "integer", "description": "Credits returned on error" }
                  }
                }
              }
            }
          },
          "404": { "description": "Unknown or expired job" }
        }
      }
    },
    "/api/lab/chat": {
      "post": {
        "summary": "Develop or amend a LAB idea",
        "description": "Sends a plain-English idea to LAB. A successful compilation creates or amends an idea and returns its id; a clarifying response does not create a runnable rule.",
        "operationId": "compileLabIdea",
        "security": [
          {
            "LabBearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["message"],
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "Plain-English research idea",
                    "example": "Test a 15-minute mean reversion after a three-sigma SPX move above VWAP."
                  },
                  "id": { "type": "string", "description": "Existing LAB idea id to amend" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Compiled idea or a clarifying chat response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "kind": { "type": "string", "enum": ["spec", "chat", "error"] },
                    "say": { "type": "string" },
                    "idea": { "type": "object", "description": "Present when kind is spec; its id is required by /api/lab/run." },
                    "errors": { "type": "array", "items": {} }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, or revoked LAB token"
          },
          "402": {
            "description": "No LAB credits are available to develop an idea"
          },
          "422": {
            "description": "LAB could not compile the request"
          }
        }
      }
    },
    "/api/lab/run": {
      "post": {
        "summary": "Backtest or optimize a saved LAB idea",
        "description": "Runs the named saved idea. Run backtest before quant optimization; each successful request spends one LAB credit and a failed engine job is refunded.",
        "operationId": "runLabStep",
        "security": [
          {
            "LabBearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["id", "kind"],
                "properties": {
                  "id": { "type": "string", "description": "Compiled LAB idea id" },
                  "kind": { "type": "string", "enum": ["backtest", "quant"], "description": "Run backtest first, then quant optimization" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Completed backtest or optimization",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "idea": { "type": "object" },
                    "result": { "type": "object", "description": "Engine result; its fields depend on the selected run kind" },
                    "state": { "type": "object" }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, unknown, or revoked LAB token"
          },
          "402": {
            "description": "No LAB credits are available"
          },
          "404": {
            "description": "Unknown idea"
          },
          "422": {
            "description": "The engine rejected the rule; the credit is refunded"
          },
          "502": {
            "description": "The engine failed; the credit is refunded"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "LabBearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "labk_...",
        "description": "API Bearer token generated at https://firmtape.com/account"
      }
    }
  }
}
