{"openapi":"3.1.0","info":{"title":"Biotech Catalyst Sentinel API","description":"Machine-first FDA PDUFA-catalyst feed for AI agents. Upcoming FDA decision dates assembled from SEC 8-K/6-K filings.\n\n**Scope, stated plainly.** A PDUFA date is public only because a company chose to disclose it — the FDA does not publish goal dates in advance. This feed is built from SEC 8-K/6-K filings, so it covers issuers that announce their dates (which skews to small- and mid-cap sponsors, for whom one decision is material) and misses large-cap sponsors who never file an 8-K about a single drug. We publish our measured coverage, unflattered, next to the accuracy numbers at `GET /v1/accuracy` — check it before you build on this.\n\n**One FDA decision is one record.** Several public companies often file on the same decision (sponsor, drug originator, royalty holder); they are merged into a single catalyst whose `tickers` array lists every one of them. Filter on `tickers`, not the single `ticker`, or you will miss catalysts a partner is exposed to. Both paid routes also accept `?ticker=` to apply that same membership server-side (plus records filed by the ticker's issuer), at the flat per-request price — the free `GET /v1/coverage?ticker=` counts what a filtered call would return, so check it before paying for a lookup that may be empty.\n\nFrom the same builder: [Crypto ETF Sentinel](https://cryptoetfsentinel.com) — SEC EDGAR spot crypto-ETF filing intelligence.\n\n**Rate limit:** no hard limit is enforced today; please keep requests at or below ~5/second (see `GET /v1/meta` → `limits`).\n\n**Payment scope.** Pricing is flat per HTTP request: every request to a paid route costs the listed amount regardless of query parameters, and a `since`-cursor drain of N pages costs N requests. Payment buys the single response — not a time window, not a per-URL license. The resource URL echoed in the 402 challenge identifies the request; it does not scope the payment. A request answered with any status ≥ 400 is never charged: settlement runs only after a successful handler, and malformed queries are rejected 422 before payment.\n\n**If a paid response is lost in transit.** Settlement is on-chain and happens *after* the handler produces the data but *before* the response reaches you, so a connection dropped at the wrong moment means the payment settled and you hold nothing. There is no replay: the authorization you signed carries a single-use nonce, and re-sending the same payment header is rejected as already-used rather than serving the data again. A retry is a NEW authorization at the normal price. Two consequences worth designing around — write the response to durable storage before acknowledging it internally, and treat `/v1/events` (a resumable `since` cursor, where a lost page costs one re-fetch of known-missing ids) as the cheaper tier to build a pipeline on. If you lose a response you believe you paid for, mail the contact address with the transaction hash.\n\n**Reporting a wrong date.** Every record ships with the filing that asserted it (`source_url`) and the sentence we read (`source_quote`) so you can check us; if those disagree with the record, that is a defect and we want it. Mail the contact address with the `catalyst_id` and the filing. A repair is published like any other change — as a `corrected` event on `/v1/events` (distinct from `date_extension`, which is the FDA moving; `corrected` is us being wrong) — so a subscriber sees the fix arrive on the same stream as the data. Corrections to a scored outcome appear on `/v1/accuracy` marked `operator_approved`. We do not quote a turnaround: verification against the primary source is a human step.\n\n**Failures & availability.** Successful responses use the `{data, meta}` envelope; failures never do — 400/500 are RFC 7807 problem details, 422 is FastAPI's `{detail: [...]}`, 402 is the x402 challenge. There is no 429 today (no hard rate limit; if one ever lands it will be 429 with `Retry-After`, announced in the changelog). The API serves from a local store, so an SEC/openFDA outage never surfaces as a request-time error — it shows up as data freshness (`GET /v1/meta` → `freshness`). A 5xx means the service itself is failing: retry with backoff. Free routes support `HEAD` for liveness probes; the paid routes are GET-only.\n\n**Disclaimer:** informational only; not investment advice. No warranty. Every record links to its source filing and carries a timestamp.","version":"0.8.0","x-logo":{"url":"https://biotechcatalystsentinel.com/assets/logo.png"},"contact":{"email":"contact@biotechcatalystsentinel.com"},"termsOfService":"https://biotechcatalystsentinel.com/terms.html","x-guidance":"Upcoming FDA PDUFA decision dates and their change stream for US-listed biotechs, relayed from SEC 8-K/6-K filings with the filing quote and EDGAR source; every past date is scored against the FDA record and published free at /v1/accuracy. Served dates are signed and anchored on-chain; GET /attest/verify?uid= and the public recipe at https://biotechcatalystsentinel.com/verify.html let anyone check one."},"paths":{"/":{"get":{"tags":["health"],"summary":"Root","description":"Service banner.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Root  Get"}}}}},"security":[]}},"/health":{"get":{"tags":["health"],"summary":"Health","description":"Liveness + a cheap DB readiness probe.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}},"security":[]}},"/v1/meta":{"get":{"tags":["meta"],"summary":"Meta","description":"Service metadata: what this is, the endpoints, live feed counts, and disclaimer.","operationId":"meta_v1_meta_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_dict_str__Any__"}}}}},"security":[]}},"/v1/accuracy":{"get":{"tags":["accuracy"],"summary":"Accuracy","description":"Public accuracy track record (free; audit it before you pay).\n\nReturns JSON to agents/crawlers (the unchanged machine contract, now including the\nper-decision ``track_record``) and a site-styled HTML page to human browsers.\n``?summary=1`` serves the cheap scalars-and-counts view (S61) — the default payload\nis byte-identical to before the parameter existed.","operationId":"accuracy_v1_accuracy_get","parameters":[{"name":"summary","in":"query","required":false,"schema":{"type":"boolean","description":"Return the scalars-and-counts view (roughly a tenth of the full size): all headline and coverage figures, windows and notes, with the row-level ledgers elided (each replaced by a `_rows` count). The full document — every receipt, miss and unresolved prediction, individually checkable — is served without this parameter, unchanged. JSON only; browsers always get the HTML page.","default":false,"title":"Summary"},"description":"Return the scalars-and-counts view (roughly a tenth of the full size): all headline and coverage figures, windows and notes, with the row-level ledgers elided (each replaced by a `_rows` count). The full document — every receipt, miss and unresolved prediction, individually checkable — is served without this parameter, unchanged. JSON only; browsers always get the HTML page."}],"responses":{"200":{"description":"The accuracy document. Branch on `available` FIRST, then on `summary`: `available: false` is the degraded shape and carries no `summary` key even when `?summary=1` was passed. `recall_misses` and `unresolved_predictions` are OMITTED entirely — never served empty — when the committed baseline predates those ledgers, because an empty table would read as 'we missed nothing'. A browser (text/html Accept AND a Mozilla User-Agent) receives a styled HTML page at this same URL instead of JSON; the response varies on Accept and User-Agent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_Union_AccuracyOut__AccuracySummaryOut__AccuracyUnavailableOut__"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/v1/coverage":{"get":{"tags":["coverage"],"summary":"Coverage","description":"What we hold for one filer — free, and never a forward date.\n\nAn empty ticker-filtered result on the paid feed cannot tell you whether no FDA\ndecision is pending, whether the company is one we cannot see (private, non-SEC,\nor never discloses PDUFA dates in 8-Ks), or whether we missed it. This route answers\nwith the facts we can verify: how many of the filer's filings matched our EDGAR\ndiscovery query, how many disclosed a date, when, how many live folders we keep, and\nhow many rows the paid route would serve right now (a count — the dates are the paid\nproduct). Read ``note`` before acting on an absence.","operationId":"coverage_v1_coverage_get","parameters":[{"name":"ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":12},{"type":"null"}],"description":"Ticker symbol (case-insensitive). Exactly one of ticker / cik.","title":"Ticker"},"description":"Ticker symbol (case-insensitive). Exactly one of ticker / cik."},{"name":"cik","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":10,"pattern":"^\\d+$"},{"type":"null"}],"description":"SEC CIK, with or without leading zeros. Exactly one of ticker / cik.","title":"Cik"},"description":"SEC CIK, with or without leading zeros. Exactly one of ticker / cik."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_CoverageOut_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/v1/events":{"get":{"tags":["events"],"summary":"Events","description":"Return catalyst change events (full history, forward dates included).\n\n``ticker=`` narrows the stream to one position server-side (issuer CIK, lead\nticker, or co-filer alias — the membership ``/v1/coverage`` counts). The filter is\napplied before ``limit``, so a filtered ``since``-drain reaches every matching\nevent with no gaps; ``meta.next_since``/``has_more`` work exactly as unfiltered. A\nfiltered stream still carries the ``retracted`` markers of records exposed to the\nticker — including a record merged into one that is (you may see a retraction for\nan id you never held; drop it).\n\nOrdering depends on ``since`` — ascending while draining a cursor, newest-first for a\nplain browse. See ``list_events`` for why: a newest-first cursored page silently skips\nevery event between the caller's position and the newest page whenever the backlog\nexceeds ``limit``, and a skipped event is a missed PDUFA date change.\n\n**Retention & completeness:** events are append-only with no pruning — ``since=0``\ndrains the full history back to feed inception, and ``event_id`` values are stable\n(they are the cursor; they are never renumbered or reused). When two records are\ndiscovered to be the same catalyst, the merged-away ``catalyst_id`` emits a\n``retracted`` event whose ``source_quote`` names the surviving id, and its earlier\nevents are re-parented to the survivor so the timeline stays whole; the one deletion\nthe system ever performs is dropping a loser's row that duplicates a survivor row\n(same filing, same event type — two filers reporting one fact), so a re-drain reflects\nthe merged, deduplicated timeline.\n\n**Replaying to current state:** process events ascending by ``event_id``, keyed by\n``catalyst_id``. ``new_date_assigned`` and ``date_extension`` set the current expected\ndate to ``new_date``; ``decision_outcome`` resolves that cycle (see ``outcome`` — a\n``crl`` is non-terminal, a later resubmission date can re-open the same\n``catalyst_id``); ``retracted`` means stop relying on the id (re-point per\n``source_quote``); ``restored`` revives it. ``corrected`` sets the current expected\ndate to ``new_date`` exactly like ``date_extension``, but records a **vendor data\nrepair** — never count it as an FDA action; its ``delta_days`` measures our repair,\nnot an FDA move. The current expected date for a live id is the ``new_date`` of its\nlatest date-bearing event. Forward compatibility: never count an event type you do\nnot recognize as an FDA action, but do apply its ``new_date`` (when set) if you\nmirror served state from this stream.\n\nPriced flat per request ($0.05): each page of a ``since``-cursor drain is one\nrequest, so draining an N-page backlog costs N calls — query parameters do not\nchange the price. Amounts in the 402 challenge are atomic USDC units (6 decimals:\n``50000`` = $0.05). GET only (no HEAD): a request answered ≥ 400 is never charged.\n\n**Free preview:** ``GET /v1/events/sample`` returns a static, fictional response of\nexactly this shape — one row per event type, ``corrected`` and ``retracted``\nincluded — at no charge. Build and test your decoder against it before paying.","operationId":"events_v1_events_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Return only events newer than this event_id. WITH a cursor the page is ASCENDING (oldest first) so a backlog drains without gaps; without one you get the newest events first. Follow meta.next_since and repeat while meta.has_more.","title":"Since"},"description":"Return only events newer than this event_id. WITH a cursor the page is ASCENDING (oldest first) so a backlog drains without gaps; without one you get the newest events first. Follow meta.next_since and repeat while meta.has_more."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max events to return.","default":100,"title":"Limit"},"description":"Max events to return."},{"name":"ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":12,"pattern":"^\\S+$"},{"type":"null"}],"description":"Only events whose record is filed by this ticker's issuer or exposes it as a co-filer in tickers[] (case-insensitive; the same membership /v1/coverage counts). Composes with since: LIMIT counts matched events, so a filtered backlog drains without gaps. An unknown ticker returns an empty 200. Does not change the price.","title":"Ticker"},"description":"Only events whose record is filed by this ticker's issuer or exposes it as a co-filer in tickers[] (case-insensitive; the same membership /v1/coverage counts). Composes with since: LIMIT counts matched events, so a filtered backlog drains without gaps. An unknown ticker returns an empty 200. Does not change the price."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_list_EventOut__"}}}},"402":{"description":"Payment required — the x402 challenge. The same document is base64'd into the `PAYMENT-REQUIRED` response header; the body mirrors it because clients read one or the other. Amounts in `accepts[].amount` are ATOMIC USDC units (6 decimals — e.g. `20000` = $0.02, `50000` = $0.05; this description is shared by both paid routes, so read THIS route's actual price from the challenge's `accepts[].amount`, never from an example). Sign one option and retry the same request with the payment header. Sent with `Cache-Control: no-store`.\n\nNote: the discovery extension's input schema advertises a `method` enum wider than this API supports (SDK boilerplate) — the paid routes are GET-only, as `info.input.method` states.\n\nTwo other bodies also use 402 and carry no `accepts`: `{\"error\": \"Payment required\"}` if the challenge could not be built, and `{\"error\": \"Settlement failed\", \"details\": …}` if a verified payment did not settle. Branch on `accepts` being present, not on the status alone. Neither charges you.","headers":{"PAYMENT-REQUIRED":{"description":"The same challenge, base64-encoded JSON.","schema":{"type":"string"}},"Cache-Control":{"description":"Always `no-store` on a challenge.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRequiredOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-payment-info":{"protocols":["x402"],"price":{"amount":"0.05","currency":"USDC","chain":"base"}}}},"/v1/events/sample":{"get":{"tags":["events"],"summary":"Events Sample","description":"A free, static, FICTIONAL sample of the paid ``/v1/events`` response.\n\nExactly the paid route's shape, rendered by the same serializer over five invented\nrows — one per event type, so a decoder meets the whole vocabulary before paying:\n``new_date_assigned`` → ``date_extension`` (with ``prior_date`` + ``delta_days``) →\n``decision_outcome`` (a CRL) on one catalyst; a ``corrected`` vendor repair\n(synthetic accession, null ``source_url``, ``delta_days`` measuring the repair);\nand a ``retracted`` merge announcement (the null-heavy lifecycle row, its\n``source_quote`` naming where to re-point).\n\n**Every value is fictional** — drugs, companies, application numbers (a 9999xx\nrange no real FDA application occupies), accessions and dates are invented; URLs\nare shape-only. ``event_id`` values sit far above the real stream's id space:\nnever feed them to ``since`` on the paid route. For real, verifiable history see\nthe free ``/v1/accuracy`` track record.\n\nDeterministic by design: ``new_date_upcoming`` / ``days_to_date`` are computed\nagainst the fixed reference date 2026-08-01, not the request clock, so the payload\nis byte-stable and safe to snapshot-test against. The ``meta`` block is rendered as\nthe final page of a cursored drain (``has_more`` false, ``next_since`` set) so\nevery envelope key is exercised too.","operationId":"events_sample_v1_events_sample_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_list_EventOut__"}}}}},"security":[]}},"/v1/catalysts/upcoming":{"get":{"tags":["catalysts"],"summary":"Upcoming","description":"Return upcoming PDUFA catalysts in the next *within_days* days.\n\n``ticker=`` narrows to one position server-side: records filed by that ticker's\nissuer **or** carrying it in ``tickers[]`` (a co-filer on a merged record) — the\nexact membership the free ``GET /v1/coverage?ticker=`` counts as\n``upcoming_on_feed``, so check there before paying for a lookup that may be empty.\nNote a record matched via the issuer's CIK can lack the ticker in its own\n``tickers[]`` (an unmapped or delisted listing).\n\nPriced flat per request ($0.02): query parameters do not change the price, and the\nresource URL echoed in the 402 challenge identifies the request — it does not scope\nthe payment. Amounts in the 402 challenge are atomic USDC units (6 decimals:\n``20000`` = $0.02). GET only (no HEAD): a request answered ≥ 400 is never charged.\n\n**Free preview:** ``GET /v1/catalysts/upcoming/sample`` returns a static, fictional\nresponse of exactly this shape at no charge — build and test your decoder against it\nbefore paying.","operationId":"upcoming_v1_catalysts_upcoming_get","parameters":[{"name":"within_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Forward window in days (1–365).","default":365,"title":"Within Days"},"description":"Forward window in days (1–365)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max catalysts to return.","default":200,"title":"Limit"},"description":"Max catalysts to return."},{"name":"ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":12,"pattern":"^\\S+$"},{"type":"null"}],"description":"Only records filed by this ticker's issuer or exposing it as a co-filer in tickers[] (case-insensitive; the same membership /v1/coverage counts). An unknown ticker returns an empty 200, not an error. Does not change the price.","title":"Ticker"},"description":"Only records filed by this ticker's issuer or exposing it as a co-filer in tickers[] (case-insensitive; the same membership /v1/coverage counts). An unknown ticker returns an empty 200, not an error. Does not change the price."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_list_CatalystOut__"}}}},"402":{"description":"Payment required — the x402 challenge. The same document is base64'd into the `PAYMENT-REQUIRED` response header; the body mirrors it because clients read one or the other. Amounts in `accepts[].amount` are ATOMIC USDC units (6 decimals — e.g. `20000` = $0.02, `50000` = $0.05; this description is shared by both paid routes, so read THIS route's actual price from the challenge's `accepts[].amount`, never from an example). Sign one option and retry the same request with the payment header. Sent with `Cache-Control: no-store`.\n\nNote: the discovery extension's input schema advertises a `method` enum wider than this API supports (SDK boilerplate) — the paid routes are GET-only, as `info.input.method` states.\n\nTwo other bodies also use 402 and carry no `accepts`: `{\"error\": \"Payment required\"}` if the challenge could not be built, and `{\"error\": \"Settlement failed\", \"details\": …}` if a verified payment did not settle. Branch on `accepts` being present, not on the status alone. Neither charges you.","headers":{"PAYMENT-REQUIRED":{"description":"The same challenge, base64-encoded JSON.","schema":{"type":"string"}},"Cache-Control":{"description":"Always `no-store` on a challenge.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentRequiredOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"x-payment-info":{"protocols":["x402"],"price":{"amount":"0.02","currency":"USDC","chain":"base"}}}},"/v1/catalysts/upcoming/sample":{"get":{"tags":["catalysts"],"summary":"Upcoming Sample","description":"A free, static, FICTIONAL sample of the paid ``/v1/catalysts/upcoming`` response.\n\nExactly the paid route's shape — same envelope, same fields, same vocabularies —\nrendered by the same serializer over three invented rows (a fully-populated\ngrounded extraction, a human-reviewed extension, and a null-heavy row so every\nnullable field is met as null at least once). **Every value is fictional**: drug,\ncompany, tickers, application numbers (a 9999xx range no real FDA application\noccupies) and dates are invented, and URLs are shape-only (they resolve to\nnothing). For real, verifiable rows see the free ``/v1/accuracy`` track record.\n\nDeterministic by design: derived fields (``age_days``) are computed against the\nfixed reference date 2026-08-01, not the request clock, so the payload is\nbyte-stable and safe to snapshot-test against.","operationId":"upcoming_sample_v1_catalysts_upcoming_sample_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse_list_CatalystOut__"}}}}},"security":[]}},"/attest/verify":{"get":{"tags":["attestation"],"summary":"Verify","description":"Return the signed attestation JSON + anchor proof for one UID.\n\nThe uid is normalised before lookup (P-E): upper-case and prefix-less forms of a\nknown uid resolve; malformed input is a 422, distinct from a well-formed miss (404).","operationId":"verify_attest_verify_get","parameters":[{"name":"uid","in":"query","required":true,"schema":{"type":"string","title":"Uid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Verify Attest Verify Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/attest/accuracy/{feed_id}":{"get":{"tags":["attestation"],"summary":"Accuracy","description":"Return the feed's accuracy stats from the ledger.","operationId":"accuracy_attest_accuracy__feed_id__get","parameters":[{"name":"feed_id","in":"path","required":true,"schema":{"type":"string","title":"Feed Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Accuracy Attest Accuracy  Feed Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}}},"components":{"schemas":{"AccuracyCoverageOut":{"properties":{"predictions_total":{"type":"integer","title":"Predictions Total","description":"Prediction ROWS in the report generation (see baseline_source)."},"baseline_decisions_matched":{"type":"integer","title":"Baseline Decisions Matched","description":"The report generation's own count of FDA DECISIONS matched (frozen only on the committed fallback — see baseline_source). NOT the recall numerator — see matched_note before attempting arithmetic with predictions_total."},"implausible_dropped":{"type":"integer","title":"Implausible Dropped","description":"Baseline predictions dropped from the median. Counts three distinct reasons (no matched application number, no computable delta, over the plausibility bound), so it does not mean 'bad forecasts'."},"unmatched":{"type":"integer","title":"Unmatched","description":"Baseline prediction ROWS that matched no FDA decision. See matched_note for the unit mismatch."},"matched_note":{"type":"string","title":"Matched Note","description":"Why baseline_decisions_matched + unmatched need not equal predictions_total (different units)."},"addressable_caught":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Addressable Caught","description":"Numerator of recall_addressable. Null on a baseline artifact predating the field."},"decisions_addressable":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decisions Addressable","description":"Denominator of recall_addressable. Null on a baseline artifact predating the field."},"decisions_addressable_note":{"type":"string","title":"Decisions Addressable Note","description":"What the addressable denominator includes and excludes, and why it is a band not a measurement."},"recall_addressable":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Recall Addressable","description":"addressable_caught / decisions_addressable (0-1). Null when either operand is null."},"decisions_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decisions Total","description":"Denominator of recall_floor: EVERY original NDA/BLA decision in the action window, unconditionally."},"decisions_caught":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decisions Caught","description":"Numerator of recall_floor. Null on a baseline artifact predating the field."},"recall_floor":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Recall Floor","description":"decisions_caught / decisions_total (0-1) — unconditional coverage, with no addressability conditioning. The lower and more honest of the two recall figures."},"sponsor_method_counts":{"anyOf":[{"$ref":"#/components/schemas/AccuracySponsorMethodCountsOut"},{"type":"null"}],"description":"How sponsors were resolved, making the addressable filter checkable. Null on an older baseline."},"recall_floor_note":{"type":"string","title":"Recall Floor Note","description":"How recall_floor and recall_addressable differ, and which direction the conditioning flatters."},"crl_decisions":{"type":"integer","title":"Crl Decisions","description":"All rejections on the record: date_accuracy.matched_crls + additional_rejections.count + any dated-less rejection carrying an application number."},"rejections":{"$ref":"#/components/schemas/AccuracyRejectionCoverageOut","description":"Rejection coverage, deliberately its own figure and never folded into recall_addressable."}},"type":"object","required":["predictions_total","baseline_decisions_matched","implausible_dropped","unmatched","matched_note","decisions_addressable_note","recall_floor_note","crl_decisions","rejections"],"title":"AccuracyCoverageOut","description":"Both directions of coverage, with both operands of every ratio exposed.\n\n⚑ Not to be confused with :class:`~drug_catalyst_sentinel.api.models.CoverageOut`,\nwhich is per-filer coverage for ``GET /v1/coverage``. This block is recall against the\nFDA's own decision record."},"AccuracyDateStatsOut":{"properties":{"decisions_matched":{"type":"integer","title":"Decisions Matched","description":"Scored FDA decisions behind the headline — baseline plus live. The `n`."},"matched_approvals":{"type":"integer","title":"Matched Approvals","description":"Of decisions_matched, how many were approvals. Equals decisions_matched - matched_crls."},"matched_crls":{"type":"integer","title":"Matched Crls","description":"Of decisions_matched, how many were rejections (Complete Response Letters) that carried a verifiable FDA decision date. Counts baseline plus live, so it is expected to exceed the frozen coverage.rejections.caught band."},"median_delta_days":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Median Delta Days","description":"Median signed error in days between the date we published and the date the FDA acted. Null when the baseline artifact carries no computable median."},"within_7_days":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Within 7 Days","description":"Fraction of scored decisions within 7 days (0-1). Null on a baseline with no computable figure."},"within_30_days":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Within 30 Days","description":"Fraction of scored decisions within 30 days (0-1). Null on a baseline with no computable figure."}},"type":"object","required":["decisions_matched","matched_approvals","matched_crls"],"title":"AccuracyDateStatsOut","description":"The headline date-accuracy figures: how close our dates were to the FDA's."},"AccuracyOut":{"properties":{"available":{"type":"boolean","enum":[true],"title":"Available","description":"True — the backtest is published. Branch on this FIRST; false selects the degraded shape."},"baseline_source":{"type":"string","enum":["live","committed_fallback"],"title":"Baseline Source","description":"Provenance of the report generation these figures come from: 'live' — regenerated daily from the full corpus against a fresh openFDA pull — or 'committed_fallback' — the frozen committed 2023-2025 artifact, served only when no valid stored live report exists (pre-first-generation, or a stored report rejected at load)."},"generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generated At","description":"When the live report was generated (ISO-8601 UTC). Null on the committed fallback."},"headline":{"type":"string","title":"Headline","description":"One sentence stating n, the median error and the within-30-days figure."},"date_accuracy":{"$ref":"#/components/schemas/AccuracyDateStatsOut","description":"How close our published dates were to the FDA's."},"coverage":{"$ref":"#/components/schemas/AccuracyCoverageOut","description":"How much of the FDA's decision record we caught, both ways."},"windows":{"$ref":"#/components/schemas/AccuracyWindowsOut","description":"The filing and action windows every figure is measured over."},"additional_rejections":{"$ref":"#/components/schemas/AdditionalRejectionsOut","description":"Rejections counted for coverage but excluded from the headline. Always present, possibly empty."},"recall_misses":{"anyOf":[{"$ref":"#/components/schemas/RecallMissesOut"},{"type":"null"}],"description":"The decisions we did NOT catch, enumerated. ⚑ OMITTED from the payload — not served empty — when the baseline predates this ledger; an empty table would read as 'we missed nothing'."},"unresolved_predictions":{"anyOf":[{"$ref":"#/components/schemas/UnresolvedPredictionsOut"},{"type":"null"}],"description":"Our own published dates that never scored, each with a reason. ⚑ OMITTED from the payload — not served empty — when the baseline predates this ledger."},"method":{"type":"string","title":"Method","description":"How the figures are produced, in prose."},"caveat":{"type":"string","title":"Caveat","description":"What these numbers do not claim. Read before quoting them."},"verify":{"type":"string","title":"Verify","description":"How to check the figures against primary sources yourself."},"track_record_note":{"type":"string","title":"Track Record Note","description":"Squares the receipt count against the headline n; they differ by design."},"track_record":{"items":{"$ref":"#/components/schemas/AccuracyReceiptOut"},"type":"array","title":"Track Record","description":"Every scored decision the baseline attributes to a sponsor. REQUIRED — discriminates this shape."},"unlisted_note":{"type":"string","title":"Unlisted Note","description":"Why some scored decisions have no receipt row."},"unlisted_withheld_forward":{"type":"integer","title":"Unlisted Withheld Forward","description":"Scored decisions counted in the statistics whose row is withheld from BOTH ledgers because their predicted date is still in the future (the FDA acted early; publishing the row would print a forward date). The count closes the row arithmetic — see unlisted_note."},"unlisted_scored_decisions":{"items":{"$ref":"#/components/schemas/AccuracyReceiptOut"},"type":"array","title":"Unlisted Scored Decisions","description":"Scored decisions with no receipt row, same row shape, so every counted decision is enumerable. REQUIRED — discriminates this shape."},"paid_endpoints":{"items":{"$ref":"#/components/schemas/PaidEndpointOut"},"type":"array","title":"Paid Endpoints","description":"Pointers to the paid routes, with their prices and free samples."}},"type":"object","required":["available","baseline_source","generated_at","headline","date_accuracy","coverage","windows","additional_rejections","method","caveat","verify","track_record_note","track_record","unlisted_note","unlisted_withheld_forward","unlisted_scored_decisions"],"title":"AccuracyOut","description":"The full accuracy document — the default shape of ``GET /v1/accuracy``.\n\nAssembled at two sites: the report itself comes from\n``live_accuracy.blend_report``, while ``paid_endpoints`` is injected by the router."},"AccuracyReceiptOut":{"properties":{"sponsor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sponsor","description":"Sponsor as the FDA records it. Null where unattributed."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"Exchange ticker where we resolved one. Null otherwise."},"drug_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drug Name","description":"Drug name. Null where the source record carries none."},"application_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Application Number","description":"FDA application number (e.g. NDA216158, BLA761416) — look it up at api.fda.gov. Null where unmatched."},"predicted_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Predicted Date","description":"The PDUFA date we published (ISO), from the filing."},"actual_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Actual Date","description":"The date the FDA acted (ISO). Null where the decision could not be dated."},"error_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Error Days","description":"Signed actual_date - predicted_date in days. Negative = the FDA acted EARLY. Null when undatable."},"outcome":{"type":"string","title":"Outcome","description":"What the FDA did. Not enumerated here: this vocabulary spans several sources and is open."},"had_prior_crl":{"type":"boolean","title":"Had Prior Crl","description":"True when this application had an earlier Complete Response Letter, i.e. a resubmission cycle."},"source":{"type":"string","title":"Source","description":"Where the decision record came from. Not enumerated: merges provenance and origin vocabularies."},"match_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Match Tier","description":"How the prediction was joined to the FDA record. Null where the committed artifact does not record the join unambiguously. Not enumerated: it falls back to a `source` value."},"operator_approved":{"type":"boolean","title":"Operator Approved","description":"True when a human adjudicated this row against primary sources. Latched; never set by automation."},"filing_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filing Url","description":"EDGAR link to the filing that asserted predicted_date. Null where the operands do not exist — which is every committed baseline receipt until the next regeneration."}},"type":"object","required":["outcome","had_prior_crl","source","operator_approved"],"title":"AccuracyReceiptOut","description":"One scored decision: what we said, what the FDA did, and how to check it.\n\nThe same row shape serves ``track_record`` and ``unlisted_scored_decisions``."},"AccuracyRejectionCoverageOut":{"properties":{"addressable":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Addressable","description":"Frozen baseline denominator: addressable FDA rejections. Null on a baseline predating the field."},"caught":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Caught","description":"Frozen baseline numerator: addressable rejections we caught. Null on a baseline predating the field."},"recall":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Recall","description":"caught / addressable (0-1). Null when either operand is null."},"note":{"type":"string","title":"Note","description":"Why rejection coverage is published separately from approvals recall, and why date_accuracy.matched_crls is a different population from `caught`. Read before comparing them."}},"type":"object","required":["note"],"title":"AccuracyRejectionCoverageOut","description":"Rejection coverage, reported separately from approvals recall — never blended."},"AccuracySponsorMethodCountsOut":{"properties":{"cik":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cik","description":"Resolved by SEC CIK. Counted as addressable."},"name_exact":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Name Exact","description":"Resolved by exact sponsor-name match. Counted as addressable."},"name_fuzzy":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Name Fuzzy","description":"Resolved only by a fuzzy name match. DROPPED from the addressable denominator — a guessed match would move the denominator with the matcher's aggressiveness rather than with SEC observability."},"none":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"None","description":"Sponsor could not be resolved to a filer. Dropped from the addressable denominator."}},"type":"object","title":"AccuracySponsorMethodCountsOut","description":"How each in-window FDA decision's sponsor was resolved to a filer.\n\nMakes the addressable filter checkable arithmetic rather than a frozen assertion:\n``decisions_addressable`` keeps ``cik + name_exact`` and drops ``none + name_fuzzy``\n(``live_accuracy.py:1218-1222``)."},"AccuracySummaryOut":{"properties":{"available":{"type":"boolean","enum":[true],"title":"Available","description":"True — the backtest is published. Branch on this FIRST."},"baseline_source":{"type":"string","enum":["live","committed_fallback"],"title":"Baseline Source","description":"Identical to full mode — the report generation's provenance."},"generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generated At","description":"Identical to full mode — null on the committed fallback."},"summary":{"type":"boolean","enum":[true],"title":"Summary","description":"True — this is the elided view. REQUIRED — discriminates this shape."},"summary_note":{"type":"string","title":"Summary Note","description":"Points at the full auditable document. REQUIRED — discriminates this shape."},"headline":{"type":"string","title":"Headline","description":"One sentence stating n, the median error and the within-30-days figure."},"date_accuracy":{"$ref":"#/components/schemas/AccuracyDateStatsOut","description":"Identical to full mode — scalars are never elided."},"coverage":{"$ref":"#/components/schemas/AccuracyCoverageOut","description":"Identical to full mode — scalars are never elided."},"windows":{"$ref":"#/components/schemas/AccuracyWindowsOut","description":"Identical to full mode."},"additional_rejections":{"$ref":"#/components/schemas/AdditionalRejectionsSummaryOut","description":"Elided form: `items` -> `items_rows`."},"recall_misses":{"anyOf":[{"$ref":"#/components/schemas/RecallMissesSummaryOut"},{"type":"null"}],"description":"Elided form. ⚑ OMITTED, not empty, when the baseline predates the ledger."},"unresolved_predictions":{"anyOf":[{"$ref":"#/components/schemas/UnresolvedPredictionsSummaryOut"},{"type":"null"}],"description":"Elided form. ⚑ OMITTED, not empty, when the baseline predates the ledger."},"method":{"type":"string","title":"Method","description":"How the figures are produced, in prose."},"caveat":{"type":"string","title":"Caveat","description":"What these numbers do not claim."},"verify":{"type":"string","title":"Verify","description":"How to check the figures against primary sources yourself."},"track_record_note":{"type":"string","title":"Track Record Note","description":"Kept verbatim from full mode, describing the ledger this view replaced with a count."},"track_record_rows":{"type":"integer","title":"Track Record Rows","description":"Row count of the `track_record` ledger served in full mode. REQUIRED — discriminates this shape."},"unlisted_note":{"type":"string","title":"Unlisted Note","description":"Kept verbatim from full mode."},"unlisted_withheld_forward":{"type":"integer","title":"Unlisted Withheld Forward","description":"Kept verbatim from full mode (already a scalar count)."},"unlisted_scored_decisions_rows":{"type":"integer","title":"Unlisted Scored Decisions Rows","description":"Row count of the `unlisted_scored_decisions` ledger served in full mode. REQUIRED — discriminates this shape."},"paid_endpoints":{"items":{"$ref":"#/components/schemas/PaidEndpointOut"},"type":"array","title":"Paid Endpoints","description":"Pointers to the paid routes, with their prices and free samples."}},"type":"object","required":["available","baseline_source","generated_at","summary","summary_note","headline","date_accuracy","coverage","windows","additional_rejections","method","caveat","verify","track_record_note","track_record_rows","unlisted_note","unlisted_withheld_forward","unlisted_scored_decisions_rows"],"title":"AccuracySummaryOut","description":"``?summary=1``: every scalar kept, every ledger replaced by its row count.\n\n⚑ The elision is a RECURSIVE structural rule, not a fixed list: any list-valued key\nnamed ``items`` becomes ``items_rows`` at any depth (``accuracy.py:39-55``), which is\nfour places in today's payload — two of them nested inside ``recall_misses``."},"AccuracyUnavailableOut":{"properties":{"available":{"type":"boolean","enum":[false],"title":"Available","description":"False — no figures are published. REQUIRED — discriminates this shape."},"note":{"type":"string","title":"Note","description":"Why the document is unavailable. REQUIRED — discriminates this shape."},"paid_endpoints":{"items":{"$ref":"#/components/schemas/PaidEndpointOut"},"type":"array","title":"Paid Endpoints","description":"Pointers to the paid routes; served even when the backtest is not."}},"type":"object","required":["available","note"],"title":"AccuracyUnavailableOut","description":"The degraded shape: no backtest artifact could be read.\n\n⚑ Reachable with ``?summary=1`` too — the summary view returns this dict unchanged\n(``accuracy.py:69``), so it carries NO ``summary`` key. Branch on ``available`` first."},"AccuracyWindowsOut":{"properties":{"filing":{"items":{"type":"string"},"type":"array","maxItems":2,"minItems":2,"title":"Filing","description":"[start, end] ISO dates of the SEC filing window the backtest corpus was drawn from."},"action":{"items":{"type":"string"},"type":"array","maxItems":2,"minItems":2,"title":"Action","description":"[start, end] ISO dates of the FDA action window. The end advances past the frozen baseline as live decisions resolve, so it always covers the rows actually shown."}},"type":"object","required":["filing","action"],"title":"AccuracyWindowsOut","description":"The two time windows every figure above is measured over."},"AdditionalRejectionRowOut":{"properties":{"drug_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drug Name","description":"Drug named in the filing."},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","description":"Company as it appears on the filing."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"Exchange ticker where resolved. Null otherwise."},"disclosed":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Disclosed","description":"ISO date the company DISCLOSED the rejection — not the FDA decision date, which is why it is unscored."},"source_quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Quote","description":"The sentence in the filing that grounds this rejection, verbatim."},"filing_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filing Url","description":"EDGAR link to the filing. Null where the accession is not EDGAR-shaped."}},"type":"object","title":"AdditionalRejectionRowOut","description":"A rejection we captured from the company's own filing but cannot independently date."},"AdditionalRejectionsOut":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of such rejections."},"note":{"type":"string","title":"Note","description":"Why these are excluded from the headline n and the median, and how they upgrade."},"items":{"items":{"$ref":"#/components/schemas/AdditionalRejectionRowOut"},"type":"array","title":"Items","description":"The rows. Empty list when there are none."}},"type":"object","required":["count","note"],"title":"AdditionalRejectionsOut","description":"Rejections counted for coverage but deliberately excluded from the headline."},"AdditionalRejectionsSummaryOut":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of such rejections."},"note":{"type":"string","title":"Note","description":"Why these are excluded from the headline n and the median, and how they upgrade."},"items_rows":{"type":"integer","title":"Items Rows","description":"Row count of the `items` ledger served in full mode."}},"type":"object","required":["count","note","items_rows"],"title":"AdditionalRejectionsSummaryOut","description":"``?summary=1`` form of :class:`AdditionalRejectionsOut` — the ledger replaced by its length."},"ApiResponse_CoverageOut_":{"properties":{"data":{"$ref":"#/components/schemas/CoverageOut"},"meta":{"$ref":"#/components/schemas/Meta"}},"type":"object","required":["data"],"title":"ApiResponse[CoverageOut]"},"ApiResponse_Union_AccuracyOut__AccuracySummaryOut__AccuracyUnavailableOut__":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/AccuracyOut"},{"$ref":"#/components/schemas/AccuracySummaryOut"},{"$ref":"#/components/schemas/AccuracyUnavailableOut"}],"title":"Data"},"meta":{"$ref":"#/components/schemas/Meta"}},"type":"object","required":["data"],"title":"ApiResponse[Union[AccuracyOut, AccuracySummaryOut, AccuracyUnavailableOut]]"},"ApiResponse_dict_str__Any__":{"properties":{"data":{"additionalProperties":true,"type":"object","title":"Data"},"meta":{"$ref":"#/components/schemas/Meta"}},"type":"object","required":["data"],"title":"ApiResponse[dict[str, Any]]"},"ApiResponse_list_CatalystOut__":{"properties":{"data":{"items":{"$ref":"#/components/schemas/CatalystOut"},"type":"array","title":"Data"},"meta":{"$ref":"#/components/schemas/Meta"}},"type":"object","required":["data"],"title":"ApiResponse[list[CatalystOut]]"},"ApiResponse_list_EventOut__":{"properties":{"data":{"items":{"$ref":"#/components/schemas/EventOut"},"type":"array","title":"Data"},"meta":{"$ref":"#/components/schemas/Meta"}},"type":"object","required":["data"],"title":"ApiResponse[list[EventOut]]"},"CatalystOut":{"properties":{"catalyst_id":{"type":"integer","title":"Catalyst Id","description":"The stable key correlating this record across tiers — the same `catalyst_id` the /v1/events stream carries, and the id a merge retraction's `merged_into` points at. Use THIS to join the two paid tiers: `source_accession` is not unique across a record's history (a merged record's timeline holds co-filers' filings). A record that leaves this feed after a merge announces it on /v1/events as `retracted`.","examples":[174]},"drug_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drug Name","description":"The drug as the SPONSOR'S FILING names it (brand, generic, code, or a combination phrase) — not normalised to an FDA vocabulary. One drug may appear with different spellings across sponsors' records."},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","description":"The lead filer (the company whose filing set the served state)."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"The lead filer's primary ticker; null when unlisted or unmapped. Filter on `tickers` (the whole exposure list), not this scalar."},"tickers":{"items":{"type":"string"},"type":"array","title":"Tickers","description":"Every public company exposed to this decision, lead issuer first, co-filers alphabetically after. One FDA decision is ONE record here, but the sponsor, the drug's originator and a royalty holder may each file their own 8-K about it — filter on this array, not on the scalar `ticker`, or you will miss catalysts a partner is exposed to. Empty when no ticker is known."},"cik":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cik","description":"The lead filer's SEC CIK, zero-padded to 10 digits (pad before comparing — an unpadded lookup silently matches nothing).","examples":["0001232524"]},"indication":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Indication","description":"What the decision is about, as disclosed (context label). Maintained on material changes that move the date; a rephrasing never rewrites it, so the stored text can be RICHER than the newest filing's."},"application_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Application Number","description":"The FDA application number (NDA/BLA…), the cross-reference key into openFDA and /v1/accuracy receipts — when the sponsor disclosed it, which is rare before the decision (currently no upcoming row carries one). Null means undisclosed, not unknown-to-FDA."},"pdufa_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Pdufa Date","description":"FDA target action date, ISO `YYYY-MM-DD`. Never null on the paid upcoming feed — the serve predicate requires a date (`pdufa_date IS NOT NULL`), so every row you pay for carries one; the schema marks it nullable only because the model is shared with surfaces that may lack a date.","examples":["2026-07-29"]},"stage":{"type":"string","enum":["set","extended","resolved","crl"],"title":"Stage","description":"Lifecycle of this catalyst. `set` = a date is assigned; `extended` = the FDA moved it (see prior_date on the event stream); `resolved` = the decision happened; `crl` = rejected via Complete Response Letter, which is NOT terminal — a resubmission can set a new date.","examples":["set"]},"confidence":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["high","medium","low"],"title":"Confidence","description":"The extraction's own confidence. Only `high` auto-publishes."},"date_confidence":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["confirmed","reported","estimated"],"title":"Date Confidence","description":"How much to trust THIS DATE, as distinct from `confidence` (which grades the extraction). `confirmed` = human-reviewed, or a grounded high-confidence extraction; `reported` = the date is quoted verbatim in the filing but our automated confidence is medium; `estimated` = low confidence, regex fallback, or no verbatim grounding quote — treat as soft."},"authority":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["human_lock","llm_high","regex","llm_medium","llm_low"],"title":"Authority","description":"Provenance rank of the current state, highest first: `human_lock` (a human applied it; never auto-overwritten), `llm_high` (grounded high-confidence extraction — the auto-publish bar), `regex` (no-LLM fallback), then `llm_medium` / `llm_low`, which never reach the published feed."},"source_quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Quote","description":"The sentence, from the filing `source_accession` names, that asserts `pdufa_date` (day-precise; the grounding gate verifies it appears in the filing). Word-for-word, with one caveat: typographic punctuation may be normalized to ASCII (curly quotes “ ” become straight quotes), so compare fold-insensitively, not byte-for-byte. Quote and URL always describe the SAME filing — after a cross-filer merge a partner company's filing may also assert this date on /v1/events, but it is never quoted here."},"source_accession":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Accession","description":"SEC EDGAR accession number of the filing this record's CURRENT served state came from — a moving pointer: each newer filing that advances the date replaces it. Join tip: it is the `accession` of one of this catalyst's date-bearing events on /v1/events — usually the latest, but after a cross-filer merge a partner company's re-parented event can be newer on the stream while this record keeps the filing that set its served date (that stream's per-event `accession` is fixed per event and spans the whole history)."},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"The EDGAR filing-INDEX page for the filing that asserted this record — `https://www.sec.gov/Archives/edgar/data/{cik}/{accession_nodash}/{accession}-index.htm` (the same shape as /v1/accuracy `filing_url`; since 0.4.2 one filing has one URL on every surface). Null when no real filing stands behind the row."},"last_updated":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date-time","title":"Last Updated","description":"When this record last CHANGED, ISO-8601 UTC with an explicit `+00:00` offset (`YYYY-MM-DDTHH:MM:SS.ffffff+00:00`). Not 'last confirmed' — an unchanged record keeps its old `last_updated` however many filings re-confirm it; see /v1/meta `freshness` for that distinction, and `age_days` for the derived day count."},"age_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Age Days","description":"Whole days since `last_updated`, computed at request time — a staleness hint, not a data field (re-fetching moves it). Null when `last_updated` is absent."}},"type":"object","required":["catalyst_id","stage"],"title":"CatalystOut","description":"A single upcoming FDA catalyst, as served to a consumer.\n\nEvery record carries its source filing (``source_url``) and a ``last_updated``\ntimestamp — a trust feature and a liability shield (the data is informational,\nnot advice; consumers can verify against the primary source)."},"CoverageOut":{"properties":{"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"The ticker as requested (upper-cased), or null when queried by CIK."},"cik":{"type":"string","title":"Cik","description":"The SEC Central Index Key, zero-padded to 10 digits."},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","description":"The filer's name from the SEC company list, or the label our catalysts carry; null if neither."},"observable":{"type":"boolean","title":"Observable","description":"True iff at least one of this filer's filings in our window disclosed a PDUFA date we extracted (actionable_filings_seen > 0). FALSE is a statement about the company's SEC filings, NOT about whether an FDA decision is pending — see `note`."},"pdufa_filings_seen":{"type":"integer","title":"Pdufa Filings Seen","description":"Filings by this CIK that matched our EDGAR discovery query in the window (any outcome, passing mentions included)."},"actionable_filings_seen":{"type":"integer","title":"Actionable Filings Seen","description":"Of those, the filings from which we extracted a PDUFA date (new date or extension)."},"first_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"First Seen","description":"Date of the oldest matching filing (ISO date), or null."},"last_seen":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Last Seen","description":"Date of the newest matching filing (ISO date), or null. A filing date, never a PDUFA date."},"catalysts_tracked":{"type":"integer","title":"Catalysts Tracked","description":"Live catalyst folders we keep for this CIK, at any stage (set / extended / crl / resolved)."},"upcoming_on_feed":{"type":"integer","title":"Upcoming On Feed","description":"How many rows the PAID /v1/catalysts/upcoming route would return for this CIK right now (its exact serve predicate, widest window). A COUNT only — the dates are the paid product. 0 with observable=true means we hold no CURRENT disclosed date, not that none exists (addressable recall is published at /v1/accuracy)."},"window":{"$ref":"#/components/schemas/CoverageWindow","description":"The observation window every count above is measured inside: `corpus_start` (our earliest ingested filing) to `as_of` (this read). Nothing before `corpus_start` was ever observable."},"note":{"type":"string","title":"Note","description":"The bound on every field above, stated in prose. Read it before acting on an absence."}},"type":"object","required":["cik","observable","pdufa_filings_seen","actionable_filings_seen","catalysts_tracked","upcoming_on_feed","window","note"],"title":"CoverageOut","description":"What we hold for one filer — facts only, never a forward date (S62, free).\n\nAnswers the ambiguous null: an empty ticker-filtered feed result cannot distinguish\n\"no decision pending\" from \"a company we cannot see\" from \"we missed it\". Every field\nhere is computed from the live tables on each request; the bound on what they mean is\nstated in ``note`` and in each field's description."},"CoverageWindow":{"properties":{"corpus_start":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Corpus Start","description":"Oldest filing date in our corpus (ISO date). Counts start here, not at the company's IPO."},"as_of":{"type":"string","format":"date","title":"As Of","description":"The date the counts were computed (ISO date, UTC)."}},"type":"object","required":["as_of"],"title":"CoverageWindow","description":"The corpus window every count in :class:`CoverageOut` is measured over."},"EventOut":{"properties":{"event_id":{"type":"integer","title":"Event Id","description":"The stream cursor: pass the highest value you have seen back as `since`. Ids ascend with time but are NOT dense — `INSERT OR IGNORE` consumes AUTOINCREMENT ids when an already-known filing is replayed, so gaps in the sequence are normal and are not missed events (17 such gaps exist in the live stream's history)."},"catalyst_id":{"type":"integer","title":"Catalyst Id","description":"The stable key: correlate all events of one record into a timeline, and join to the snapshot tier's `catalyst_id`. After a merge the loser's events are re-parented to the survivor; the loser's id gets a final `retracted` event whose `merged_into` names where to re-point."},"event_type":{"type":"string","enum":["new_date_assigned","date_extension","decision_outcome","retracted","restored","corrected"],"title":"Event Type","description":"`new_date_assigned` = a PDUFA date first recorded; `date_extension` = the FDA moved it (`prior_date` holds the old one); `decision_outcome` = the decision landed (see `outcome`); `retracted` = this catalyst_id was withdrawn or merged away — STOP relying on it, and read `source_quote` for where to re-point; `restored` = a previously retracted catalyst_id is live again (those two carry no date and no filing, so `new_date`, `accession` and `source_url` are null on them); `corrected` = a VENDOR DATA REPAIR of our served date — `prior_date` is what we served before, `new_date` is what we serve now, and `delta_days` measures our repair, NOT an FDA move: never count `corrected` as an FDA action in date-change analytics. A sweep-emitted correction has a synthetic accession and a null `source_url`; a reviewer-applied correction keeps its real filing provenance. Mirroring served state from this stream: apply an event's `new_date` when `applied` is true — NOT simply on every event, because the daily catch-up can append a recovered OLDER filing with a newer event id whose date was never served (`applied: false`). The rule behind the flag, if you derive it yourself: process date-bearing events in `event_id` order and apply when `filed_at` is null (a correction) or >= the newest `filed_at` seen so far. Forward compatibility: never count an unrecognized event type as an FDA action, but do apply its `new_date` by the same `applied` rule.","examples":["date_extension"]},"drug_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drug Name","description":"The drug as the sponsor's filing names it — see the snapshot tier."},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","description":"The record's CURRENT lead filer (not the event's filer)."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"The lead filer's primary ticker; filter on `tickers`."},"tickers":{"items":{"type":"string"},"type":"array","title":"Tickers","description":"Every co-filer exposed to this decision, lead issuer first — see the snapshot tier's `tickers`."},"indication":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Indication","description":"What the decision is about, as disclosed (context label)."},"application_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Application Number","description":"The FDA application number — the openFDA / accuracy-receipt join key; null when the sponsor has not disclosed one."},"prior_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Prior Date","description":"The date moved AWAY from (`YYYY-MM-DD`): the old goal on `date_extension`, the previously served date on `corrected`. Null on first assignments — and on RESTATEMENTS: a filing re-asserting an unchanged date carries no prior (a delta of 0 would fabricate a move; filter restatements with `date_changed`), and a recovered stale filing's prior is never derived (its date was not served). So null here means \"nothing moved\" or \"the old date was not stated\", never a data gap to chase."},"new_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"New Date","description":"The PDUFA date this event asserts (`YYYY-MM-DD`) — forward dates included; this is the paid tier. Null on `retracted`/`restored` and on decision events."},"new_date_upcoming":{"type":"boolean","title":"New Date Upcoming","description":"Is `new_date` still in the future? Evaluated AT REQUEST TIME, not frozen when the event was written — so re-fetching an old event can flip this to false once the date passes. Derive from `new_date` yourself if you need the value as of some other moment.","default":false},"days_to_date":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days To Date","description":"Whole days from TODAY until `new_date`; null once the date is past or absent. Also evaluated at request time."},"delta_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Delta Days","description":"How far the date MOVED on this event: `new_date` − `prior_date`, in days (positive = pushed later). Null unless both dates are present — see `prior_date` for why a restatement or stale recovery has none. NOT a prediction error — that lives in /v1/accuracy."},"merged_into":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Merged Into","description":"Only on `retracted` events of a MERGED record: the surviving `catalyst_id` to re-point to (as of this read — an unmerge clears it, announced by `restored`). Null on a takedown retraction (withdrawn, no successor) and on every other event type."},"outcome":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["approved","tentative","crl","none_found"],"title":"Outcome","description":"Only on `decision_outcome`. `approved` = full FDA approval. `tentative` = FDA TENTATIVE approval: the application met the approval standard but marketing is blocked by unexpired patent/exclusivity on the reference product; it converts to full approval when the block lapses, without a new review cycle. `crl` = Complete Response Letter (rejected this cycle, not necessarily for ever). `none_found` = matured with no FDA decision we could verify. Null on every other event type. In practice this stream serves only `approved` and `crl`: a `decision_outcome` event announces what the SPONSOR'S OWN FILING says, and the extraction vocabulary is approved/CRL — `tentative` and `none_found` are enumerated for vocabulary lockstep with /v1/accuracy (where reconciliation against FDA sources produces them) and have never appeared on this stream; treat them as reserved, not as values to branch on."},"confidence":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["high","medium","low"],"title":"Confidence","description":"Raw extraction confidence. Only `high` auto-publishes."},"date_confidence":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["confirmed","reported","estimated"],"title":"Date Confidence","description":"Per-date trust label, same vocabulary as the catalyst snapshot: `confirmed` (human-reviewed or grounded high-confidence) / `reported` (quoted in the filing, medium confidence) / `estimated` (soft — low confidence, regex fallback, or no verbatim grounding quote)."},"authority":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["human_lock","llm_high","regex","llm_medium","llm_low"],"title":"Authority","description":"Provenance rank of the asserting extraction — see CatalystOut.authority."},"source_quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Quote","description":"The filing sentence behind this change, word-for-word except that typographic punctuation may be normalized to ASCII (compare fold-insensitively, not byte-for-byte). On `corrected` it is the repair reason (not filing text); on `retracted` it is the re-point prose — prefer the structured `merged_into`."},"detected_at":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date-time","title":"Detected At","description":"When our poller recorded this event — ISO-8601 UTC with an explicit `+00:00` offset (e.g. `2026-08-13T21:01:51.154744+00:00`). Every timestamp this API serves is UTC; date-only fields (`new_date`, `prior_date`) are calendar dates with no time component.","examples":["2026-08-13T21:01:51.154744+00:00"]},"filed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Filed At","description":"SEC publication date of the filing behind this event (`YYYY-MM-DD`) — the freshness signal (`detected_at` is when OUR poller saw it, which can lag by months when the daily catch-up recovers a filing a past poll lost). Null on lifecycle rows and sweep-emitted corrections, which have no filing.","examples":["2026-05-07"]},"applied":{"type":"boolean","title":"Applied","description":"Whether this event advances a mirror of the served date, under the stream's replay rule (see `event_type`): true when the event's filing is at least as recent as every date-bearing filing before it (corrections always apply), false on stale recoveries — an OLDER filing appended later by the daily catch-up (its `new_date` was never served; a recency-based mirror would regress) — and on date-less events. A restatement of an unchanged date is applied=true: it re-asserts the served date.","default":false},"date_changed":{"type":"boolean","title":"Date Changed","description":"`applied` AND the served date actually moved (a first assignment counts as a move). THE filter for date-change analytics: it excludes restatements (~44% of `new_date_assigned` events re-assert an unchanged date) and stale recoveries. Combine with `event_type != 'corrected'` to count only FDA moves, not our repairs.","default":false},"accession":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accession","description":"SEC EDGAR accession number of the filing behind THIS event — fixed once written (it is part of the event's identity). Synthetic accessions (`correction:…`, `lifecycle:…`) mark rows with no filing behind them; `source_url` is null on those. Join tip: the snapshot tier's `source_accession` is the `accession` of the event whose filing set the served date — usually the latest date-bearing event, but not necessarily after a cross-filer merge (a partner's re-parented event can be newer) — same identifier vocabulary, different meaning (current-state provenance there, per-event provenance here)."},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"The EDGAR filing-INDEX page for the filing that asserted this record — `https://www.sec.gov/Archives/edgar/data/{cik}/{accession_nodash}/{accession}-index.htm` (the same shape as /v1/accuracy `filing_url`; since 0.4.2 one filing has one URL on every surface). Null when no real filing stands behind the row."}},"type":"object","required":["event_id","catalyst_id","event_type"],"title":"EventOut","description":"One catalyst change event (set → extended → resolved) on the full-history change stream.\n\nThe premium ($0.05) paid tier: it proves the feed tracks *changes* (extensions,\nresolutions) and — unlike the cheaper snapshot — exposes **everything**, including\nstill-upcoming forward dates and their source filing. ``new_date_upcoming`` remains as\nan informational flag (has the decision happened yet?). ``event_id`` is the cursor to\npass back as ``since``."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Meta":{"properties":{"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total","description":"Rows in THIS response's `data` payload (post-limit), not the table's total row count. On the unpaginated endpoints (`/v1/accuracy`, `/v1/meta`) it counts the payload's main table where one exists (for `/v1/accuracy`, the `track_record` receipt rows) and is null where none does."},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","description":"The limit the response was produced under (the request's `limit`, or the route's default). Null on unpaginated endpoints. There is no `offset` — the paid feeds page by `limit` (upcoming) or a `since` cursor (events), never by offset."},"next_since":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Next Since","description":"`/v1/events` only: pass this as `since` on the next call to continue the drain without gaps or repeats. It is the highest `event_id` in this page, or your own `since` echoed back when the page is empty (never regresses your position). Null on non-cursor endpoints."},"has_more":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has More","description":"`/v1/events` with a `since` cursor only: true ⇒ more events are already waiting — call again with `since=next_since`; false ⇒ you are caught up. Null when not cursoring (a newest-first browse has no 'more') and on every other endpoint."}},"type":"object","title":"Meta","description":"Result-set metadata attached to every response.\n\nNo ``offset``: the paid feeds page by ``limit`` (upcoming) or a ``since`` cursor\n(events), never by offset. A dangling ``offset`` field advertised a pagination\ncontract the API doesn't honour, so it's omitted.\n\n``limit`` is null on unpaginated endpoints (``/v1/accuracy``, ``/v1/meta``); there\n``total`` still counts the rows of the payload's main table where one exists (for\n``/v1/accuracy``, the ``track_record`` receipt rows)."},"PaidEndpointOut":{"properties":{"route":{"type":"string","title":"Route","description":"HTTP method and path, e.g. `GET /v1/catalysts/upcoming`."},"price":{"type":"string","title":"Price","description":"Human-readable price per request, e.g. `$0.02`."},"returns":{"type":"string","title":"Returns","description":"One line on what the route returns."},"sample":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sample","description":"Path to a free fictional sample of this route's shape. OMITTED where the route has no sample."}},"type":"object","required":["route","price","returns"],"title":"PaidEndpointOut","description":"A pointer from the free document to a paid route."},"PaymentOption":{"properties":{"scheme":{"type":"string","title":"Scheme","description":"Payment scheme. `exact` — pay the stated amount."},"network":{"type":"string","title":"Network","description":"CAIP-2 chain id. `eip155:8453` = Base mainnet, `solana:…` = Solana mainnet (`eip155:84532` = Base Sepolia on the testnet deployment).","examples":["eip155:8453"]},"asset":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Asset","description":"Token contract address to pay in (USDC on both chains)."},"amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amount","description":"Price in ATOMIC units of `asset`, as a string. USDC has 6 decimals, so `20000` = $0.02 and `50000` = $0.05 — do not read it as dollars.","examples":["20000"]},"payTo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payto","description":"The payee address for this chain."},"maxTimeoutSeconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxtimeoutseconds","description":"How long the server will wait for settlement before abandoning it."},"extra":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extra","description":"Scheme-specific fields (e.g. EIP-712 domain data)."}},"type":"object","required":["scheme","network"],"title":"PaymentOption","description":"One way to pay for a paid route — an entry in the 402 challenge's ``accepts``.\n\n``accepts[0]`` is always Base (EVM) and ``accepts[1]`` Solana when both are offered;\nseveral clients index the first entry, so the order is part of the contract."},"PaymentRequiredOut":{"properties":{"x402Version":{"type":"integer","title":"X402Version","description":"Protocol version of this challenge.","examples":[2]},"accepts":{"items":{"$ref":"#/components/schemas/PaymentOption"},"type":"array","title":"Accepts","description":"The payment options offered, Base first then Solana. Pick one, sign it, and retry the SAME request with the payment header."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"Why the previous attempt failed, when retrying."},"resource":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Resource","description":"What is being sold — url, description, mimeType, serviceName, tags."},"extensions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extensions","description":"Discovery metadata, including the `bazaar` block: this route's input schema, its output row shape, and a worked example — enough to write a decoder before paying. The same shape is served free at the route's `/sample`."}},"type":"object","required":["x402Version","accepts"],"title":"PaymentRequiredOut","description":"The x402 challenge served with **402** on a paid route (S64).\n\nThe same document is also base64'd into the `PAYMENT-REQUIRED` response header; the\nbody is a convenience mirror, because the SDK may leave it empty and some clients\nread one, some the other. Responses carry `Cache-Control: no-store` — a challenge is\nsingle-use and must never be cached.\n\n⚑ **Two OTHER shapes also use status 402** and are NOT this document: a bare\n`{\"error\": \"Payment required\"}` when the challenge could not be built, and\n`{\"error\": \"Settlement failed\", \"details\": \"…\"}` when a verified payment did not\nsettle on-chain. Branch on the presence of `accepts`, not on the status code alone.\nIn both error cases nothing settled and you were not charged."},"RecallMissRowOut":{"properties":{"application_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Application Number","description":"FDA application number, so the miss can be verified at api.fda.gov."},"action_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Action Date","description":"ISO date the FDA acted."},"outcome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Outcome","description":"What the FDA did."},"sponsor_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sponsor Name","description":"Sponsor as the FDA records it."},"sponsor_ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sponsor Ticker","description":"Ticker we resolved the sponsor to. Null where none."},"sponsor_match":{"anyOf":[{"type":"string"},{"type":"null"}],"enum":["cik","name_exact"],"title":"Sponsor Match","description":"How the sponsor was resolved to a filer. Closed vocabulary: these are exactly the methods the addressable denominator accepts."}},"type":"object","title":"RecallMissRowOut","description":"One FDA decision we did NOT catch."},"RecallMissSideOut":{"properties":{"addressable":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Addressable","description":"Decisions of this class we could have caught."},"caught":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Caught","description":"How many we did catch."},"missed":{"type":"integer","title":"Missed","description":"addressable - caught, and the length of `items`."},"items":{"items":{"$ref":"#/components/schemas/RecallMissRowOut"},"type":"array","title":"Items","description":"Every missed decision, enumerated. Empty list when none were missed."}},"type":"object","required":["missed"],"title":"RecallMissSideOut","description":"One side (approvals or rejections) of the misses ledger, with both ratio operands."},"RecallMissSideSummaryOut":{"properties":{"addressable":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Addressable","description":"Decisions of this class we could have caught."},"caught":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Caught","description":"How many we did catch."},"missed":{"type":"integer","title":"Missed","description":"addressable - caught, and the row count served in full mode."},"items_rows":{"type":"integer","title":"Items Rows","description":"Row count of the `items` ledger served in full mode."}},"type":"object","required":["missed","items_rows"],"title":"RecallMissSideSummaryOut","description":"``?summary=1`` form of :class:`RecallMissSideOut`."},"RecallMissesOut":{"properties":{"note":{"type":"string","title":"Note","description":"What a row here does and does not mean."},"approvals":{"$ref":"#/components/schemas/RecallMissSideOut","description":"Approvals we did not catch."},"rejections":{"$ref":"#/components/schemas/RecallMissSideOut","description":"Rejections we did not catch."}},"type":"object","required":["note","approvals","rejections"],"title":"RecallMissesOut","description":"The decisions we missed, published beside the ones we caught.\n\n⚑ OMITTED from the payload entirely — not served empty — when the committed baseline\npredates these ledgers."},"RecallMissesSummaryOut":{"properties":{"note":{"type":"string","title":"Note","description":"What a row here does and does not mean."},"approvals":{"$ref":"#/components/schemas/RecallMissSideSummaryOut","description":"Approvals we did not catch."},"rejections":{"$ref":"#/components/schemas/RecallMissSideSummaryOut","description":"Rejections we did not catch."}},"type":"object","required":["note","approvals","rejections"],"title":"RecallMissesSummaryOut","description":"``?summary=1`` form of :class:`RecallMissesOut`."},"UnresolvedPredictionRowOut":{"properties":{"drug_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drug Name","description":"Drug name as we published it."},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company","description":"Company as we published it."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"Exchange ticker where resolved. Null otherwise."},"cik":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cik","description":"SEC Central Index Key, zero-padded to 10 digits."},"pdufa_date":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"date","title":"Pdufa Date","description":"The date we published (ISO). Already matured."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Why it never scored. ⚑ NULL here is counted under the key `unrecorded` in reason_counts — join the two on that substitution or the totals will not reconcile. Vocabulary described in reason_notes; codes are added, never reworded."}},"type":"object","title":"UnresolvedPredictionRowOut","description":"One date we published that never scored against a verifiable FDA decision."},"UnresolvedPredictionsOut":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of unresolved predictions, and the length of `items`."},"withheld_not_yet_matured":{"type":"integer","title":"Withheld Not Yet Matured","description":"Unresolved predictions EXCLUDED from this ledger because their date has not yet passed. Withheld deliberately: that is forward-looking data, and it is what the paid feed sells."},"note":{"type":"string","title":"Note","description":"What a row here does and does not mean about the date's correctness."},"reason_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reason Counts","description":"Histogram of `reason` over `items`, keyed by reason code. Rows whose reason is null are counted under `unrecorded`. Only observed codes appear, so this key set shrinks."},"reason_notes":{"additionalProperties":{"type":"string"},"type":"object","title":"Reason Notes","description":"Prose for each reason code present in reason_counts. FILTERED to the observed codes, so the key set shrinks with the data rather than listing the whole vocabulary."},"items":{"items":{"$ref":"#/components/schemas/UnresolvedPredictionRowOut"},"type":"array","title":"Items","description":"Every unresolved prediction, enumerated."}},"type":"object","required":["count","withheld_not_yet_matured","note"],"title":"UnresolvedPredictionsOut","description":"Our own published dates that never resolved — the other direction of honesty.\n\n⚑ OMITTED from the payload entirely — not served empty — when the committed baseline\npredates this ledger or carries no reason codes."},"UnresolvedPredictionsSummaryOut":{"properties":{"count":{"type":"integer","title":"Count","description":"Number of unresolved predictions."},"withheld_not_yet_matured":{"type":"integer","title":"Withheld Not Yet Matured","description":"Unresolved predictions whose date has not yet passed."},"note":{"type":"string","title":"Note","description":"What a row here does and does not mean about the date's correctness."},"reason_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reason Counts","description":"Histogram of `reason`, keyed by reason code. Only observed codes appear."},"reason_notes":{"additionalProperties":{"type":"string"},"type":"object","title":"Reason Notes","description":"Prose for each reason code present in reason_counts."},"items_rows":{"type":"integer","title":"Items Rows","description":"Row count of the `items` ledger served in full mode."}},"type":"object","required":["count","withheld_not_yet_matured","note","items_rows"],"title":"UnresolvedPredictionsSummaryOut","description":"``?summary=1`` form of :class:`UnresolvedPredictionsOut`."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}