{
  "openapi": "3.1.0",
  "info": {
    "title": "FormulaSignal Intelligence API",
    "version": "1.0.0",
    "summary": "The historical supplement Record, available to software and agents.",
    "description": "FormulaSignal keeps a permanent, dated, evidence-backed Record of what U.S.\npre-workout products declare, what they cost per serving, and how that has\nchanged. This API is the controlled external gateway to it.\n\nIt returns answers, not the Record. There is no export, no product listing, no\ndocument endpoint and no query interface. Every response separates a declared\nfact from a captured commercial fact, from a deterministic calculation, from\nresearch context, from a regulatory record, from a documented caution, from a\nlimitation, because collapsing those is how supplement information becomes\nwrong.\n\nFormulaSignal performs no laboratory testing and gives no medical or\nsuitability advice.\n\nStart at https://formulasignal.com/developers.\n\n**This document is generated from the capability manifest.** An operation\nhere exists because a capability exists, so it cannot describe a route that\nis not served and cannot omit one that is.",
    "contact": {
      "name": "FormulaSignal",
      "url": "https://formulasignal.com/developers",
      "email": "hello@formulasignal.com"
    },
    "license": {
      "name": "Commercial. Redistribution depends on your plan."
    }
  },
  "externalDocs": {
    "description": "Developer documentation, quickstart and the evidence model.",
    "url": "https://formulasignal.com/developers"
  },
  "servers": [
    {
      "url": "https://formulasignal.com"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "record",
      "description": "Ask one bounded question of the Record."
    },
    {
      "name": "products",
      "description": "Canonical identity, current state, history, economics."
    },
    {
      "name": "signals",
      "description": "Confirmed, reviewed changes and the release feed."
    },
    {
      "name": "context",
      "description": "Research and regulatory context for declared ingredients."
    },
    {
      "name": "category",
      "description": "Bounded category movement."
    },
    {
      "name": "ledger",
      "description": "Category Ledger editions. Requires the ledger:read scope."
    },
    {
      "name": "watch",
      "description": "One bound account's watchlist. Requires watch:manage."
    },
    {
      "name": "meta",
      "description": "Self-description: version, coverage, liveness."
    }
  ],
  "paths": {
    "/api/v1/health": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "health",
        "summary": "Liveness. The only route that needs no key.",
        "description": "Returns 200 while the Worker is serving. It says nothing about whether any manufacturer source is currently readable: source health is data-level state and is reported inside answers and at https://formulasignal.com/status, never as an API outage.",
        "security": [],
        "responses": {
          "200": {
            "description": "The Worker is serving.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "api_version"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    },
                    "api_version": {
                      "type": "string",
                      "examples": [
                        "v1"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/record/search": {
      "post": {
        "tags": [
          "record"
        ],
        "operationId": "search_record",
        "summary": "Answer one specific, bounded question from FormulaSignal's covered pre-workout Record.",
        "description": "Answer one specific, bounded question from FormulaSignal's covered pre-workout Record.\n\n**When to use.** Use for a single natural-language question about a covered product: what it declares now, whether it changed, what it costs per serving, or which covered products meet one stated numeric condition.\n\n**What it cannot provide.** It cannot list the database, export products, answer medical or suitability questions, or answer about a product FormulaSignal does not cover.\n\nRequires the `record:search` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "maxLength": 300
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "record:search",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_search_record",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/products/resolve": {
      "post": {
        "tags": [
          "products"
        ],
        "operationId": "resolve_product",
        "summary": "Resolve a brand, product name, or alias to one canonical covered product.",
        "description": "Resolve a brand, product name, or alias to one canonical covered product.\n\n**When to use.** Call this first whenever you hold a user-supplied product name and need the canonical product_id every other capability takes.\n\n**What it cannot provide.** It never guesses. An ambiguous or generic name returns the candidate list and no match, and an uncovered product returns no match at all.\n\nRequires the `products:read` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "identifier"
                ],
                "properties": {
                  "identifier": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "products:read",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_resolve_product",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/products/{product_id}": {
      "get": {
        "tags": [
          "products"
        ],
        "operationId": "get_product_record",
        "summary": "Return the controlled current Record for one covered product: identity, serving configuration, declared ingredients, captured price context, and freshness.",
        "description": "Return the controlled current Record for one covered product: identity, serving configuration, declared ingredients, captured price context, and freshness.\n\n**When to use.** Use when you need what a product declares right now, with the dates and limitations attached.\n\n**What it cannot provide.** It returns no preserved source bytes, no internal capture identifiers, no review notes, and no verdict about whether the product is good or suitable.\n\nRequires the `products:read` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "product_id",
            "in": "path",
            "required": true,
            "description": "A canonical FormulaSignal product id. Resolve names with `resolve_product` first; this endpoint does not accept a brand name.",
            "schema": {
              "type": "string"
            },
            "example": "cellucor-c4-original"
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "products:read",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_get_product_record",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/products/{product_id}/history": {
      "get": {
        "tags": [
          "products"
        ],
        "operationId": "get_formula_history",
        "summary": "Return the controlled historical timeline for one covered product, with each state classified by what it can actually support.",
        "description": "Return the controlled historical timeline for one covered product, with each state classified by what it can actually support.\n\n**When to use.** Use to find out whether a product has changed, how deep the recoverable history is, and which states are comparable to each other.\n\n**What it cannot provide.** It never presents an observation window as an exact reformulation date, and it never reports an unreviewed candidate as a confirmed change.\n\nRequires the `history:read` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "product_id",
            "in": "path",
            "required": true,
            "description": "A canonical FormulaSignal product id. Resolve names with `resolve_product` first; this endpoint does not accept a brand name.",
            "schema": {
              "type": "string"
            },
            "example": "cellucor-c4-original"
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "history:read",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_get_formula_history",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/signals/{signal_id}": {
      "get": {
        "tags": [
          "signals"
        ],
        "operationId": "explain_signal",
        "summary": "Explain one approved FormulaSignal Signal: a confirmed, reviewed change between two comparable product states.",
        "description": "Explain one approved FormulaSignal Signal: a confirmed, reviewed change between two comparable product states.\n\n**When to use.** Use when a product record or history response named a signal_id and you need the before state, the after state, the observation window, and the evidence.\n\n**What it cannot provide.** It has no access to unreviewed candidates, review deliberation, or the internal review queue. A signal_id that is not approved returns not found.\n\nRequires the `signals:read` scope.\nEach distinct signal named in a successful answer spends one unit of the key's daily signal budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "signal_id",
            "in": "path",
            "required": true,
            "description": "The signal id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "signals:read",
        "x-formulasignal-entity-kind": "signal",
        "x-formulasignal-mcp-tool": "formulasignal_explain_signal",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/signals": {
      "get": {
        "tags": [
          "signals"
        ],
        "operationId": "list_signal_changes",
        "summary": "Return approved FormulaSignal Signals in release order, newest first, for incremental polling.",
        "description": "Return approved FormulaSignal Signals in release order, newest first, for incremental polling.\n\n**When to use.** Use to keep a system in step with the Record. Pass `since` with the release date you last saw, or follow `next_cursor`, and filter by product, brand, dimension or category.\n\n**What it cannot provide.** It returns no unreviewed candidate and no change event that fails the publication gate, and it is not an export of the product table.\n\nRequires the `signals:read` scope.\nEach distinct signal named in a successful answer spends one unit of the key's daily signal budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "until",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "product_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "brand",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dimension",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "signals:read",
        "x-formulasignal-entity-kind": "signal",
        "x-formulasignal-mcp-tool": "formulasignal_list_signal_changes",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/products/compare": {
      "post": {
        "tags": [
          "products"
        ],
        "operationId": "compare_products",
        "summary": "Compare two or three covered products on consistent FormulaSignal criteria.",
        "description": "Compare two or three covered products on consistent FormulaSignal criteria.\n\n**When to use.** Use when a user asks how covered products differ on formula, serving economics, stimulant load, or record depth.\n\n**What it cannot provide.** It returns no overall winner and no suitability judgement. A product whose depth is REGISTERED is refused rather than compared, because a guessed label beside a measured one implies a precision the Record does not have.\n\nRequires the `comparisons:create` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "product_ids"
                ],
                "properties": {
                  "product_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 2,
                    "maxItems": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "comparisons:create",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_compare_products",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/products/{product_id}/serving-economics": {
      "get": {
        "tags": [
          "products"
        ],
        "operationId": "get_serving_economics",
        "summary": "Return the captured commercial facts and the deterministic price arithmetic for one covered product.",
        "description": "Return the captured commercial facts and the deterministic price arithmetic for one covered product.\n\n**When to use.** Use for package price, price basis, serving count, price per serving, and the date each was observed.\n\n**What it cannot provide.** It never calls an observed price a list price unless the Record recorded it as one, and it never reports a promotional-versus-list difference as a price change.\n\nRequires the `economics:read` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "product_id",
            "in": "path",
            "required": true,
            "description": "A canonical FormulaSignal product id. Resolve names with `resolve_product` first; this endpoint does not accept a brand name.",
            "schema": {
              "type": "string"
            },
            "example": "cellucor-c4-original"
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "economics:read",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_get_serving_economics",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/research/context": {
      "post": {
        "tags": [
          "context"
        ],
        "operationId": "get_research_context",
        "summary": "Return the dose range used in the selected evidence set for named ingredients, with the citation and its limitations.",
        "description": "Return the dose range used in the selected evidence set for named ingredients, with the citation and its limitations.\n\n**When to use.** Use to place a declared amount against published work. Pass ingredient ids, or a product_id to read the ingredients that product declares.\n\n**What it cannot provide.** A dose comparison is not evidence of effectiveness, safety, or suitability for any person, and the response says so on every record.\n\nRequires the `research:read` scope.\nEach distinct ingredient named in a successful answer spends one unit of the key's daily ingredient budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "ingredient_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 5
                  },
                  "product_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "research:read",
        "x-formulasignal-entity-kind": "ingredient",
        "x-formulasignal-mcp-tool": "formulasignal_get_research_context",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/regulatory/context": {
      "post": {
        "tags": [
          "context"
        ],
        "operationId": "get_regulatory_context",
        "summary": "Return regulatory records and documented cautions for named ingredients, each carrying the class of record it actually is.",
        "description": "Return regulatory records and documented cautions for named ingredients, each carrying the class of record it actually is.\n\n**When to use.** Use when you need to know what filings, advisories, label warnings, interactions, or enforcement actions FormulaSignal holds for an ingredient.\n\n**What it cannot provide.** A regulatory filing is never returned as an approval, a warning, or a finding of harm. Nothing here is medical clearance.\n\nRequires the `regulatory:read` scope.\nEach distinct ingredient named in a successful answer spends one unit of the key's daily ingredient budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "ingredient_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 5
                  },
                  "product_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "regulatory:read",
        "x-formulasignal-entity-kind": "ingredient",
        "x-formulasignal-mcp-tool": "formulasignal_get_regulatory_context",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/category/snapshot": {
      "get": {
        "tags": [
          "category"
        ],
        "operationId": "get_category_snapshot",
        "summary": "Return a bounded summary of approved Signals and coverage state across the covered pre-workout set for a date window.",
        "description": "Return a bounded summary of approved Signals and coverage state across the covered pre-workout set for a date window.\n\n**When to use.** Use for 'what changed in the category recently'. Pass from and to dates; the window is capped and the page size is capped.\n\n**What it cannot provide.** It is not an export. It returns approved Signals inside one bounded window, never the product table and never the underlying Record.\n\nRequires the `category:read` scope.\nEach distinct signal named in a successful answer spends one unit of the key's daily signal budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "maximum": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "category:read",
        "x-formulasignal-entity-kind": "signal",
        "x-formulasignal-mcp-tool": "formulasignal_get_category_snapshot",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/ledger": {
      "get": {
        "tags": [
          "ledger"
        ],
        "operationId": "list_ledger_editions",
        "summary": "List the published Category Ledger editions: period, status, data-as-of date and the count released in each.",
        "description": "List the published Category Ledger editions: period, status, data-as-of date and the count released in each.\n\n**When to use.** Call this first to find which edition covers a period, then fetch that edition by id.\n\n**What it cannot provide.** It returns no figures from inside an edition and no product data. A closed edition's identity never changes, so this list is safe to cache.\n\nRequires the `ledger:read` scope.\nEach distinct signal named in a successful answer spends one unit of the key's daily signal budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "ledger:read",
        "x-formulasignal-entity-kind": "signal",
        "x-formulasignal-mcp-tool": "formulasignal_list_ledger_editions",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/ledger/{edition_id}": {
      "get": {
        "tags": [
          "ledger"
        ],
        "operationId": "get_ledger_edition",
        "summary": "Return one Category Ledger edition: the executive summary, confirmed changes released in the period, category benchmarks with their cohorts, serving economics, the product comparison, and the limitations.",
        "description": "Return one Category Ledger edition: the executive summary, confirmed changes released in the period, category benchmarks with their cohorts, serving economics, the product comparison, and the limitations.\n\n**When to use.** Use when an agent needs the month's category intelligence with every denominator attached, or needs to cite a figure a customer is reading on the web edition.\n\n**What it cannot provide.** It never returns an unreviewed candidate, a Signal the publication gate refuses, a preserved-source path, or a hash of any stored source. The edition_hash it does return is a hash of the published document itself, so a machine citation and the page a person was sent can be checked against each other.\n\nRequires the `ledger:read` scope.\nEach distinct signal named in a successful answer spends one unit of the key's daily signal budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "edition_id",
            "in": "path",
            "required": true,
            "description": "The edition id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "ledger:read",
        "x-formulasignal-entity-kind": "signal",
        "x-formulasignal-mcp-tool": "formulasignal_get_ledger_edition",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/watch": {
      "get": {
        "tags": [
          "watch"
        ],
        "operationId": "list_watched_products",
        "summary": "Return the watchlist of the one Founding Pro account this key is bound to, with each product's monitoring state.",
        "description": "Return the watchlist of the one Founding Pro account this key is bound to, with each product's monitoring state.\n\n**When to use.** Use to answer what a person is watching, when each product was last read, when the next check is due, and whether anything is currently unreadable.\n\n**What it cannot provide.** It reaches exactly one account, the one bound to this key, and no other. It returns no candidate detail, no source URL and no internal identifier, and it is not a way to read the covered set.\n\nRequires the `watch:manage` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "watch:manage",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_list_watched_products",
        "x-formulasignal-mutates": false
      },
      "post": {
        "tags": [
          "watch"
        ],
        "operationId": "watch_product",
        "summary": "Add one covered product to the bound account's watchlist.",
        "description": "Add one covered product to the bound account's watchlist.\n\n**When to use.** Use when a person asks to start watching a product. Resolve the product first if you were given a name rather than an id.\n\n**What it cannot provide.** It cannot add a product outside the covered set, exceed the account's watch limit, or act on an account this key is not bound to. It does not start a subscription and refuses when the account is not entitled.\n\nRequires the `watch:manage` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "product_id"
                ],
                "properties": {
                  "product_id": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "watch:manage",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_watch_product",
        "x-formulasignal-mutates": true
      }
    },
    "/api/v1/watch/{product_id}": {
      "delete": {
        "tags": [
          "watch"
        ],
        "operationId": "unwatch_product",
        "summary": "Remove one product from the bound account's watchlist.",
        "description": "Remove one product from the bound account's watchlist.\n\n**When to use.** Use when a person asks to stop watching a product. Future alerts stop; everything already delivered stays.\n\n**What it cannot provide.** It cannot delete an account, cancel a subscription, or remove history. Removing a watch never removes what was already sent.\n\nRequires the `watch:manage` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "product_id",
            "in": "path",
            "required": true,
            "description": "A canonical FormulaSignal product id. Resolve names with `resolve_product` first; this endpoint does not accept a brand name.",
            "schema": {
              "type": "string",
              "maxLength": 120
            },
            "example": "cellucor-c4-original"
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "watch:manage",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_unwatch_product",
        "x-formulasignal-mutates": true
      }
    },
    "/api/v1/watch/receipt": {
      "get": {
        "tags": [
          "watch"
        ],
        "operationId": "get_watch_receipt",
        "summary": "Return the monitoring receipt for one period: valid checks, attempts that returned nothing, recoveries, and confirmed Signals.",
        "description": "Return the monitoring receipt for one period: valid checks, attempts that returned nothing, recoveries, and confirmed Signals.\n\n**When to use.** Use to answer whether anything a person watches changed in a period, and what monitoring did in the period whether or not anything did.\n\n**What it cannot provide.** A count of valid checks is not a claim that a product held still. It reports no candidate, no source URL and no internal identifier, and a period the monitoring ledger does not reach is reported as partly covered rather than as zero.\n\nRequires the `watch:manage` scope.\nEach distinct product named in a successful answer spends one unit of the key's daily product budget. Asking twice about the same one costs once.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 7
            }
          },
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "watch:manage",
        "x-formulasignal-entity-kind": "product",
        "x-formulasignal-mcp-tool": "formulasignal_get_watch_receipt",
        "x-formulasignal-mutates": false
      }
    },
    "/api/v1/record/version": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "get_record_version",
        "summary": "Return the public Record version, the methodology version, the supported category, coverage counts, and source freshness.",
        "description": "Return the public Record version, the methodology version, the supported category, coverage counts, and source freshness.\n\n**When to use.** Call this to state which version of FormulaSignal intelligence an answer came from, or to detect that coverage has moved.\n\n**What it cannot provide.** It exposes no implementation detail, no internal scoring, and no proprietary methodology text.\n\nRequires the `framework:read` scope.\nSpends no daily entity budget.",
        "security": [
          {
            "apiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Origin"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Envelope"
          },
          "400": {
            "$ref": "#/components/responses/Refused"
          },
          "401": {
            "$ref": "#/components/responses/Refused"
          },
          "403": {
            "$ref": "#/components/responses/Refused"
          },
          "404": {
            "$ref": "#/components/responses/Refused"
          },
          "405": {
            "$ref": "#/components/responses/Refused"
          },
          "413": {
            "$ref": "#/components/responses/Refused"
          },
          "429": {
            "$ref": "#/components/responses/Refused"
          },
          "500": {
            "$ref": "#/components/responses/Refused"
          }
        },
        "x-formulasignal-scope": "framework:read",
        "x-formulasignal-entity-kind": null,
        "x-formulasignal-mcp-tool": "formulasignal_get_record_version",
        "x-formulasignal-mutates": false
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fsk_{environment}_{keyId}_{secret}",
        "description": "`Authorization: Bearer fsk_live_...`. The header `X-FormulaSignal-Key: fsk_live_...`\nis accepted as an alternative, because an MCP client configuration is a JSON file a\nperson edits by hand and \"remember to write Bearer in front of it\" is a support ticket.\n\nThe key is never accepted in a query string, so it cannot reach an access log or a\nreferrer header. Create one at https://formulasignal.com/developers/keys."
      }
    },
    "parameters": {
      "Origin": {
        "name": "X-FormulaSignal-Origin",
        "in": "header",
        "required": false,
        "description": "Optional attribution: `mcp` when the call comes from the FormulaSignal MCP server, `api` otherwise. Self-reported and recorded on the usage row so a customer can see which surface their traffic came from. Nothing reads it to decide what a caller may do, so a wrong value costs only the accuracy of that customer's own reporting.",
        "schema": {
          "type": "string",
          "enum": [
            "api",
            "mcp"
          ]
        }
      }
    },
    "responses": {
      "Envelope": {
        "description": "A FormulaSignal answer. `status` says what kind of answer it is, and every value except `supported` is still a real answer about the Record rather than a fact about the product.",
        "headers": {
          "X-Request-Id": {
            "schema": {
              "type": "string"
            },
            "description": "Quote this if you contact support."
          },
          "RateLimit-Limit": {
            "schema": {
              "type": "string"
            }
          },
          "RateLimit-Remaining": {
            "schema": {
              "type": "string"
            }
          },
          "RateLimit-Reset": {
            "schema": {
              "type": "string"
            },
            "description": "Seconds until the window resets."
          },
          "RateLimit-Policy": {
            "schema": {
              "type": "string",
              "enum": [
                "minute",
                "hour",
                "day"
              ]
            }
          },
          "Cache-Control": {
            "schema": {
              "type": "string"
            },
            "description": "Always `private, no-store, max-age=0, must-revalidate`. Every answer is entitlement-specific, so no shared cache may hold one."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            }
          }
        }
      },
      "Refused": {
        "description": "A refusal, in the same envelope shape.",
        "headers": {
          "X-Request-Id": {
            "schema": {
              "type": "string"
            }
          },
          "Retry-After": {
            "schema": {
              "type": "string"
            },
            "description": "Seconds. Present on 429 only, for both `rate_limited` and `extraction_limit_reached`. On an extraction refusal, waiting is the only thing that helps: rewording the question or narrowing the window spends more of the same daily budget."
          },
          "Cache-Control": {
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": [
          "request_id",
          "capability",
          "status",
          "summary",
          "verified_facts",
          "commercial_facts",
          "calculations",
          "research_context",
          "regulatory_records",
          "documented_cautions",
          "limitations",
          "source_references",
          "confidence",
          "record_version",
          "record_as_of",
          "record_timestamp",
          "methodology_version",
          "disclaimer"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "pattern": "^req_[0-9a-f]{24}$"
          },
          "capability": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "supported",
              "partial",
              "stale",
              "under_review",
              "ambiguous",
              "unsupported",
              "refused"
            ],
            "description": "What kind of answer this is. Every value except `supported` is still a real answer about the Record rather than a fact about the product."
          },
          "product": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ProductIdentity"
              },
              {
                "type": "null"
              }
            ]
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductIdentity"
            }
          },
          "candidates": {
            "type": "array",
            "description": "Present when `status` is `ambiguous`. Pick one; never assume the first.",
            "items": {
              "$ref": "#/components/schemas/ProductIdentity"
            }
          },
          "summary": {
            "type": "string",
            "description": "One sentence saying what kind of answer this is."
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HistoryState"
            }
          },
          "signals": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "comparison": {
            "type": "object"
          },
          "coverage": {
            "type": "object"
          },
          "ledger": {
            "type": "object"
          },
          "watch": {
            "type": "object"
          },
          "next_cursor": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "verified_facts": {
            "type": "array",
            "description": "What a captured source literally declares. A declared fact.",
            "items": {
              "$ref": "#/components/schemas/VerifiedFact"
            }
          },
          "commercial_facts": {
            "type": "array",
            "description": "Dated commercial observations, each with its price basis. An observation.",
            "items": {
              "$ref": "#/components/schemas/CommercialFact"
            }
          },
          "calculations": {
            "type": "array",
            "description": "Deterministic arithmetic FormulaSignal performed, with the operands to redo it. Never a manufacturer figure.",
            "items": {
              "$ref": "#/components/schemas/Calculation"
            }
          },
          "research_context": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ResearchContext"
            }
          },
          "regulatory_records": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RegulatoryRecord"
            }
          },
          "documented_cautions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RegulatoryRecord"
            }
          },
          "limitations": {
            "type": "array",
            "description": "Always present, including when empty. Read it.",
            "items": {
              "type": "string"
            }
          },
          "source_references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SourceReference"
            }
          },
          "confidence": {
            "$ref": "#/components/schemas/Confidence"
          },
          "record_version": {
            "type": "string",
            "description": "The Record data-state id. Two answers sharing it came from one committed state."
          },
          "record_as_of": {
            "type": "string",
            "format": "date"
          },
          "record_timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "The newest dated observation the Record holds. Deliberately not \"now\"."
          },
          "methodology_version": {
            "type": "string",
            "examples": [
              "record-v1"
            ]
          },
          "disclaimer": {
            "type": "string",
            "examples": [
              "FormulaSignal provides evidence-backed supplement information and does not provide individualized medical advice."
            ]
          }
        }
      },
      "ProductIdentity": {
        "type": "object",
        "required": [
          "product_id",
          "brand",
          "name",
          "variant"
        ],
        "properties": {
          "product_id": {
            "type": "string"
          },
          "brand": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "variant": {
            "oneOf": [
              {
                "type": "string",
                "description": "The observed flavor. Flavor is a version dimension, not a footnote."
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "VerifiedFact": {
        "type": "object",
        "required": [
          "field"
        ],
        "properties": {
          "field": {
            "type": "string"
          },
          "value": {
            "$ref": "#/components/schemas/FactValue"
          },
          "before": {
            "$ref": "#/components/schemas/FactValue"
          },
          "after": {
            "$ref": "#/components/schemas/FactValue"
          },
          "confirmed": {
            "type": "boolean"
          }
        }
      },
      "FactValue": {
        "type": "object",
        "properties": {
          "declared_name": {
            "type": "string",
            "description": "Exactly as printed or published by the brand."
          },
          "normalized_ingredient": {
            "type": "string"
          },
          "amount": {
            "oneOf": [
              {
                "type": "number",
                "description": "Null means the source named the ingredient without disclosing an amount. It never means zero."
              },
              {
                "type": "null"
              }
            ]
          },
          "unit": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "CommercialFact": {
        "type": "object",
        "required": [
          "field",
          "value",
          "unit",
          "observed_at"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "`list_price` only where `price_basis` is LIST. Otherwise `observed_price`."
          },
          "value": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "unit": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "price_basis": {
            "oneOf": [
              {
                "type": "string",
                "description": "LIST, UNKNOWN, UNRECORDED, or the two recorded bases on a change row. Never inferred from the number."
              },
              {
                "type": "null"
              }
            ]
          },
          "observed_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date"
              },
              {
                "type": "null"
              }
            ]
          },
          "seller": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Calculation": {
        "type": "object",
        "required": [
          "field",
          "value",
          "unit",
          "method",
          "operands"
        ],
        "properties": {
          "field": {
            "type": "string"
          },
          "value": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "unit": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "description": "The arithmetic in words, so a reader can redo it."
          },
          "operands": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {
                  "oneOf": [
                    {
                      "type": "number"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "ResearchContext": {
        "type": "object",
        "required": [
          "normalized_ingredient",
          "comparison",
          "comparison_statement",
          "limitations"
        ],
        "properties": {
          "normalized_ingredient": {
            "type": "string"
          },
          "ingredient_form": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "declared_amount_mg": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "selected_low_mg": {
            "oneOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "selected_high_mg": {
            "oneOf": [
              {
                "type": "number",
                "description": "Null on both bounds means FormulaSignal found no studied range. That is a finding, not a gap."
              },
              {
                "type": "null"
              }
            ]
          },
          "dose_basis": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "evidence_type": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "A1",
                  "A2",
                  "A3",
                  "A4"
                ],
                "description": "A1 government or institutional, A2 peer-reviewed, A3 trademark holder, A4 other secondary."
              },
              {
                "type": "null"
              }
            ]
          },
          "citation": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "citation_url": {
            "oneOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ]
          },
          "comparison": {
            "type": "string",
            "enum": [
              "INSIDE",
              "BELOW",
              "ABOVE",
              "NO_RANGE",
              "NOT_DECLARED",
              "NO_PRODUCT_CONTEXT"
            ]
          },
          "comparison_statement": {
            "type": "string"
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RegulatoryRecord": {
        "type": "object",
        "required": [
          "normalized_ingredient",
          "record_class",
          "statement",
          "evidence_type",
          "citation",
          "citation_url",
          "accessed_at"
        ],
        "properties": {
          "normalized_ingredient": {
            "type": "string"
          },
          "record_class": {
            "type": "string",
            "enum": [
              "REGULATORY_RECORD",
              "LABEL_WARNING",
              "SAFETY_ADVISORY",
              "INTERACTION",
              "FDA_ACTION"
            ],
            "description": "REGULATORY_RECORD means a filing exists and says nothing about safety on its own. It appears in `regulatory_records`; the other four appear in `documented_cautions`. The two never share a heading."
          },
          "statement": {
            "type": "string"
          },
          "evidence_type": {
            "type": "string",
            "enum": [
              "A1",
              "A2",
              "A3",
              "A4"
            ]
          },
          "citation": {
            "type": "string"
          },
          "citation_url": {
            "type": "string",
            "format": "uri"
          },
          "accessed_at": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "HistoryState": {
        "type": "object",
        "properties": {
          "observed_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date"
              },
              {
                "type": "null"
              }
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "CURRENT",
              "CHANGE_BEFORE",
              "CHANGE_AFTER",
              "RICHEST_PRIOR_PANEL"
            ]
          },
          "classes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "METADATA_ONLY",
                "LABEL_BEARING",
                "FORMULA_COMPARABLE",
                "PRICE_COMPARABLE"
              ]
            }
          },
          "declared_components": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "declared_with_amounts": {
            "oneOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "flavor": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "source": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SourceReference"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "SourceReference": {
        "type": "object",
        "required": [
          "source_id",
          "title",
          "url",
          "observed_at",
          "source_type"
        ],
        "properties": {
          "source_id": {
            "type": "string",
            "description": "Derived and opaque. Stable for a given URL and observation date."
          },
          "title": {
            "type": "string"
          },
          "url": {
            "oneOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ]
          },
          "observed_at": {
            "oneOf": [
              {
                "type": "string",
                "format": "date",
                "description": "When FormulaSignal read the source. Not a manufacture date."
              },
              {
                "type": "null"
              }
            ]
          },
          "source_type": {
            "type": "string",
            "enum": [
              "first_party_current",
              "first_party_archived",
              "regulatory_filing",
              "retailer_listing",
              "secondary_coverage",
              "distributor_listing",
              "authoritative_reference",
              "peer_reviewed_reference",
              "interested_party_reference",
              "secondary_reference",
              "other"
            ]
          },
          "evidence_class": {
            "oneOf": [
              {
                "type": "string",
                "description": "Only P1, P2 and R1 support a CONFIRMED product-state label."
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Confidence": {
        "type": "object",
        "required": [
          "level",
          "explanation"
        ],
        "properties": {
          "level": {
            "type": "string",
            "enum": [
              "high",
              "moderate",
              "low",
              "none"
            ],
            "description": "Evidence quality. Never how certain the writing sounds, and never \"certain\"."
          },
          "explanation": {
            "type": "string"
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "request_id",
          "capability",
          "status",
          "error",
          "disclaimer"
        ],
        "properties": {
          "request_id": {
            "type": "string"
          },
          "capability": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "refused"
            ]
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_credentials",
                  "insufficient_scope",
                  "plan_entitlement_required",
                  "key_suspended",
                  "rate_limited",
                  "extraction_limit_reached",
                  "invalid_request",
                  "payload_too_large",
                  "not_found",
                  "unsupported_capability",
                  "method_not_allowed",
                  "internal_error"
                ],
                "description": "A stable machine code. Branch on this, never on `message`, which is written for a person and may be reworded."
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object"
              },
              "documentation": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "limitations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "record_version": {
            "type": "string"
          },
          "record_as_of": {
            "type": "string"
          },
          "record_timestamp": {
            "type": "string"
          },
          "methodology_version": {
            "type": "string"
          },
          "disclaimer": {
            "type": "string"
          }
        }
      }
    }
  },
  "x-formulasignal-plans": [
    {
      "id": "free",
      "label": "Free",
      "rate": {
        "perMinute": 30,
        "perHour": 500,
        "perDay": 2000
      },
      "daily_distinct_products": 15,
      "daily_distinct_signals": 15,
      "daily_distinct_ingredients": 25,
      "self_service": true,
      "commercial_redistribution": false
    },
    {
      "id": "trial",
      "label": "Trial",
      "rate": {
        "perMinute": 10,
        "perHour": 100,
        "perDay": 250
      },
      "daily_distinct_products": 10,
      "daily_distinct_signals": 10,
      "daily_distinct_ingredients": 15,
      "self_service": false,
      "commercial_redistribution": false
    },
    {
      "id": "pro",
      "label": "Pro",
      "rate": {
        "perMinute": 30,
        "perHour": 600,
        "perDay": 3000
      },
      "daily_distinct_products": 25,
      "daily_distinct_signals": 25,
      "daily_distinct_ingredients": 40,
      "self_service": false,
      "commercial_redistribution": false
    },
    {
      "id": "developer",
      "label": "Developer",
      "rate": {
        "perMinute": 60,
        "perHour": 2000,
        "perDay": 15000
      },
      "daily_distinct_products": 40,
      "daily_distinct_signals": 40,
      "daily_distinct_ingredients": 80,
      "self_service": false,
      "commercial_redistribution": true
    },
    {
      "id": "enterprise",
      "label": "Enterprise",
      "rate": {
        "perMinute": 120,
        "perHour": 6000,
        "perDay": 60000
      },
      "daily_distinct_products": null,
      "daily_distinct_signals": null,
      "daily_distinct_ingredients": null,
      "self_service": false,
      "commercial_redistribution": true
    }
  ],
  "x-formulasignal-hard-limits": {
    "queryChars": 300,
    "identifierChars": 120,
    "productsPerResponse": 25,
    "answerStatements": 12,
    "answerCharsPerStatement": 800,
    "compareProducts": 3,
    "sourceReferences": 12,
    "sourceExcerptChars": 0,
    "historyStates": 12,
    "researchIngredients": 5,
    "researchRecordsPerProduct": 12,
    "snapshotWindowDays": 400,
    "snapshotPageSize": 25,
    "requestBytes": 8192
  }
}
