{
 "openapi": "3.1.0",
 "info": {
  "title": "FirmTape public API",
  "version": "1.0.0",
  "description": "Every public FirmTape operation: the levels API (LIVE), the Orders and POSITIONS APIs (ORDERS), the LAB API, the archive JSON (REPLAY), the dataset files and the Geo API. Measurements of dealer positioning and public records, never trading signals. Docs: https://firmtape.com/docs/developers"
 },
 "servers": [
  {
   "url": "https://firmtape.com"
  }
 ],
 "paths": {
  "/api/v1/levels": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; a 304 is free",
    "operationId": "getLevels",
    "x-slug": "levels",
    "summary": "Get levels",
    "description": "The volume book's flip, call wall, put wall and net gamma for up to 20 symbols in one call, with the open-interest book alongside under `oi`.",
    "x-features": [
     "levels-api",
     "walls-and-levels",
     "futures-views",
     "integrations"
    ],
    "x-since": "2026-09-30",
    "x-changes": [
     [
      "2026-10-11",
      "A finished day answers with its closing levels, read at 15:45 ET; `levels_at` and `last_second` added."
     ],
     [
      "2026-10-11",
      "RTY and M2K."
     ]
    ],
    "parameters": [
     {
      "name": "sym",
      "in": "query",
      "required": true,
      "description": "Comma list, up to 20: `SPX,ES,NQ,QQQ`. MES, MNQ and M2K answer as ES, NQ and RTY; a dated contract (`ESH27`) names that contract.",
      "schema": {
       "type": "string"
      },
      "x-error": "missing_sym, too_many_symbols"
     },
     {
      "name": "include",
      "in": "query",
      "required": false,
      "description": "`gamma` adds each symbol's gamma by strike: one request per chart.",
      "schema": {
       "type": "string",
       "enum": [
        "gamma"
       ]
      }
     },
     {
      "name": "book",
      "in": "query",
      "required": false,
      "description": "With `include=gamma`: which book's strikes.",
      "schema": {
       "type": "string",
       "enum": [
        "vol",
        "oi"
       ],
       "default": "vol"
      },
      "x-error": "bad_book"
     },
     {
      "name": "contract",
      "in": "query",
      "required": false,
      "description": "Futures: a dated contract in the two weeks before a roll, e.g. `ESH27`. The levels move by that contract's own basis.",
      "schema": {
       "type": "string"
      },
      "x-default": "the front quarterly",
      "x-error": "contract_not_live"
     },
     {
      "name": "errors",
      "in": "query",
      "required": false,
      "description": "`200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status.",
      "schema": {
       "type": "string",
       "enum": [
        "200"
       ]
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "One entry per symbol asked, in order. A symbol that fails carries its own `error`; the rest still answer.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "book",
          "next_update_s",
          "levels"
         ],
         "properties": {
          "book": {
           "type": "string",
           "description": "Always `volume`.",
           "const": "volume"
          },
          "next_update_s": {
           "type": "integer",
           "description": "The smallest `next_update_s` of the symbols.",
           "x-unit": "seconds"
          },
          "levels": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/LiveLevel"
           },
           "description": "The readings."
          }
         }
        },
        "example": {
         "book": "volume",
         "next_update_s": 3,
         "levels": [
          {
           "sym": "ES",
           "name": "E-mini S&P 500",
           "class": "future",
           "contract": "ESZ26",
           "session": "open",
           "day": "2026-09-30",
           "book": "volume",
           "price": 6771.5,
           "price_as_of": "2026-09-30T10:15:03-04:00",
           "flip": 6752.25,
           "call_wall": 6800,
           "put_wall": 6700,
           "net_gamma": {
            "value": -812.4,
            "sign": "negative",
            "unit": "usd_m_per_point"
           },
           "at_flip": false,
           "dealer_gamma": "short",
           "gamma_pctl": -4,
           "as_of": "2026-09-30T10:15:03-04:00",
           "as_of_unix": 1790777703,
           "age_s": 1,
           "cadence": {
            "book": "1s",
            "price": "1s"
           },
           "grade": null,
           "oi": {
            "flip": 6731.4,
            "call_wall": 6825,
            "put_wall": 6700,
            "net_gamma": {
             "value": 1234.5,
             "sign": "positive",
             "unit": "usd_m_per_point"
            }
           },
           "basis": {
            "value": 59.5,
            "index": "SPX",
            "passes": 4,
            "rule": "running median of the day's basis passes up to that time, rounded to 0.25"
           },
           "source": "SPX volume book moved by the ES basis",
           "next_update_s": 3
          }
         ]
        }
       }
      }
     },
     "304": {
      "description": "Not modified: the reading behind the `ETag` you sent has not changed. Not counted against the limit."
     },
     "400": {
      "description": "A parameter is wrong.",
      "x-codes": [
       "missing_sym",
       "too_many_symbols",
       "bad_book"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "x-codes": [
       "no_key",
       "bad_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "403": {
      "description": "The account holds no LIVE plan.",
      "x-codes": [
       "live_required"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "429": {
      "description": "Over the minute's limit, or the key is in use from a fourth address this hour.",
      "x-codes": [
       "rate_limited",
       "too_many_places"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/gamma": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; a 304 is free",
    "operationId": "getGamma",
    "x-slug": "gamma",
    "summary": "Gamma by strike",
    "description": "The newest book's dealer gamma strike by strike, for one symbol, in the volume or the open-interest book.",
    "x-features": [
     "levels-api",
     "ladder-columns",
     "integrations"
    ],
    "x-since": "2026-09-30",
    "parameters": [
     {
      "name": "sym",
      "in": "query",
      "required": true,
      "description": "One symbol: SPX, ES, NQ, RTY (or MES, MNQ, M2K), a dated contract such as `ESH27`, or a ticker from `/api/v1/symbols`.",
      "schema": {
       "type": "string"
      },
      "x-error": "missing_sym"
     },
     {
      "name": "book",
      "in": "query",
      "required": false,
      "description": "`vol` or `oi`.",
      "schema": {
       "type": "string",
       "enum": [
        "vol",
        "oi"
       ],
       "default": "vol"
      },
      "x-error": "bad_book"
     },
     {
      "name": "contract",
      "in": "query",
      "required": false,
      "description": "Futures: a dated contract in the two weeks before a roll, e.g. `ESH27`. The levels move by that contract's own basis.",
      "schema": {
       "type": "string"
      },
      "x-default": "the front quarterly",
      "x-error": "contract_not_live"
     },
     {
      "name": "errors",
      "in": "query",
      "required": false,
      "description": "`200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status.",
      "schema": {
       "type": "string",
       "enum": [
        "200"
       ]
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Strikes and gamma, index-aligned.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "sym",
          "book",
          "strikes",
          "gamma",
          "unit"
         ],
         "properties": {
          "sym": {
           "type": "string",
           "description": "The symbol answered."
          },
          "name": {
           "type": "string",
           "description": "Futures: the future's name."
          },
          "book": {
           "type": "string",
           "description": "`volume` or `open_interest`.",
           "enum": [
            "volume",
            "open_interest"
           ]
          },
          "session": {
           "type": "string",
           "description": "`open`, `closed` or `stale`.",
           "enum": [
            "open",
            "closed",
            "stale"
           ]
          },
          "as_of": {
           "type": "string",
           "description": "When the book was built, ISO 8601 in New York time."
          },
          "as_of_unix": {
           "type": "integer",
           "description": "The same instant, Unix seconds."
          },
          "age_s": {
           "type": "integer",
           "description": "Seconds since the book was built.",
           "x-unit": "seconds"
          },
          "day": {
           "type": "string",
           "description": "The ET trading day."
          },
          "unit": {
           "type": "string",
           "description": "`usd_m_per_point` or `usd_per_1pct`."
          },
          "unit_text": {
           "type": "string",
           "description": "The unit in words."
          },
          "strike_step": {
           "type": [
            "number",
            "null"
           ],
           "description": "The usual distance between strikes.",
           "x-unit": "index points"
          },
          "strikes": {
           "type": "array",
           "items": {
            "type": "number"
           },
           "description": "The strikes, rising.",
           "x-unit": "index points"
          },
          "gamma": {
           "type": "array",
           "items": {
            "type": [
             "number",
             "null"
            ]
           },
           "description": "Dealer gamma at each strike, in `unit`."
          },
          "contract": {
           "type": [
            "string",
            "null"
           ],
           "description": "Futures: the contract."
          },
          "basis": {
           "type": "object",
           "description": "Futures: the basis that moved the strikes.",
           "properties": {
            "value": {
             "type": [
              "number",
              "null"
             ],
             "description": "Future minus index.",
             "x-unit": "index points"
            },
            "index": {
             "type": "string",
             "description": "The index."
            },
            "rule": {
             "type": "string",
             "description": "How the basis is made."
            },
            "note": {
             "type": "string",
             "description": "The strikes in words."
            }
           }
          }
         }
        },
        "example": {
         "sym": "SPX",
         "book": "volume",
         "session": "open",
         "as_of": "2026-09-30T14:00:00-04:00",
         "as_of_unix": 1790791200,
         "age_s": 5,
         "day": "2026-09-30",
         "unit": "usd_m_per_point",
         "unit_text": "millions of USD of dealer gamma per 1 index point",
         "strike_step": 5,
         "strikes": [
          6700,
          6705,
          6710
         ],
         "gamma": [
          -5.5,
          1.25,
          9.75
         ]
        }
       }
      }
     },
     "304": {
      "description": "Not modified. Not counted."
     },
     "400": {
      "description": "A parameter is wrong.",
      "x-codes": [
       "missing_sym",
       "bad_book"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "x-codes": [
       "no_key",
       "bad_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "403": {
      "description": "No LIVE plan.",
      "x-codes": [
       "live_required"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "404": {
      "description": "No such symbol, or no book yet.",
      "x-codes": [
       "unknown_symbol",
       "no_data",
       "no_basis",
       "contract_not_live"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "429": {
      "description": "Over a limit.",
      "x-codes": [
       "rate_limited",
       "too_many_places"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/history": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; the last 90 days, older with DATASET",
    "operationId": "getHistory",
    "x-slug": "history",
    "summary": "Level history of a day",
    "description": "One day's price, flip, call wall, put wall and net gamma as columns and rows: SPX and ES one row per 10 seconds, tickers, NQ and RTY one per five-minute book.",
    "x-features": [
     "levels-api",
     "deep-history"
    ],
    "x-since": "2026-09-30",
    "x-changes": [
     [
      "2026-10-05",
      "The last 90 days for an account key; older days answer `history_too_old` unless the account holds DATASET."
     ]
    ],
    "parameters": [
     {
      "name": "sym",
      "in": "query",
      "required": true,
      "description": "One symbol: SPX, ES, NQ, RTY (or MES, MNQ, M2K), a dated contract such as `ESH27`, or a ticker from `/api/v1/symbols`.",
      "schema": {
       "type": "string"
      },
      "x-error": "missing_sym"
     },
     {
      "name": "day",
      "in": "query",
      "required": false,
      "description": "`YYYY-MM-DD`, within the last 90 days; older with the DATASET licence.",
      "schema": {
       "type": "string",
       "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
      },
      "x-default": "the running or most recent session",
      "x-error": "bad_day, history_too_old"
     },
     {
      "name": "contract",
      "in": "query",
      "required": false,
      "description": "Futures: a dated contract in the two weeks before a roll, e.g. `ESH27`. The levels move by that contract's own basis.",
      "schema": {
       "type": "string"
      },
      "x-default": "the front quarterly",
      "x-error": "contract_not_live"
     },
     {
      "name": "errors",
      "in": "query",
      "required": false,
      "description": "`200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status.",
      "schema": {
       "type": "string",
       "enum": [
        "200"
       ]
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Columns and rows.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "sym",
          "day",
          "columns",
          "rows"
         ],
         "properties": {
          "sym": {
           "type": "string",
           "description": "The symbol."
          },
          "day": {
           "type": "string",
           "description": "The ET trading day."
          },
          "book": {
           "type": "string",
           "description": "Always `volume`.",
           "const": "volume"
          },
          "step": {
           "type": "string",
           "description": "`10s` or `5m`.",
           "enum": [
            "10s",
            "5m"
           ]
          },
          "unit": {
           "type": "string",
           "description": "Net gamma's unit."
          },
          "unit_text": {
           "type": "string",
           "description": "The unit in words."
          },
          "columns": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "`t`, `price`, `flip`, `call_wall`, `put_wall`, `net_gamma`."
          },
          "rows": {
           "type": "array",
           "items": {
            "type": "array"
           },
           "description": "One row per step: the ET time, then the columns' values (index points; net gamma in `unit`)."
          },
          "contract": {
           "type": [
            "string",
            "null"
           ],
           "description": "Futures: the contract."
          },
          "basis": {
           "type": "object",
           "description": "Futures: the basis as it stood at each row.",
           "properties": {
            "index": {
             "type": "string",
             "description": "The index."
            },
            "rule": {
             "type": "string",
             "description": "How the basis is made."
            },
            "first": {
             "type": [
              "number",
              "null"
             ],
             "description": "Basis at the first row.",
             "x-unit": "index points"
            },
            "last": {
             "type": [
              "number",
              "null"
             ],
             "description": "Basis at the last row.",
             "x-unit": "index points"
            },
            "note": {
             "type": "string",
             "description": "In words."
            }
           }
          }
         }
        },
        "example": {
         "sym": "SPX",
         "day": "2026-09-30",
         "book": "volume",
         "step": "10s",
         "unit": "usd_m_per_point",
         "unit_text": "millions of USD of dealer gamma per 1 index point",
         "columns": [
          "t",
          "price",
          "flip",
          "call_wall",
          "put_wall",
          "net_gamma"
         ],
         "rows": [
          [
           "09:30:09",
           6712.4,
           6692,
           6742,
           6662,
           -812.4
          ],
          [
           "09:30:19",
           6713.1,
           6692,
           6742,
           6662,
           -790.2
          ]
         ]
        }
       }
      }
     },
     "304": {
      "description": "Not modified. Not counted."
     },
     "400": {
      "description": "A parameter is wrong.",
      "x-codes": [
       "missing_sym",
       "bad_day"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "x-codes": [
       "no_key",
       "bad_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "403": {
      "description": "No LIVE plan, or a day older than 90 days without DATASET.",
      "x-codes": [
       "live_required",
       "history_too_old"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "404": {
      "description": "No such symbol, or no series for that day.",
      "x-codes": [
       "unknown_symbol",
       "no_data",
       "no_basis",
       "contract_not_live"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "429": {
      "description": "Over a limit.",
      "x-codes": [
       "rate_limited",
       "too_many_places"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/symbols": {
   "get": {
    "operationId": "listSymbols",
    "x-slug": "symbols",
    "summary": "Every symbol",
    "security": [],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "120 a minute per address",
    "description": "Every symbol the levels API answers for: class, cadence, whether it has an open-interest book, the unit and the trust grade. No key needed.",
    "x-features": [
     "levels-api",
     "symbols-on-the-site"
    ],
    "x-since": "2026-09-30",
    "responses": {
     "200": {
      "description": "The list.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "count",
          "symbols"
         ],
         "properties": {
          "count": {
           "type": "integer",
           "description": "How many symbols."
          },
          "book": {
           "type": "string",
           "description": "Always `volume`.",
           "const": "volume"
          },
          "symbols": {
           "type": "array",
           "description": "One entry per symbol.",
           "items": {
            "type": "object",
            "required": [
             "sym"
            ],
            "properties": {
             "sym": {
              "type": "string",
              "description": "The symbol."
             },
             "aliases": {
              "type": "array",
              "items": {
               "type": "string"
              },
              "description": "Futures: the micro that answers as it."
             },
             "name": {
              "type": "string",
              "description": "Its name."
             },
             "class": {
              "type": "string",
              "description": "`index`, `future`, `etf` or `stock`."
             },
             "cadence": {
              "type": "object",
              "description": "How often its book and price are rebuilt.",
              "properties": {
               "book": {
                "type": "string",
                "description": "`1s` or `5m`."
               },
               "price": {
                "type": "string",
                "description": "`1s`."
               }
              }
             },
             "has_oi": {
              "type": "boolean",
              "description": "Whether it has an open-interest book."
             },
             "grade": {
              "type": [
               "string",
               "null"
              ],
              "description": "The chain's trust grade."
             },
             "unit": {
              "type": "string",
              "description": "Net gamma's unit."
             },
             "source": {
              "type": "string",
              "description": "Where its book comes from."
             }
            }
           }
          }
         }
        },
        "example": {
         "count": 2,
         "book": "volume",
         "symbols": [
          {
           "sym": "SPX",
           "name": "S&P 500 Index",
           "class": "index",
           "cadence": {
            "book": "1s",
            "price": "1s"
           },
           "has_oi": true,
           "grade": null,
           "unit": "usd_m_per_point",
           "source": "FirmTape SPX engine, from the full options tape"
          },
          {
           "sym": "QQQ",
           "name": "Invesco QQQ Trust",
           "class": "etf",
           "cadence": {
            "book": "5m",
            "price": "1s"
           },
           "has_oi": true,
           "grade": "A",
           "unit": "usd_per_1pct"
          }
         ]
        }
       }
      }
     }
    }
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/expected_move": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; a 304 is free",
    "operationId": "getExpectedMove",
    "x-slug": "expected-move",
    "summary": "Expected move and close odds",
    "description": "SPX's expected move left to the close (twice the ATM straddle) and the 0DTE chain's own odds for the close, as READOUTS shows them; with `day`, the same at a minute of a finished session against what the close did.",
    "x-features": [
     "expected-move",
     "readouts",
     "levels-api"
    ],
    "x-since": "2026-10-03",
    "parameters": [
     {
      "name": "sym",
      "in": "query",
      "required": false,
      "description": "SPX only.",
      "schema": {
       "type": "string",
       "enum": [
        "SPX"
       ],
       "default": "SPX"
      },
      "x-error": "spx_only"
     },
     {
      "name": "day",
      "in": "query",
      "required": false,
      "description": "A finished session, `YYYY-MM-DD`; leave it out for today.",
      "schema": {
       "type": "string",
       "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
      },
      "x-default": "today",
      "x-error": "bad_day, not_finished"
     },
     {
      "name": "time",
      "in": "query",
      "required": false,
      "description": "With `day`: the ET minute, `HH:MM`, 09:31 to 15:59.",
      "schema": {
       "type": "string",
       "pattern": "^\\d{2}:\\d{2}$"
      },
      "x-default": "10:00",
      "x-error": "bad_time"
     },
     {
      "name": "errors",
      "in": "query",
      "required": false,
      "description": "`200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status.",
      "schema": {
       "type": "string",
       "enum": [
        "200"
       ]
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The expected move and the odds; with `day`, the `outcome` too.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "sym",
          "day",
          "price",
          "levels"
         ],
         "properties": {
          "sym": {
           "type": "string",
           "description": "`SPX`.",
           "const": "SPX"
          },
          "day": {
           "type": "string",
           "description": "The ET trading day."
          },
          "session": {
           "type": "string",
           "description": "`open`, `closed` or `stale`."
          },
          "as_of": {
           "type": "string",
           "description": "The reading's time, ISO 8601 in New York time."
          },
          "as_of_unix": {
           "type": "integer",
           "description": "The same, Unix seconds."
          },
          "age_s": {
           "type": "integer",
           "description": "Seconds since the reading.",
           "x-unit": "seconds"
          },
          "price": {
           "type": [
            "number",
            "null"
           ],
           "description": "SPX at the reading.",
           "x-unit": "index points"
          },
          "minutes_left": {
           "type": "integer",
           "description": "Minutes to 16:00 ET; 0 when closed.",
           "x-unit": "minutes"
          },
          "left_to_close": {
           "type": [
            "object",
            "null"
           ],
           "description": "Twice the ATM straddle from here to 16:00; null when closed or unpriced.",
           "properties": {
            "points": {
             "type": [
              "number",
              "null"
             ],
             "description": "The expected move left.",
             "x-unit": "index points"
            },
            "low": {
             "type": [
              "number",
              "null"
             ],
             "description": "Price minus it.",
             "x-unit": "index points"
            },
            "high": {
             "type": [
              "number",
              "null"
             ],
             "description": "Price plus it.",
             "x-unit": "index points"
            },
            "source": {
             "type": "string",
             "description": "Where the straddle came from."
            },
            "note": {
             "type": "string",
             "description": "What it measured over the archive."
            }
           }
          },
          "close_odds": {
           "type": [
            "object",
            "null"
           ],
           "description": "The 0DTE chain's odds for the close, with 30 or more minutes left; null otherwise.",
           "properties": {
            "frame_t": {
             "type": [
              "string",
              "null"
             ],
             "description": "The ladder frame's ET minute."
            },
            "range_80": {
             "type": "array",
             "items": {
              "type": [
               "number",
               "null"
              ]
             },
             "description": "The 10th and 90th percentile of the close.",
             "x-unit": "index points"
            },
            "middle_half": {
             "type": "array",
             "items": {
              "type": [
               "number",
               "null"
              ]
             },
             "description": "The 25th and 75th percentile.",
             "x-unit": "index points"
            },
            "median": {
             "type": [
              "number",
              "null"
             ],
             "description": "The median close.",
             "x-unit": "index points"
            },
            "beyond_pct": {
             "type": "object",
             "description": "Odds, percent, that the close ends beyond each level, away from spot.",
             "properties": {
              "flip": {
               "type": [
                "number",
                "null"
               ],
               "description": "Beyond the flip.",
               "x-unit": "percent"
              },
              "call_wall": {
               "type": [
                "number",
                "null"
               ],
               "description": "Beyond the call wall.",
               "x-unit": "percent"
              },
              "put_wall": {
               "type": [
                "number",
                "null"
               ],
               "description": "Beyond the put wall.",
               "x-unit": "percent"
              }
             }
            },
            "note": {
             "type": "string",
             "description": "How the odds are made and how they held."
            }
           }
          },
          "close_odds_note": {
           "type": "string",
           "description": "Why `close_odds` is absent."
          },
          "levels": {
           "type": "object",
           "description": "The volume book's levels at the reading.",
           "properties": {
            "flip": {
             "type": [
              "number",
              "null"
             ],
             "description": "Flip.",
             "x-unit": "index points"
            },
            "call_wall": {
             "type": [
              "number",
              "null"
             ],
             "description": "Call wall.",
             "x-unit": "index points"
            },
            "put_wall": {
             "type": [
              "number",
              "null"
             ],
             "description": "Put wall.",
             "x-unit": "index points"
            },
            "book": {
             "type": "string",
             "description": "`volume`."
            }
           }
          },
          "levels_at": {
           "type": "string",
           "description": "A finished day: the levels minute."
          },
          "last_second": {
           "type": "object",
           "description": "A finished day: the levels at the last second.",
           "properties": {
            "flip": {
             "type": [
              "number",
              "null"
             ],
             "description": "Flip.",
             "x-unit": "index points"
            },
            "call_wall": {
             "type": [
              "number",
              "null"
             ],
             "description": "Call wall.",
             "x-unit": "index points"
            },
            "put_wall": {
             "type": [
              "number",
              "null"
             ],
             "description": "Put wall.",
             "x-unit": "index points"
            }
           }
          },
          "outcome": {
           "type": "object",
           "description": "With `day`: what the close did.",
           "properties": {
            "close": {
             "type": [
              "number",
              "null"
             ],
             "description": "The close.",
             "x-unit": "index points"
            },
            "close_as_of": {
             "type": "string",
             "description": "Its time."
            },
            "inside_left_to_close": {
             "type": [
              "boolean",
              "null"
             ],
             "description": "Whether the close landed inside the expected move."
            },
            "inside_range_80": {
             "type": [
              "boolean",
              "null"
             ],
             "description": "Inside the 80% range."
            },
            "inside_middle_half": {
             "type": [
              "boolean",
              "null"
             ],
             "description": "Inside the middle half."
            },
            "vs_median": {
             "type": [
              "number",
              "null"
             ],
             "description": "Close minus the median.",
             "x-unit": "index points"
            }
           }
          }
         }
        },
        "example": {
         "sym": "SPX",
         "day": "2026-09-30",
         "session": "open",
         "as_of": "2026-09-30T14:00:04-04:00",
         "as_of_unix": 1790791204,
         "age_s": 1,
         "price": 6712.14,
         "minutes_left": 120,
         "left_to_close": {
          "points": 24.6,
          "low": 6687.54,
          "high": 6736.74,
          "source": "the session's ATM straddle",
          "note": "twice the ATM straddle from here to 16:00; spot plus or minus it held 91% of closes over 1,108 sessions"
         },
         "close_odds": {
          "frame_t": "14:00",
          "range_80": [
           6694.1,
           6729.8
          ],
          "middle_half": [
           6703.2,
           6720.9
          ],
          "median": 6712.3,
          "beyond_pct": {
           "flip": 18.2,
           "call_wall": 3.1,
           "put_wall": 4.4
          },
          "note": "the 0DTE chain's own odds for the close"
         },
         "levels": {
          "flip": 6692,
          "call_wall": 6742,
          "put_wall": 6662,
          "book": "volume"
         }
        }
       }
      }
     },
     "304": {
      "description": "Not modified. Not counted."
     },
     "400": {
      "description": "A parameter is wrong.",
      "x-codes": [
       "spx_only",
       "bad_day",
       "not_finished",
       "bad_time"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "x-codes": [
       "no_key",
       "bad_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "403": {
      "description": "No LIVE plan.",
      "x-codes": [
       "live_required"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "404": {
      "description": "No reading for that day.",
      "x-codes": [
       "no_data"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "429": {
      "description": "Over a limit.",
      "x-codes": [
       "rate_limited",
       "too_many_places"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/levels.txt": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; a 304 is free",
    "operationId": "getLevelsString",
    "x-slug": "levels-string",
    "summary": "Levels string (FT1)",
    "description": "The same readings as one pasteable line, the FT1 levels string, for the TradingView and thinkorswim scripts, which cannot call an API: up to 8 symbols, each with its top `g` gamma strikes.",
    "x-features": [
     "integrations",
     "levels-api"
    ],
    "x-since": "2026-09-30",
    "parameters": [
     {
      "name": "sym",
      "in": "query",
      "required": true,
      "description": "Comma list, up to 8.",
      "schema": {
       "type": "string"
      },
      "x-error": "missing_sym, too_many_symbols"
     },
     {
      "name": "g",
      "in": "query",
      "required": false,
      "description": "Gamma strikes per symbol, 0 to 30.",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "maximum": 30,
       "default": 20
      },
      "x-error": "bad_g"
     },
     {
      "name": "errors",
      "in": "query",
      "required": false,
      "description": "`200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status.",
      "schema": {
       "type": "string",
       "enum": [
        "200"
       ]
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "One line of text, starting `FT1`. Each block carries the key's mark `k=`.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "text/plain": {
        "schema": {
         "type": "string"
        },
        "example": "FT1|ES|...\n"
       }
      }
     },
     "304": {
      "description": "Not modified. Not counted."
     },
     "400": {
      "description": "A parameter is wrong.",
      "x-codes": [
       "missing_sym",
       "too_many_symbols",
       "bad_g"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "401": {
      "description": "No key.",
      "x-codes": [
       "no_key",
       "bad_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "403": {
      "description": "No LIVE plan.",
      "x-codes": [
       "live_required"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "429": {
      "description": "Over a limit.",
      "x-codes": [
       "rate_limited",
       "too_many_places"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/bookmap.csv": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; a 304 is free",
    "operationId": "getBookmapCsv",
    "x-slug": "bookmap-csv",
    "summary": "Bookmap Cloud Notes sheet",
    "description": "A Bookmap Cloud Notes sheet: one note with a price line per level, in the contract Bookmap names. Without a LIVE plan it answers an empty sheet that says why, so old notes go.",
    "x-features": [
     "integrations"
    ],
    "x-since": "2026-09-30",
    "parameters": [
     {
      "name": "sym",
      "in": "query",
      "required": true,
      "description": "Comma list, up to 20.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "oi",
      "in": "query",
      "required": false,
      "description": "`1` adds the open-interest levels.",
      "schema": {
       "type": "string",
       "enum": [
        "1"
       ]
      }
     },
     {
      "name": "line",
      "in": "query",
      "required": false,
      "description": "`0` drops the horizontal price line.",
      "schema": {
       "type": "string",
       "enum": [
        "0"
       ]
      }
     },
     {
      "name": "automap",
      "in": "query",
      "required": false,
      "description": "Bookmap's data feed for the `#automap` line, or `none`.",
      "schema": {
       "type": "string",
       "default": "RITHMIC"
      }
     },
     {
      "name": "alias",
      "in": "query",
      "required": false,
      "description": "Comma list, one name per symbol, to override the contract Bookmap reads.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "CSV. An auth or plan failure is also 200, an empty sheet whose first line names it, with `X-FirmTape-Status`.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "text/csv": {
        "schema": {
         "type": "string"
        },
        "example": "#automap RITHMIC,,,,,,,\nSymbol,Price Level,Note,Foreground Color,Background Color,Text Alignment,Diameter,Draw Note Price Horizontal Line\nESZ6,6752.25,FT flip 6752.25,#000000,#E0A040,right,1,TRUE\n"
       }
      }
     },
     "304": {
      "description": "Not modified. Not counted."
     },
     "400": {
      "description": "`missing_sym` or `too_many_symbols`, as one line of text."
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/v1/key": {
   "get": {
    "x-plan": "live",
    "x-key": "live",
    "x-limit": "120 a minute per key; a 304 is free",
    "operationId": "checkKey",
    "x-slug": "key",
    "summary": "Check a key",
    "description": "Whether the key works and its plan is LIVE: `{ok, plan, limit_per_minute}`. For a plugin's test button.",
    "x-features": [
     "integration-tokens",
     "levels-api"
    ],
    "x-since": "2026-09-30",
    "parameters": [
     {
      "name": "errors",
      "in": "query",
      "required": false,
      "description": "`200`: an error answers 200 with the same body and the real status in `X-FirmTape-Status`, for clients that cannot read a status.",
      "schema": {
       "type": "string",
       "enum": [
        "200"
       ]
      }
     },
     {
      "name": "key",
      "in": "query",
      "required": false,
      "description": "Legacy: the key in the address, for platforms that cannot send headers. Prefer the header.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The key works.",
      "headers": {
       "X-RateLimit-Limit": "120",
       "X-RateLimit-Remaining": "117"
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "ok",
          "plan"
         ],
         "properties": {
          "ok": {
           "type": "boolean",
           "description": "Always true."
          },
          "plan": {
           "type": "string",
           "description": "`live`."
          },
          "limit_per_minute": {
           "type": "integer",
           "description": "Requests a minute this key may make."
          }
         }
        },
        "example": {
         "ok": true,
         "plan": "live",
         "limit_per_minute": 120
        }
       }
      }
     },
     "401": {
      "description": "No key.",
      "x-codes": [
       "no_key",
       "bad_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "403": {
      "description": "No LIVE plan.",
      "x-codes": [
       "live_required"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     },
     "429": {
      "description": "Over a limit.",
      "x-codes": [
       "rate_limited",
       "too_many_places"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LiveError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Livebearer": []
     },
     {
      "LivequeryKey": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/orders/api/day": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "ordersDay",
    "x-slug": "day",
    "summary": "One session's order ledger",
    "description": "Every large SPX order of one session, sorted by the dealer gamma it added. A day whose ledger has not run answers 200 with an empty list and a note.",
    "x-features": [
     "orders-api",
     "order-ledger"
    ],
    "x-since": "2026-09-24",
    "parameters": [
     {
      "name": "day",
      "in": "query",
      "required": true,
      "description": "The ET trading day, `YYYY-MM-DD`.",
      "schema": {
       "type": "string",
       "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
      },
      "x-error": "bad_day"
     }
    ],
    "responses": {
     "200": {
      "description": "The ledger.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersLedger"
        },
        "example": {
         "day": "2026-08-17",
         "sym": "SPX",
         "min_premium": 100000,
         "asof": "15:59:57",
         "orders": [
          {
           "oid": "p79654",
           "t": "13:43",
           "g": 7.25,
           "kind": "vertical",
           "legs": "7740P/7750P",
           "lg": [
            [
             7740,
             "P",
             1
            ],
            [
             7750,
             "P",
             -1
            ]
           ],
           "exp": "Aug 17",
           "size": 1958,
           "net": 0.15,
           "flip": 38,
           "side": "over"
          }
         ],
         "stats": {
          "n": 312,
          "net": 96.3,
          "no_side": 177,
          "no_gamma": 0,
          "gamma_up": 47,
          "gamma_dn": 74
         }
        }
       }
      }
     },
     "400": {
      "description": "`day` is not `YYYY-MM-DD`.",
      "x-codes": [
       "bad_day"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/orders/api/latest": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "ordersLatest",
    "x-slug": "latest",
    "summary": "The newest session's order ledger",
    "description": "The newest session the ledger has: today's while the market is open, the last finished one after the close.",
    "x-features": [
     "orders-api",
     "order-ledger"
    ],
    "x-since": "2026-09-24",
    "responses": {
     "200": {
      "description": "The ledger.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersLedger"
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/orders/api/sessions": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "ordersSessions",
    "x-slug": "sessions",
    "summary": "Every day with a ledger",
    "description": "Every day an order ledger exists for, oldest first.",
    "x-features": [
     "orders-api"
    ],
    "x-since": "2026-09-24",
    "responses": {
     "200": {
      "description": "Oldest first.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "sessions"
         ],
         "properties": {
          "sessions": {
           "type": "array",
           "items": {
            "type": "string"
           },
           "description": "The days, `YYYY-MM-DD`."
          },
          "first": {
           "type": [
            "string",
            "null"
           ],
           "description": "The oldest."
          },
          "last": {
           "type": [
            "string",
            "null"
           ],
           "description": "The newest."
          }
         }
        },
        "example": {
         "sessions": [
          "2026-08-14",
          "2026-08-17"
         ],
         "first": "2026-08-14",
         "last": "2026-08-17"
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/positions": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "positionsList",
    "x-slug": "positions",
    "summary": "Your positions",
    "description": "All the account's positions with `catalogue` (the kinds and their legs, every condition with its unit, default threshold and range, the label counts and the alert defaults); with `id`, one position.",
    "x-features": [
     "positions-panel"
    ],
    "x-since": "2026-10-04",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": false,
      "description": "One position, `p_...`.",
      "schema": {
       "type": "string"
      },
      "x-error": "not_found"
     }
    ],
    "responses": {
     "200": {
      "description": "`{ positions, max, catalogue }`, or `{ position }` with `id`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "positions": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/OrdersPosition"
           },
           "description": "Up to `max`."
          },
          "max": {
           "type": "integer",
           "description": "Positions an account may keep (5)."
          },
          "catalogue": {
           "type": "object",
           "description": "Kinds, conditions, label counts and alert defaults."
          },
          "position": {
           "$ref": "#/components/schemas/OrdersPosition"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "404": {
      "description": "No position with this id on this account.",
      "x-codes": [
       "not_found"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "post": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "positionsChange",
    "x-slug": "positions-change",
    "summary": "Add, update or remove a position",
    "description": "Everything the editor does: `{ \"op\": \"add\" | \"update\" | \"remove\", \"position\": {...} }`. A refusal says what to fix, in words.",
    "x-features": [
     "positions-panel",
     "positions-conditions"
    ],
    "x-since": "2026-10-04",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "op",
         "position"
        ],
        "properties": {
         "op": {
          "type": "string",
          "description": "`add`, `update` or `remove`.",
          "enum": [
           "add",
           "update",
           "remove"
          ]
         },
         "position": {
          "$ref": "#/components/schemas/OrdersPosition"
         }
        }
       },
       "example": {
        "op": "add",
        "position": {
         "kind": "condor",
         "legs": {
          "lp": 7690,
          "sp": 7700,
          "sc": 7745,
          "lc": 7755
         },
         "entry": "13:45",
         "alert": {
          "channel": "webhook",
          "url": "https://your.server/hook",
          "before": 10
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "`{ ok: true, ... }`; `add` answers the new `id`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": "Done."
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "The body cannot be used; `message` says why.",
      "x-codes": [
       "bad_json",
       "bad_op",
       "invalid"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "404": {
      "description": "No position with this id.",
      "x-codes": [
       "not_found"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "405": {
      "description": "GET or POST only.",
      "x-codes": [
       "method"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/positions/check": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "positionsCheck",
    "x-slug": "positions-check",
    "summary": "Check your positions at a minute",
    "description": "Each position judged at one market minute: only what was known at that minute, never anything later in the day.",
    "x-features": [
     "positions-panel",
     "positions-conditions"
    ],
    "x-since": "2026-10-04",
    "parameters": [
     {
      "name": "day",
      "in": "query",
      "required": false,
      "description": "`YYYY-MM-DD`.",
      "schema": {
       "type": "string"
      },
      "x-default": "today"
     },
     {
      "name": "t",
      "in": "query",
      "required": false,
      "description": "`HH:MM` ET.",
      "schema": {
       "type": "string"
      },
      "x-default": "the latest minute"
     },
     {
      "name": "id",
      "in": "query",
      "required": false,
      "description": "One position; without it, all of them.",
      "schema": {
       "type": "string"
      },
      "x-error": "not_found"
     }
    ],
    "responses": {
     "200": {
      "description": "One check per position.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "day",
          "checks"
         ],
         "properties": {
          "day": {
           "type": "string",
           "description": "The ET trading day."
          },
          "t": {
           "type": [
            "string",
            "null"
           ],
           "description": "The minute judged."
          },
          "live": {
           "type": "boolean",
           "description": "Whether the day is the live session."
          },
          "checks": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/OrdersCheck"
           },
           "description": "One per position."
          },
          "note": {
           "type": "string",
           "description": "When the day has no market minute yet."
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "404": {
      "description": "No position with this id.",
      "x-codes": [
       "not_found"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/positions/track": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "positionsTrack",
    "x-slug": "positions-track",
    "summary": "A position's label track",
    "description": "One position's label minute by minute over the hour up to `t`.",
    "x-features": [
     "positions-track"
    ],
    "x-since": "2026-10-05",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": true,
      "description": "The position.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "day",
      "in": "query",
      "required": false,
      "description": "`YYYY-MM-DD`.",
      "schema": {
       "type": "string"
      },
      "x-default": "today"
     },
     {
      "name": "t",
      "in": "query",
      "required": false,
      "description": "`HH:MM` ET.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "`{ day, t, id, from, points }`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "day": {
           "type": "string",
           "description": "The day."
          },
          "t": {
           "type": [
            "string",
            "null"
           ],
           "description": "The last minute."
          },
          "id": {
           "type": "string",
           "description": "The position."
          },
          "from": {
           "type": [
            "string",
            "null"
           ],
           "description": "The first minute."
          },
          "points": {
           "type": "array",
           "items": {},
           "description": "One point a minute."
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/positions/entries": {
   "get": {
    "x-plan": "orders",
    "x-key": "orders",
    "x-limit": "20,000 calls a day on Orders API, 100,000 on Orders Team, per account",
    "operationId": "positionsEntries",
    "x-slug": "positions-entries",
    "summary": "A position's entry log",
    "description": "What each session's entry minute looked like for one position, with up to `replay` replayed past days.",
    "x-features": [
     "entry-log"
    ],
    "x-since": "2026-10-05",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": true,
      "description": "The position.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "replay",
      "in": "query",
      "required": false,
      "description": "Past days to replay.",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The log.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "No key, or a revoked one.",
      "x-codes": [
       "no_key"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "403": {
      "description": "The account is not on the Orders API or Team plan.",
      "x-codes": [
       "plan"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     },
     "429": {
      "description": "The day's calls are spent.",
      "x-codes": [
       "rate"
      ],
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/OrdersError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Ordersbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/lab/chat": {
   "post": {
    "summary": "Develop or amend an idea",
    "description": "Compile a plain-words idea into a rule, or amend an existing one by `id`. Free, but needs a positive balance. Any chat on an idea sends it back to the start of the conveyor.",
    "operationId": "labChat",
    "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"
         }
        }
       }
      }
     }
    },
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "Credits; one idea on the conveyor at a time",
    "x-slug": "chat",
    "x-features": [
     "lab-api",
     "backtester"
    ],
    "x-since": "2026-08-12",
    "responses": {
     "200": {
      "description": "`kind: \"spec\"` with the compiled idea, or `kind: \"chat\"` with a question.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "kind": {
           "type": "string",
           "description": "`spec`, `chat` or `error`.",
           "enum": [
            "spec",
            "chat",
            "error"
           ]
          },
          "say": {
           "type": "string",
           "description": "LAB's answer in words."
          },
          "idea": {
           "type": "object",
           "description": "With `spec`: `{ id, name, spec }`."
          },
          "errors": {
           "type": "array",
           "items": {},
           "description": "With `error`: what could not compile."
          }
         }
        },
        "example": {
         "kind": "spec",
         "say": "Compiled: fade a +3 sigma stretch above vwap.",
         "idea": {
          "id": "i_abc123def456",
          "name": "vwap stretch fade",
          "spec": {}
         }
        }
       }
      }
     },
     "400": {
      "description": "A parameter is wrong.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "402": {
      "description": "No credits.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "404": {
      "description": "No such idea or job.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "422": {
      "description": "The rule could not be compiled or was refused (refunded).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/lab/run": {
   "post": {
    "summary": "Backtest or optimize an idea",
    "description": "Run the idea's current conveyor step: `backtest` first (rule to tested), then `quant` (tested to ready). One credit, refunded on failure.",
    "operationId": "labRun",
    "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"
         }
        }
       }
      }
     }
    },
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "1 credit a run",
    "x-slug": "run",
    "x-features": [
     "lab-api",
     "backtester",
     "quant-optimize"
    ],
    "x-since": "2026-08-12",
    "responses": {
     "200": {
      "description": "`{ idea, result, state }`: the engine's verdict per leg and the balance after.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "idea": {
           "type": "object",
           "description": "The idea after the step."
          },
          "result": {
           "type": "object",
           "description": "Per leg: era tables (all, this year, holdout), the full rule against its baseline, and `verdict`."
          },
          "state": {
           "type": "object",
           "description": "Balance and ideas."
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "A parameter is wrong.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "402": {
      "description": "No credits.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "404": {
      "description": "No such idea or job.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "409": {
      "description": "Another idea holds the conveyor, or a step came out of order.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "422": {
      "description": "The rule could not be compiled or was refused (refunded).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "502": {
      "description": "The engine failed (refunded); retry.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/lab/state": {
   "get": {
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "Credits; one idea on the conveyor at a time",
    "operationId": "labState",
    "x-slug": "state",
    "summary": "Ideas, conveyor and balance",
    "description": "Everything in one call: the account's ideas with their stages and results, the idea holding the conveyor, and the credit balance. Free.",
    "x-features": [
     "lab-api",
     "credits-and-tokens"
    ],
    "x-since": "2026-08-12",
    "responses": {
     "200": {
      "description": "The state.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ideas": {
           "type": "array",
           "items": {},
           "description": "`{ id, name, stage, spec, results }`."
          },
          "conveyor": {
           "type": [
            "string",
            "null"
           ],
           "description": "The idea holding the conveyor."
          },
          "balance": {
           "type": "object",
           "description": "`{ monthly, purchased, total }` credits."
          }
         }
        },
        "example": {
         "ideas": [],
         "conveyor": null,
         "balance": {
          "monthly": 5,
          "purchased": 0,
          "total": 5
         }
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/lab/thread": {
   "get": {
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "Credits; one idea on the conveyor at a time",
    "operationId": "labThread",
    "x-slug": "thread",
    "summary": "An idea's chat thread",
    "description": "The chat thread of one idea. Free.",
    "x-features": [
     "lab-api"
    ],
    "x-since": "2026-08-12",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": true,
      "description": "The idea, `i_...`.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "The thread.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "404": {
      "description": "No such idea or job.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/lab/idea": {
   "post": {
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "Credits; one idea on the conveyor at a time",
    "operationId": "labIdea",
    "x-slug": "idea",
    "summary": "Desk actions on an idea",
    "description": "`desk` (a ready idea onto the desk, freeing the conveyor), `drop` (back to the start, freeing the conveyor), `delete`, or `set` (the desk's switches: `on`, `draw`, `alert`). Free.",
    "x-features": [
     "lab-api",
     "desk",
     "desk-alerts"
    ],
    "x-since": "2026-08-12",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "id",
         "action"
        ],
        "properties": {
         "id": {
          "type": "string",
          "description": "The idea."
         },
         "action": {
          "type": "string",
          "description": "`desk`, `drop`, `delete` or `set`.",
          "enum": [
           "desk",
           "drop",
           "delete",
           "set"
          ]
         },
         "on": {
          "type": "boolean",
          "description": "With `set`."
         },
         "draw": {
          "type": "string",
          "description": "With `set`."
         },
         "alert": {
          "type": "string",
          "description": "With `set`."
         }
        }
       },
       "example": {
        "id": "i_abc123def456",
        "action": "desk"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "The idea after the action.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "A parameter is wrong.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "404": {
      "description": "No such idea or job.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "409": {
      "description": "Another idea holds the conveyor, or a step came out of order.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/lab/vocabulary": {
   "get": {
    "operationId": "labVocabulary",
    "x-slug": "vocabulary",
    "summary": "The LAB vocabulary",
    "security": [],
    "x-plan": "replay",
    "x-key": "none",
    "description": "Every column a rule may name, with its unit, range and an example: the compiler's own dictionary. Public, no key; the page for a reader is [/lab/vocabulary](/lab/vocabulary).",
    "x-features": [
     "vocabulary"
    ],
    "x-since": "2026-09-04",
    "responses": {
     "200": {
      "description": "The vocabulary.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/struct/run": {
   "post": {
    "summary": "Test an options structure over the archive",
    "description": "Price an SPX 0DTE structure of up to four legs at an entry minute over the archived sessions on real quotes and settlement. 10 credits, refunded on failure; answers 202 with a job to poll.",
    "operationId": "structRun",
    "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"
           }
          }
         }
        }
       }
      }
     }
    },
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "10 credits a run",
    "x-slug": "options-run",
    "x-features": [
     "archive-run",
     "structure-tester"
    ],
    "x-since": "2026-08-31",
    "responses": {
     "202": {
      "description": "`{ job }`: poll `GET /api/struct/job`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "job": {
           "type": "string",
           "description": "The job id."
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "A parameter is wrong.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "401": {
      "description": "No key, or an unknown or revoked one.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     },
     "402": {
      "description": "No credits.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/struct/job": {
   "get": {
    "summary": "Poll an options run",
    "operationId": "structJob",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": true,
      "description": "The job named by /api/struct/run",
      "schema": {
       "type": "string"
      }
     }
    ],
    "x-plan": "lab",
    "x-key": "lab",
    "x-limit": "Credits; one idea on the conveyor at a time",
    "x-slug": "options-job",
    "description": "A structure run's state: running, done with its result, or error with the credits refunded.",
    "x-features": [
     "archive-run"
    ],
    "x-since": "2026-08-31",
    "responses": {
     "200": {
      "description": "`{ state, result? , error?, refunded? }`.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "state": {
           "type": "string",
           "description": "`running`, `done` or `error`."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "No such idea or job.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/LabError"
        }
       }
      }
     }
    },
    "security": [
     {
      "Labbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/days": {
   "get": {
    "summary": "Every finished session",
    "description": "Every finished SPX session in the archive, back to 2022. No key.",
    "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"
          }
         }
        },
        "example": {
         "days": [
          "2026-10-08",
          "2026-10-09"
         ],
         "step": 1
        }
       }
      }
     }
    },
    "x-slug": "days",
    "x-plan": "replay",
    "x-key": "none",
    "x-features": [
     "session-archive"
    ],
    "security": []
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/api/session/{day}": {
   "get": {
    "operationId": "getArchivedSession",
    "summary": "One session's closing measurements",
    "description": "FirmTape's derived closing summary of one finished session: open, close, high, low, the zero-gamma flip and its crossings, the walls, the net-gamma percentile, ATM IV. No key.",
    "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"
           ]
          },
          "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."
     }
    },
    "x-slug": "session",
    "x-plan": "replay",
    "x-key": "none",
    "x-features": [
     "session-archive",
     "free-replay"
    ],
    "x-since": "2026-09-01",
    "security": []
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/snapshots/{file}": {
   "get": {
    "operationId": "datasetFile",
    "x-slug": "files",
    "summary": "A day file",
    "x-plan": "dataset",
    "x-key": "dataset",
    "x-limit": "No breadth limit for a licence holder",
    "description": "One finished session: `<day>.json.gz`, the full payload, or `<day>.lite.json.gz`, the smaller summary twin.",
    "x-features": [
     "archive-files",
     "evening-delivery"
    ],
    "parameters": [
     {
      "name": "file",
      "in": "path",
      "required": true,
      "description": "`YYYY-MM-DD.json.gz` or `YYYY-MM-DD.lite.json.gz`.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Gzipped JSON.",
      "content": {
       "application/gzip": {
        "schema": {
         "type": "string",
         "format": "binary"
        }
       }
      }
     },
     "404": {
      "description": "No such session."
     }
    },
    "security": [
     {
      "Datasetbearer": []
     }
    ]
   },
   "servers": [
    {
     "url": "https://firmtape.com"
    }
   ]
  },
  "/items": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeItems",
    "summary": "Records, free",
    "parameters": [
     {
      "name": "week",
      "in": "query",
      "required": false,
      "description": "ISO week on the New York calendar, e.g. 2026-W38; replaces range",
      "schema": {
       "type": "string",
       "pattern": "^\\d{4}-W\\d{2}$"
      }
     },
     {
      "name": "range",
      "in": "query",
      "required": false,
      "description": "24h, 7d or 30d when no week is given",
      "schema": {
       "type": "string",
       "enum": [
        "24h",
        "7d",
        "30d"
       ],
       "default": "24h"
      }
     },
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "notable",
      "in": "query",
      "required": false,
      "description": "1 keeps only notable records",
      "schema": {
       "type": "string",
       "enum": [
        "1"
       ]
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Up to 1000",
      "schema": {
       "type": "integer",
       "maximum": 1000,
       "default": 200
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "Paging inside a week",
      "schema": {
       "type": "integer",
       "minimum": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Records, newest first, with week paging links",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/GeoRecord"
           }
          },
          "next_page": {
           "type": [
            "string",
            "null"
           ]
          },
          "prev_week": {
           "type": [
            "string",
            "null"
           ]
          },
          "next_week": {
           "type": [
            "string",
            "null"
           ]
          }
         }
        }
       }
      }
     }
    },
    "x-slug": "free-items",
    "description": "Records of one week or a recent range, as the pages show them.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days",
    "security": []
   }
  },
  "/weeks": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeWeeks",
    "summary": "Weeks with records, free",
    "parameters": [
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Weeks, newest first"
     }
    },
    "x-slug": "free-weeks",
    "description": "Weeks of the last 30 days that hold records, with counts.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days",
    "security": []
   }
  },
  "/countries": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeCountries",
    "summary": "Countries named, free",
    "responses": {
     "200": {
      "description": "Countries"
     }
    },
    "x-slug": "free-countries",
    "description": "Every country named in the last 30 days, with notable and total counts.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days",
    "security": []
   }
  },
  "/health": {
   "servers": [
    {
     "url": "https://firmtape.com/geo/api",
     "description": "Free, no key: the last 30 days, as the Geo pages show them"
    }
   ],
   "get": {
    "tags": [
     "Free"
    ],
    "operationId": "freeSources",
    "summary": "Feeds and their health, free",
    "responses": {
     "200": {
      "description": "Sources"
     }
    },
    "x-slug": "free-sources",
    "description": "Every feed, how often it is read and when it last answered.",
    "x-features": [
     "records-map",
     "geo-api"
    ],
    "x-plan": "replay",
    "x-key": "none",
    "x-limit": "Free JSON, the last 30 days",
    "security": []
   }
  },
  "/v1/items": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "items",
    "summary": "Records",
    "security": [
     {
      "Geokey": []
     }
    ],
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC start; default 7 days ago",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "until",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC end; default now",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "feed",
      "in": "query",
      "required": false,
      "description": "One feed id or several, comma separated",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_severity",
      "in": "query",
      "required": false,
      "description": "2 notable and above, 3 critical only",
      "schema": {
       "type": "integer",
       "minimum": 0,
       "maximum": 3
      }
     },
     {
      "name": "q",
      "in": "query",
      "required": false,
      "description": "Words in the title",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "uid",
      "in": "query",
      "required": false,
      "description": "One record id, or several comma separated; ignores the time window",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1 to 1000",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 1000,
       "default": 200
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "From next in the previous answer",
      "schema": {
       "type": "integer",
       "minimum": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Records; next holds the URL of the next page when more match",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "count": {
           "type": "integer"
          },
          "next": {
           "type": [
            "string",
            "null"
           ]
          },
          "history_days": {
           "type": "integer"
          },
          "items": {
           "type": "array",
           "items": {
            "$ref": "#/components/schemas/GeoRecord"
           }
          }
         }
        },
        "example": {
         "count": 1,
         "next": null,
         "history_days": 365,
         "items": [
          {
           "uid": "8f1c2a90d4b7e613",
           "feed": "faa_tfr",
           "sector": "air",
           "type": "new",
           "category": "TFR",
           "severity": 2,
           "routine": false,
           "countries": [
            "US"
           ],
           "region": null,
           "title": "Temporary flight restriction",
           "summary": "...",
           "published_utc": "2026-09-17T12:58:00Z",
           "event_utc": null,
           "first_seen_utc": "2026-09-17T13:02:00Z",
           "backfill": false,
           "source": "FAA",
           "url": "https://example.org/notice"
          }
         ]
        }
       }
      }
     },
     "400": {
      "description": "A parameter is wrong; no call is counted",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GeoError"
        }
       }
      }
     },
     "401": {
      "description": "No key, unknown key or revoked key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GeoError"
        }
       }
      }
     },
     "403": {
      "description": "No active API or Team plan, or a key beyond the plan's count",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GeoError"
        }
       }
      }
     },
     "429": {
      "description": "This month's calls are used; Retry-After says when the count resets",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GeoError"
        }
       }
      }
     }
    },
    "x-slug": "items",
    "description": "Records, newest first, a year back.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "20,000 calls a month on Geo API, 100,000 on Geo Team"
   }
  },
  "/v1/sources": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "sources",
    "summary": "Feeds",
    "security": [
     {
      "Geokey": []
     }
    ],
    "responses": {
     "200": {
      "description": "One row per feed: feed, source, sector, last_ok_utc, minutes_since_ok, last_status, fail_streak, records, first_record_utc, last_record_utc"
     },
     "401": {
      "description": "Bad key",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GeoError"
        }
       }
      }
     }
    },
    "x-slug": "sources",
    "description": "Every feed, its sector, when it last answered and what it holds.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "20,000 calls a month on Geo API, 100,000 on Geo Team"
   }
  },
  "/v1/usage": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "usage",
    "summary": "This month's usage",
    "security": [
     {
      "Geokey": []
     }
    ],
    "responses": {
     "200": {
      "description": "Usage",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "tier": {
           "type": "string"
          },
          "month": {
           "type": "string"
          },
          "calls": {
           "type": "integer"
          },
          "limit": {
           "type": "integer"
          }
         }
        }
       }
      }
     }
    },
    "x-slug": "usage",
    "description": "This month's calls against the plan; not counted.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "Not counted"
   }
  },
  "/v1/export.csv": {
   "servers": [
    {
     "url": "https://geo.firmtape.com",
     "description": "Paid Geo API: API or Team plan key, a year of history"
    }
   ],
   "get": {
    "tags": [
     "Paid"
    ],
    "operationId": "exportCsv",
    "summary": "CSV export",
    "security": [
     {
      "Geokey": []
     }
    ],
    "parameters": [
     {
      "name": "since",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC start",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "until",
      "in": "query",
      "required": false,
      "description": "ISO 8601 UTC end",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "sector",
      "in": "query",
      "required": false,
      "description": "Sector",
      "schema": {
       "type": "string",
       "enum": [
        "gov",
        "air",
        "gps",
        "sea",
        "internet",
        "ground",
        "space",
        "attention"
       ]
      }
     },
     {
      "name": "country",
      "in": "query",
      "required": false,
      "description": "ISO 3166-1 alpha-2",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "feed",
      "in": "query",
      "required": false,
      "description": "Feed ids",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_severity",
      "in": "query",
      "required": false,
      "description": "Severity floor",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "CSV",
      "content": {
       "text/csv": {
        "schema": {
         "type": "string"
        }
       }
      }
     },
     "400": {
      "description": "Window longer than the plan allows",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/GeoError"
        }
       }
      }
     }
    },
    "x-slug": "export-csv",
    "description": "Every matching record in one CSV: up to 7 days a call on API, 31 on Team.",
    "x-since": "2026-09-17",
    "x-features": [
     "geo-api"
    ],
    "x-plan": "geo",
    "x-key": "geo",
    "x-limit": "20,000 calls a month on Geo API, 100,000 on Geo Team"
   }
  }
 },
 "components": {
  "schemas": {
   "LiveError": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "object",
      "required": [
       "code",
       "message"
      ],
      "properties": {
       "code": {
        "type": "string",
        "description": "A stable code from the error catalog."
       },
       "message": {
        "type": "string",
        "description": "What happened, in words."
       },
       "pricing": {
        "type": "string",
        "description": "Where to start the plan the call needs."
       },
       "retry_after_s": {
        "type": "integer",
        "description": "With `rate_limited`: seconds to wait."
       },
       "oldest": {
        "type": "string",
        "description": "With `history_too_old`: the oldest day the key may ask for."
       },
       "live": {
        "type": "array",
        "items": {
         "type": "string"
        },
        "description": "With `contract_not_live`: the contracts measured that day."
       }
      }
     }
    }
   },
   "LiveLevel": {
    "type": "object",
    "required": [
     "sym"
    ],
    "properties": {
     "sym": {
      "type": "string",
      "description": "The symbol answered: SPX, ES, NQ, RTY or a ticker. A micro (MES, MNQ, M2K) answers as its future."
     },
     "requested": {
      "type": "string",
      "description": "Present when the symbol asked differs from `sym` (`MES` answered as `ES`)."
     },
     "name": {
      "type": "string",
      "description": "The symbol's name."
     },
     "class": {
      "type": "string",
      "description": "`index`, `future`, `etf` or `stock`."
     },
     "contract": {
      "type": "string",
      "description": "Futures: the contract the levels are moved to, e.g. `ESZ26`."
     },
     "session": {
      "type": "string",
      "description": "`open` while the book is current, `closed` outside market hours, `stale` when market hours are on but the book has not moved for a while.",
      "enum": [
       "open",
       "closed",
       "stale"
      ]
     },
     "day": {
      "type": "string",
      "description": "The ET trading day of the reading, `YYYY-MM-DD`."
     },
     "book": {
      "type": "string",
      "description": "Always `volume`: every level is the volume book, the one the terminal draws.",
      "const": "volume"
     },
     "price": {
      "type": [
       "number",
       "null"
      ],
      "description": "The price at the reading.",
      "x-unit": "index points"
     },
     "price_as_of": {
      "type": "string",
      "description": "When that price was taken, ISO 8601 in New York time."
     },
     "flip": {
      "type": [
       "number",
       "null"
      ],
      "description": "Zero-gamma flip of the volume book. On a finished day, read at the levels minute (`levels_at`).",
      "x-unit": "index points"
     },
     "call_wall": {
      "type": [
       "number",
       "null"
      ],
      "description": "Call wall: the strike with the largest call gamma. On a finished day, read at the levels minute.",
      "x-unit": "index points"
     },
     "put_wall": {
      "type": [
       "number",
       "null"
      ],
      "description": "Put wall: the strike with the largest put gamma. On a finished day, read at the levels minute.",
      "x-unit": "index points"
     },
     "net_gamma": {
      "type": "object",
      "description": "Net dealer gamma of the book: its value, its sign and its unit, named on every answer.",
      "properties": {
       "value": {
        "type": [
         "number",
         "null"
        ],
        "description": "Net dealer gamma, in `unit`."
       },
       "sign": {
        "type": [
         "string",
         "null"
        ],
        "enum": [
         "positive",
         "negative",
         null
        ],
        "description": "The sign of `value`."
       },
       "unit": {
        "type": "string",
        "description": "`usd_m_per_point` (millions of USD per index point: SPX, ES) or `usd_per_1pct` (USD per 1% move: tickers, NQ, RTY).",
        "enum": [
         "usd_m_per_point",
         "usd_per_1pct"
        ]
       }
      }
     },
     "at_flip": {
      "type": "boolean",
      "description": "SPX and ES: the price is within 5 points of the flip, where the side is not a reliable reading."
     },
     "dealer_gamma": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "long",
       "short",
       null
      ],
      "description": "SPX and ES: the side of net gamma; null at the flip."
     },
     "gamma_pctl": {
      "type": [
       "number",
       "null"
      ],
      "description": "SPX and ES: the volume book's gamma at spot against the same minute of the last 20 sessions, a decile from -11 to +11. A measurement, with no measured forward edge."
     },
     "as_of": {
      "type": "string",
      "description": "When the book was built, ISO 8601 in New York time."
     },
     "as_of_unix": {
      "type": "integer",
      "description": "The same instant, Unix seconds."
     },
     "age_s": {
      "type": "integer",
      "description": "Seconds since the book was built.",
      "x-unit": "seconds"
     },
     "cadence": {
      "type": "object",
      "description": "How often the book and the price are rebuilt.",
      "properties": {
       "book": {
        "type": "string",
        "description": "`1s` (SPX, ES) or `5m` (tickers, NQ, RTY)."
       },
       "price": {
        "type": "string",
        "description": "`1s`."
       }
      }
     },
     "grade": {
      "type": [
       "string",
       "null"
      ],
      "description": "Tickers, NQ, RTY: the chain's trust grade (A best). Null for SPX and ES."
     },
     "flip_status": {
      "type": [
       "string",
       "null"
      ],
      "description": "Tickers: whether the flip was found inside the strike grid."
     },
     "oi": {
      "type": [
       "object",
       "null"
      ],
      "description": "The open-interest book's levels, the same fields; null when the symbol has none.",
      "properties": {
       "flip": {
        "type": [
         "number",
         "null"
        ],
        "description": "Zero-gamma flip of the open-interest book.",
        "x-unit": "index points"
       },
       "call_wall": {
        "type": [
         "number",
         "null"
        ],
        "description": "Call wall of the open-interest book.",
        "x-unit": "index points"
       },
       "put_wall": {
        "type": [
         "number",
         "null"
        ],
        "description": "Put wall of the open-interest book.",
        "x-unit": "index points"
       },
       "net_gamma": {
        "type": "object",
        "description": "Net dealer gamma of the book: its value, its sign and its unit, named on every answer.",
        "properties": {
         "value": {
          "type": [
           "number",
           "null"
          ],
          "description": "Net dealer gamma, in `unit`."
         },
         "sign": {
          "type": [
           "string",
           "null"
          ],
          "enum": [
           "positive",
           "negative",
           null
          ],
          "description": "The sign of `value`."
         },
         "unit": {
          "type": "string",
          "description": "`usd_m_per_point` (millions of USD per index point: SPX, ES) or `usd_per_1pct` (USD per 1% move: tickers, NQ, RTY).",
          "enum": [
           "usd_m_per_point",
           "usd_per_1pct"
          ]
         }
        }
       },
       "flip_status": {
        "type": [
         "string",
         "null"
        ],
        "description": "Tickers: whether the flip was found inside the strike grid (`ok`) or not."
       }
      }
     },
     "basis": {
      "type": "object",
      "description": "ES, NQ, RTY: the day's basis that moved the index levels.",
      "properties": {
       "value": {
        "type": [
         "number",
         "null"
        ],
        "description": "Future minus index.",
        "x-unit": "index points"
       },
       "index": {
        "type": "string",
        "description": "The index the book is measured on: SPX, NDX or RUT."
       },
       "passes": {
        "type": "integer",
        "description": "Basis passes measured so far today."
       },
       "rule": {
        "type": "string",
        "description": "How the basis is made: the running median of the day's passes, rounded to the future's tick."
       }
      }
     },
     "source": {
      "type": "string",
      "description": "Futures: which index book and which basis."
     },
     "levels_at": {
      "type": "string",
      "description": "A finished day only: the levels minute (ET) `flip`, `call_wall` and `put_wall` were read at. Absent while the session runs.",
      "enum": [
       "15:45",
       "12:45"
      ]
     },
     "last_second": {
      "type": "object",
      "description": "A finished day only: the replaced fields as the session's last second had them.",
      "properties": {
       "flip": {
        "type": [
         "number",
         "null"
        ],
        "description": "Flip at the last second.",
        "x-unit": "index points"
       },
       "call_wall": {
        "type": [
         "number",
         "null"
        ],
        "description": "Call wall at the last second.",
        "x-unit": "index points"
       },
       "put_wall": {
        "type": [
         "number",
         "null"
        ],
        "description": "Put wall at the last second.",
        "x-unit": "index points"
       },
       "at_flip": {
        "type": "boolean",
        "description": "At the flip, at the last second."
       },
       "dealer_gamma": {
        "type": [
         "string",
         "null"
        ],
        "enum": [
         "long",
         "short",
         null
        ],
        "description": "The side, at the last second."
       }
      }
     },
     "next_update_s": {
      "type": "integer",
      "description": "When a new reading is worth asking for: 3 for SPX and ES, up to 300 for a five-minute book, 60 when the session is not open.",
      "x-unit": "seconds"
     },
     "gamma": {
      "type": "object",
      "description": "With `include=gamma`: the symbol's gamma by strike (the fields of `GET /api/v1/gamma`), or an `error`."
     },
     "error": {
      "type": "object",
      "description": "This symbol failed; the others in the call still answer.",
      "properties": {
       "code": {
        "type": "string",
        "description": "`unknown_symbol`, `no_data`, `no_basis` or `contract_not_live`."
       },
       "message": {
        "type": "string",
        "description": "What happened, in words."
       },
       "live": {
        "type": "array",
        "items": {
         "type": "string"
        },
        "description": "With `contract_not_live`: the contracts measured that day."
       }
      }
     }
    }
   },
   "OrdersError": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "object",
      "required": [
       "code",
       "message"
      ],
      "properties": {
       "code": {
        "type": "string",
        "description": "A stable code from the error catalog."
       },
       "message": {
        "type": "string",
        "description": "What happened, in words."
       }
      }
     }
    }
   },
   "OrdersOrder": {
    "type": "object",
    "required": [
     "oid",
     "t"
    ],
    "properties": {
     "oid": {
      "type": "string",
      "description": "Stable id of the parent order within the session."
     },
     "t": {
      "type": "string",
      "description": "When the parent order printed, ET (`HH:MM`)."
     },
     "g": {
      "type": [
       "number",
       "null"
      ],
      "description": "Dealer gamma the order ADDED, $M per index point. Null when it could not be priced (usually inside the last half hour of its own expiry). Null is not zero.",
      "x-unit": "$M per index point"
     },
     "kind": {
      "type": "string",
      "description": "The structure, from package reconstruction rather than leg count: `vertical`, `straddle`, `iron_condor`, `single` and the rest."
     },
     "legs": {
      "type": "string",
      "description": "The contract, for reading: `7740P/7750P`."
     },
     "lg": {
      "type": "array",
      "items": {
       "type": "array"
      },
      "description": "The legs as data: `[strike, \"C\" | \"P\", sign]`. The sign is what the package rule concluded the CUSTOMER did; 0 means it could not tell, never flat."
     },
     "exp": {
      "type": [
       "string",
       "null"
      ],
      "description": "The expiry carrying most of the order's gamma."
     },
     "size": {
      "type": [
       "integer",
       "null"
      ],
      "description": "Contracts in the parent order."
     },
     "net": {
      "type": [
       "number",
       "null"
      ],
      "description": "Net premium; a spread at its net.",
      "x-unit": "$M"
     },
     "flip": {
      "type": [
       "number",
       "null"
      ],
      "description": "Index points from the zero-gamma flip at that minute.",
      "x-unit": "index points"
     },
     "side": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "over",
       "under",
       null
      ],
      "description": "Which side of the flip."
     }
    }
   },
   "OrdersLedger": {
    "type": "object",
    "required": [
     "day",
     "orders"
    ],
    "properties": {
     "day": {
      "type": "string",
      "description": "The ET trading day."
     },
     "sym": {
      "type": "string",
      "description": "`SPX`: an order is placed against the measured book, which only SPX has."
     },
     "min_premium": {
      "type": "number",
      "description": "The premium floor the ledger was built at.",
      "x-unit": "USD"
     },
     "asof": {
      "type": [
       "string",
       "null"
      ],
      "description": "When the ledger was last rebuilt, ET."
     },
     "orders": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/OrdersOrder"
      },
      "description": "Sorted by what the order did to the book, largest first; unpriced orders after them."
     },
     "stats": {
      "type": "object",
      "description": "The whole session's counts, not the page returned.",
      "properties": {
       "n": {
        "type": "integer",
        "description": "Orders above the floor."
       },
       "net": {
        "type": "number",
        "description": "Their net premium.",
        "x-unit": "$M"
       },
       "no_side": {
        "type": "integer",
        "description": "Orders the package rule could not orient."
       },
       "no_gamma": {
        "type": "integer",
        "description": "Orders it could not price."
       },
       "gamma_up": {
        "type": "integer",
        "description": "Orders that added gamma."
       },
       "gamma_dn": {
        "type": "integer",
        "description": "Orders that removed gamma."
       }
      }
     },
     "note": {
      "type": "string",
      "description": "Present when the day has no ledger yet: the list is empty and this says so. Not an error."
     }
    }
   },
   "OrdersCheck": {
    "type": "object",
    "description": "One position judged at the minute: distances, the large orders near each short, the levels around it and the conditions with the label they add up to (TRADE, CAUTION or SKIP). Distances only, no level prices.",
    "properties": {
     "position_id": {
      "type": "string",
      "description": "The position."
     },
     "error": {
      "type": "string",
      "description": "This position could not be checked right now."
     }
    }
   },
   "OrdersPosition": {
    "type": "object",
    "description": "A short options position: `kind` (`condor`, `strangle`, `put_spread`, `call_spread`), `legs` (`lp`, `sp`, `sc`, `lc`, rising), `entry` (`HH:MM` ET) and optionally `conditions`, `label`, `alert` (`{ channel: telegram | email | webhook | off, url, before }`) and `session`.",
    "properties": {
     "id": {
      "type": "string",
      "description": "`p_...`, given by `add`."
     },
     "kind": {
      "type": "string",
      "description": "`condor`, `strangle`, `put_spread` or `call_spread`.",
      "enum": [
       "condor",
       "strangle",
       "put_spread",
       "call_spread"
      ]
     },
     "legs": {
      "type": "object",
      "description": "The strikes, rising, as the kind needs them.",
      "properties": {
       "lp": {
        "type": "number",
        "description": "Long put."
       },
       "sp": {
        "type": "number",
        "description": "Short put."
       },
       "sc": {
        "type": "number",
        "description": "Short call."
       },
       "lc": {
        "type": "number",
        "description": "Long call."
       }
      }
     },
     "entry": {
      "type": "string",
      "description": "Entry minute, `HH:MM` ET."
     },
     "conditions": {
      "type": "array",
      "items": {
       "type": "object"
      },
      "description": "`[{ id, on, threshold }]`: which conditions count, and where they switch on."
     },
     "label": {
      "type": "object",
      "description": "`{ caution, skip }`: how many conditions on make each label."
     },
     "alert": {
      "type": "object",
      "description": "`{ channel: telegram | email | webhook | off, url, before }`: where the alerts go and how many minutes before entry they start."
     },
     "session": {
      "type": "string",
      "description": "`YYYY-MM-DD`: the session the strikes were set against."
     }
    }
   },
   "LabError": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "string",
      "description": "What happened, in words."
     },
     "buy": {
      "type": "string",
      "description": "With 402: where to buy credits."
     },
     "holder": {
      "type": "string",
      "description": "With 409: the idea holding the conveyor."
     },
     "refunded": {
      "type": "integer",
      "description": "Credits refunded."
     }
    }
   },
   "GeoRecord": {
    "type": "object",
    "properties": {
     "uid": {
      "type": "string",
      "description": "Stable id of the record"
     },
     "feed": {
      "type": "string",
      "description": "Feed id, see /v1/sources or https://firmtape.com/geo/sources"
     },
     "sector": {
      "type": "string",
      "enum": [
       "gov",
       "air",
       "gps",
       "sea",
       "internet",
       "ground",
       "space",
       "attention"
      ]
     },
     "type": {
      "type": "string",
      "description": "new, update, level_raise, level_lower, cancel, spike, keyword or no_data"
     },
     "category": {
      "type": "string"
     },
     "severity": {
      "type": "integer",
      "description": "0 info, 1 routine, 2 notable, 3 critical"
     },
     "routine": {
      "type": "boolean",
      "description": "True for the records the pages hide by default"
     },
     "countries": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "ISO 3166-1 alpha-2"
     },
     "region": {
      "type": [
       "string",
       "null"
      ]
     },
     "title": {
      "type": "string"
     },
     "summary": {
      "type": "string"
     },
     "published_utc": {
      "type": "string",
      "format": "date-time",
      "description": "When the source published it"
     },
     "event_utc": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time",
      "description": "When the activity happens or starts, when the record says"
     },
     "first_seen_utc": {
      "type": "string",
      "format": "date-time",
      "description": "When FirmTape first read it"
     },
     "backfill": {
      "type": "boolean",
      "description": "Found in the source's own history rather than caught live"
     },
     "source": {
      "type": "string",
      "description": "The publisher to credit"
     },
     "url": {
      "type": "string",
      "description": "The record at its source"
     },
     "attribution": {
      "type": [
       "string",
       "null"
      ],
      "description": "Paid API only: the citation the source asks for"
     },
     "value": {
      "type": [
       "number",
       "null"
      ],
      "description": "Spikes: what was measured"
     },
     "baseline": {
      "type": [
       "number",
       "null"
      ],
      "description": "Spikes: its own normal"
     },
     "score": {
      "type": [
       "number",
       "null"
      ],
      "description": "Spikes: value against the normal"
     },
     "lat": {
      "type": [
       "number",
       "null"
      ],
      "description": "Paid API only"
     },
     "lon": {
      "type": [
       "number",
       "null"
      ],
      "description": "Paid API only"
     },
     "facts": {
      "type": "object",
      "description": "Paid API only: source-specific details"
     }
    }
   },
   "GeoError": {
    "type": "object",
    "properties": {
     "error": {
      "type": "string"
     }
    }
   }
  },
  "securitySchemes": {
   "Livebearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "An API key with the `live` scope (`ftpl_...`), made on Account, Integrations. A `lab` key (`labk_...`) also works."
   },
   "LivequeryKey": {
    "type": "apiKey",
    "in": "query",
    "name": "key",
    "description": "Legacy: the same key in the address, for platforms that cannot send headers."
   },
   "Ordersbearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "An API key with the `orders` scope (`ordk_...`), made on Account, Orders, on the Orders API or Team plan."
   },
   "Labbearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "An API key with the `lab` scope (`labk_...`), made on Account, LAB."
   },
   "Datasetbearer": {
    "type": "http",
    "scheme": "bearer",
    "description": "The account's `lab` key (`labk_...`) while the DATASET licence is active."
   },
   "Geokey": {
    "type": "http",
    "scheme": "bearer",
    "description": "A Geo API key (gk_...) from https://firmtape.com/account#geo-keys. The same key opens the MCP server at https://geo.firmtape.com/mcp."
   }
  }
 }
}