Veridion Disclosure API · changelog
Every version, dated.
The current version is 1.20.0, served on every response as X-API-Version. Minor and patch versions are additive; a breaking change would be a new major path, announced here first. This list is the one the OpenAPI document carries in its description; the stability policy says what each kind of change may and may not do.
Subscribe: https://www.veridionmarkets.com/data-api/changelog.xml
October 6, 2026: repeated disclosure withdrawals
From September 30 through the October 6 investigation, incremental and full projection syncs disagreed about ambiguous filing identities. The October 6, 12:04–12:15 UTC measurement counted 649 withdrawals of 199 disclosure IDs across 114 House filings since October 1. These pipeline withdrawals were not corrections to the government filings. Past cycles remain visible in each disclosure's history. Incremental syncs now evaluate complete affected filings; ambiguous identities remain withheld. Closure requires three consecutive scheduled full-sync checks on production; the fix date is not yet verified.
- 1.20.02026-10-16
- GET /api/v1/disclosures?document_id selects the served lines of one House numeric document ID or Senate eFD UUID, combined with existing filters. Senate UUIDs are case-insensitive. Selection applies before pagination to current, as_of and updated_since reads; cursors retain the selected document. Unknown documents return an empty page. Held or missing lines are not included.
- 1.19.22026-10-14
- House parser recovery remains recovery_in_progress while documents are pending, with its measured counts and terminal exclusions preserved. Background progress does not degrade served status when fresh successful recovery evidence has complete consistent counts and zero row loss and independent serving checks are current. Stale or unavailable recovery, failed refreshes, unknown counts, row loss and missing or failing serving evidence still degrade status. Parser revision rollout does not assert historical completion or restore past operational hours; no billing predicate or freshness threshold changes.
- coverage_recovery.requalification_hold identifies an explicit parser rollout hold, its reviewed prerequisite and release condition. The hold is separate from measured recovery counts and serving health; it does not mark pending documents complete or remove terminal failures.
- 1.19.12026-10-13
- House PTRs with exactly the official Spouse/DC Over $1,000,000 or Over $50,000,000 brackets serve an open_ended_as_disclosed amount with the first whole dollar above the printed threshold as range_low, null range_high, and no midpoint. Other missing upper bounds remain excluded. Owner still comes from the filing's Owner column.
- 1.19.02026-10-13
- Adds GET /api/v1/executive and /api/v1/executive/{transaction_id}: receipt-qualified OGE 278-T transactions from the accepted first-party temporal serving set, with filer, trade_date, filed_date (OGE received), asset, type, amount range as filed, document URL/page/line, immutable version_id and computed_at. Held rows are counted with reasons; no outcomes, returns or amount midpoints are exposed. The explicit veridion-executive-content-v1 content_hash_basis hashes projected filing facts and receipt coordinates, excluding retrieval, parsing and observation clocks. Developer snapshot pagination preserves generation and microsecond keyset positions. Anonymous, free-key and evaluator access is bounded to the same latest 25 serving candidates before filters or ID lookup, with no historical cursor/as_of access and the existing shared 60-request-per-IP hourly disclosure budget. Developer prices remain $49/month or $490/year.
- Adds invalid_transaction_id, full_history_requires_developer, executive_transaction_not_found, executive_identity_ambiguous, cursor_signing_unavailable and cursor_position_unavailable to the problem catalog. /api/docs resolves to the generated reference. /api/v1/coverage caches its public document-funnel and paper-queue measurements for 60 seconds within the deployment and Supabase project; source timestamps remain unchanged and cached age advances against the existing freshness guard. Authentication, quota and origin observation remain per-request and responses remain private no-store. No Production read-time or completeness improvement is asserted by this release note.
- 1.18.12026-10-12
- The public status coverage_recovery state now reports ready when a current measurement has no pending, in-progress or retryable-failed House PTR recovery documents, even if terminal exclusions remain. Previously that finished cohort reported complete_with_terminal_gaps. The terminal count and row-loss count remain visible, and document failure reasons remain in their source records. A stale or unavailable measurement, or an unsuccessful refresh, still degrades status. No billing-gate predicate or age threshold changes.
- 1.18.02026-10-11
- Adds required paper_transcriptions to coverage, status and both member-directory envelopes, plus a paper block on each existing member record. It reports retained Clerk-index paper inspections, pending and terminal queue records, reconciliation gaps and unknown transaction-row counts. Pending-only filers appear in the separate by_member list, without changing roster counts or pagination. Member filters disclose identity-matching limits. Failed reads return named unavailable states and nullable counts; a known pending population alone does not degrade service status. This release makes no production completeness or transcription timing claim.
- 1.17.02026-10-11
- Adds instrument to each disclosure: printed classification and ticker evidence, plus option underlying, call/put, exact decimal strike, as-filed expiration and contract count. Missing or conflicting terms are null with field-level reasons and printed excerpts. No lookup or share-to-contract conversion is performed. Original asset fields, identity and content-hash basis are unchanged; option-related exercise text is not an executable trading instruction.
- 1.16.02026-10-10
- Adds /v1/status.owner_column_repair with the measured terminal-document count, official receipts, filing dates and retained reasons. Unresolved or unreadable repair evidence remains degraded. A later idle owner-repair cron attempt no longer repeats a historical failure as a new 503; newly failed documents and failed evidence writes still fail the attempt. No corpus rows or thresholds change.
- 1.15.02026-10-08
- Fresh disclosure snapshot walks now sort by filed_date descending, then first_observed_at and disclosure_id ascending. Existing signed cursors retain their original observation-time ordering; change-event ordering and snapshot membership are unchanged. The optional page.order field names the order used. Starting an old as_of query without its saved cursor uses the new order; clients must not compare page positions across this ordering change.
- coverage_recovery reasons identify the House Clerk recovery-document population, its stored measurement instant and counts. Missing measurements remain unknown; no health predicate, ceiling or billing-gate definition is relaxed. A recovery worklist is not a measured count of missing served rows.
- 1.14.02026-09-29
- Adds /v1/status.billing_gate: a stored hourly-observation streak for the 14-day paid-activation rule. A completed UTC day qualifies only when all 24 distinct hourly observations are present and operational. Operational means the public status evaluator returned ready with no degraded reasons. Missing hours break the streak; today's incomplete hour is not missing until it ends. A recorded non-operational sample today resets the streak immediately. Missing completed hours are recorded as observation_missing failures, with no invented observation timestamp. No operational history is backfilled. Hourly samples do not establish continuous availability or a service-level guarantee. The existing ready/degraded/unavailable status enum and all thresholds are unchanged. No checkout activation or historical backfill is included.
- 1.13.02026-09-24
- The House PTR coverage block adds
series: completed reconciliation runs with published, held and served document counts, three explicitly denominated rates, and absent points where retained history cannot supply a complete measurement. Completion captures and retained-generation replays are labeled separately; computation instants are never backdated. The change follows the 2026-09-12 finding that successive reconciliations had no published history. It reports no new production measurement.
- The House PTR coverage block adds
- 1.12.02026-09-23
/v1/status.universe.*_at,/v1/status.track_record.*_atand/v1/members.directory.refreshed_atnow conform to the ISO-8601Zform their schema declares; previously emitted PostgreSQL offset text./v1/status.universeaddscoverage_share,scored_qualified_count,qualified_count,census_trading_dayandcensus_measured_at. Coverage is the intersection of current-methodology scored symbols and the stored qualified symbol set, divided by that set's count, not all scored symbols divided by the census count. The numerator, denominator and census identity are captured in the same stored measurement as freshness. Historical count-only census rows and unavailable or invalid evidence produce null coverage, never an estimated set; a stale measurement remains explicitly stale. No historical census is backfilled or normalized.
- 1.11.12026-09-20
- The unpaginated /v1/members response now declares the cache contract it is actually delivered under. Measured anonymously on production 2026-09-19 and again 2026-09-20 with credentials omitted: the response ships
Cache-Control: private, no-store, max-age=0andX-Veridion-Cache-Policy: private-no-store, because every v1 origin response is rewritten to private no-store so that a shared-cache hit cannot bypass the origin observer, while the body'scacheobject and the published MemberDirectoryPage schema still saidpolicy: publicwith a 300-second shared-cache bound and an Age header. The body, the schema, the OpenAPI response headers and the embedded example now all state private-no-store, and the Age header is no longer documented on this operation because a response that never enters a shared cache never carries one. This is a description of existing delivery, not a change to it: no header a caller receives changes, no field is removed, renamed or retyped, conditional requests and entity tags are unchanged, and the paginated response was already declared private-no-store in 1.10.0. The public-record contract remains defined for a future unmetered route; no v1 data route currently delivers it.
- The unpaginated /v1/members response now declares the cache contract it is actually delivered under. Measured anonymously on production 2026-09-19 and again 2026-09-20 with credentials omitted: the response ships
- 1.11.02026-09-19
- Paginated /v1/members responses gain a
directoryobject naming which relation answered, its refresh instant, the age bound it was checked against, and the reason the live view answered when it did. It is the same object the unpaginated directory response has published since 1.10.0, and it is added because the paginated read no longer always measures live. Measured anonymously on production 2026-09-19: /v1/members returned 332 rows in 122 KB in 212 to 301 ms while /v1/members?limit=1 returned one row in 1.7 KB in 4,509 to 5,637 ms, because the paginated read went to the directory view rather than the four-hourly snapshot of it that exists for this purpose. EXPLAIN (ANALYZE, BUFFERS) on the same query shape with LIMIT 2 measured 4,577.533 ms and 173,646 shared buffers against the view and 0.248 ms and 8 shared buffers against the snapshot; LIMIT cannot reduce the view's cost because the plan materializes the whole aggregated corpus before the top-N sort. The paginated read now prefers the snapshot while it is under six hours old and falls back to the view otherwise, matching the unpaginated branch. Rows are unchanged: all 332 rows of the snapshot and the view were compared field by field on 2026-09-19 with zero key and zero value differences, and the snapshot's own refreshed_at column is removed from member rows so the two sources are indistinguishable by shape.coverage.basisis reworded because its previous text promised that coverage was measured at this page's served_at, which is true only when the live view answered; it now states which instant applies under each source. No field is removed, renamed or retyped, no ordering changes, and cursors issued before this version remain valid.
- Paginated /v1/members responses gain a
- 1.10.02026-09-16
- /v1/members accepts limit and cursor as an additive opt-in to the shared data/coverage/page/access/cache/version/notice envelope. With neither parameter, the legacy response and ordering remain unchanged. Paginated requests default to 100 rows, allow up to 500 in every key class, and order by member_id ascending. The signed cursor uses the disclosures codec but identifies a live members position, not a pinned generation. Each page reads coverage and identity in one database statement and publishes its served_at; pages fetched at different times do not sum to one measurement instant. No storage, new throttling, resource deprecation or legacy-response sunset is introduced. A future default change requires a separately announced compatibility window.
- 1.9.12026-09-16
- Measured quota responses no longer send the resource-level Deprecation header for a header-only transition. No v1 operation is deprecated. The existing X-RateLimit-Limit deprecation is described in OpenAPI x-deprecated-headers and response notices; use RateLimit-Limit. All X-RateLimit compatibility headers remain available, preserving the 1.9.0 deferral with no scheduled removal date. Quota values, reset units, enforcement, conditional requests and admission rules are unchanged.
- 1.9.02026-09-16
- Status preserves its existing sections and reads each independent dependency with a 500 ms deadline. A missed deadline is named timed_out with deadline_ms; no source freshness threshold or quota policy changes. Existing X-RateLimit compatibility headers remain; the next-minor removal announced in 1.8.0 is deferred.
- Observation delay reads a saved poll-time report with no request-time aggregate fallback. basis is precomputed; the prior explanatory text moves to measurement_basis. computed_at is the saved computation start, not request time; missing or invalid computation has null computed_at. Stale reports retain their original timestamp but withhold distributions after one daily cycle (86400 seconds). This corrects the unavailable-state timestamp contract; clients must accept null. No measured timestamp is fabricated.
- The observation_delay block adds age_seconds, expected_max_age_seconds, methodology_version, served population and named exclusions, and measured-source continuity. The served-telemetry-v3 producer must attest eligibility over active retained versions and successful recorded polls on every day from filing through observation. Filing-to-observation hours begin at midnight UTC on the filing date, not an unrecorded filing time. P95 remains withheld below 100 eligible rows. Missing producer data stays unavailable; this release makes no production performance claim.
- 1.8.02026-09-15
- Measured quota responses additionally carry RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy. Limit and Remaining match the legacy X-prefixed fields; RateLimit-Reset is seconds remaining while X-RateLimit-Reset remains absolute Unix seconds. Policy identifies the shared anonymous route-family budget, shared key-minute budget or separate instance-local edge denial. Unobserved balances remain absent, including counter outages; these aliases do not introduce a new allowance.
- X-RateLimit-Limit is deprecated in favor of RateLimit-Limit. The compatibility alias remains throughout this minor release and is scheduled for removal in the next minor release. Measured quota responses carry Deprecation with the announcement day, 2026-09-14, as a Structured Field Date. This announces the header transition, not withdrawal of any operation or a new enforcement policy. The remaining X-prefixed quota headers keep their existing meanings; no calendar sunset date has been selected.
- After quota admission, anonymous disclosure list, detail and history responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset from one atomic PostgreSQL counter observation. X-RateLimit-Policy names each independent route-family budget. Each permits 60 requests per IP per fixed UTC hour across instances, replacing instance-local windows. Successes, conditional reads and refusals after quota admission consume a request; API-key authentication is independent. The existing pre-route edge ceiling remains instance-local; its denials now carry their own reset and an explicitly edge-instance policy scope, never the shared counter's balance.
- These responses are private, no-store so a shared cache cannot replay another caller's balance. Conditional GET and entity tags remain supported. Missing trusted client addressing, an unavailable enforced counter or an invalid enforced counter result returns api_rate_limit_unavailable without invented quota headers. The change follows anonymous-only conformance probes on 2026-09-13; keyed responses already carried Remaining and Reset. Both header families remain available; this release does not remove the legacy names.
- Origin responses additionally enter the separate v1-origin-observation counter, including directory, status, contract, authentication and method refusals. This hourly per-IP observation is unenforced, never spends an existing enforced budget, and cannot reject a response. No ceiling or remaining allowance is selected for it; enforcement is unscheduled pending at least seven complete observation days and review. Its count is not published as Remaining. The index and contract document use no-store so cached delivery cannot bypass origin counting. Pre-origin refusals and unavailable observations are not represented as counted requests.
- The 2026-09-15 ruling distinguishes enforced quota triplets from observation-only reporting. With no enforced balance, RateLimit-Policy names v1-origin-observation with observe-only and the measured UTC window-start; RateLimit-Reset reports that observation window's measured delay. Limit and Remaining are absent in both dialects. If observation fails, Policy instead carries unavailable and Reset is absent; the original status and body survive. window-start is not a deployment date or proof of continuous telemetry. Origin responses, including authentication, routing and method refusals, carry X-API-Version.
- 1.7.02026-09-15
/api/v1, the service index, enters the contract asServiceIndex. It has been served since the index route landed and was carried as a written exemption (open debt, item 57) because describing it needed a response schema. The response is derived from the OpenAPI document, the problem catalog and the contract limits at build time: every operation with its method, path, href, summary, tag and authentication requirement; the documentation, reference, problem-catalog, changelog and overview URLs; the bearer scheme with the free-tier and licensed limits; the pagination, point-in-time, incremental, error, unknown-parameter and method conventions; and the operation and problem-code counts. No served value changed; the index now describes itself, and the deploy gate no longer reports an undocumented path.
- 1.6.02026-09-13
/api/v1/coverageaddscongress.first_party_verified_rows_including_superseded.first_party_verified_rowsremains a deprecated alias with its value unchanged; neither field is a live-row or served-row count. The correction follows the 2026-09-13 report that a retained warehouse count was read as current coverage. No new production count or serving rate is asserted.population_basisstates the lifecycle and measurement scope of Congress, provenance, source-line coverage and reconciliation counts. Every numeric field in PublicCoverage has a derivation and unit. Warehouse counts include superseded rows; active-version counts exclude closed versions. Independently read blocks are not an exclusion census. The House PTR document funnel remains tied to its own completed index run.
- 1.5.02026-09-13
/v1/statusderives OGE producer health from terminal attendance, not the age of the stored filing. Two consecutive failed completions report degraded;failure_sincenames the first failed run in that completion-ordered streak, including streaks older than the attendance row window.last_success_atis the last successful producer completion, including zero-row polls. Missing attendance reports unknown, never a substitute filing timestamp.- OGE
expected_max_age_secondsis derived from two maximum gaps in its configured daily UTC schedule, plus the late-start allowance and run ceiling.threshold_basisnames the producer, failure threshold and age calculation. The configured schedule is daily, not an inferred cadence from extra invocations; this is an operational alerting policy, not a service commitment. pipeline.stateadds partial when scheduled invocations or routes remain unwrapped, with the exact instrumented/total pairs inpipeline.detailand a named coverage reason. Measured delivery failures remain degraded and unreadable attendance remains unavailable. Partial observation no longer reports ready.
- 1.4.02026-09-12
/api/v1/coverageaddsdocuments_served,held_rate,served_rateandrate_basisto the House PTR completeness block, with aggregate and per-year counts from the same shared measurement as /data-api. The legacyrateremains held documents divided by published documents; it does not describe the served corpus. Every coverage rate names its numerator and denominator fields in its own object, including source-line coverage rates.- The House PTR block carries
run_idandserved_measured_at; the reconciliation completion instant remains separate. One complete, fresh reconciliation and its indexed DocIDs constrain every stage. Missing, stale or inconsistent evidence withholds all document rates rather than publishing a partial funnel. The change follows the 2026-09-12 report that the two surfaces presented held and served coverage as different answers; no new production rate is claimed by this release note.
- 1.3.232026-09-08
/v1/disclosuresaddspage.next_updated_since: the watermark to pass asupdated_sinceon the next incremental call, after walking the current result set to its last page. It equals the response's pinnedsnapshot_as_of, so a caller that stores it receives everything changed after this response and nothing twice, and it is always a value this API accepts. Until this version the API published no watermark at all, and a client following the obvious rule -- take the greatestchange.observed_atand pass it back -- received a typed 400. Measured on production 2026-09-08: 67,053 of 67,928 served rows, 98.7 percent, carry achange.observed_atthat predates the retained-history floor, because those rows were observed before temporal retention began. The instant is true; it is simply not a point at which this API can answer what changed since. The full-pull-then-resume loop, which is the integration every consumer builds first, therefore failed on its first resume for almost every caller.- The retained-history floor is unchanged and no request refused before is accepted now. Refusing a point in time the warehouse cannot reconstruct is the discipline
as_ofalready applies, and it stays. What changed is that the response names a point the caller can use, rather than leaving the caller to infer one and get it wrong. - A caller supplying an
updated_sincebelow the floor still receives the existing typed 400 naming the floor. No path, problem code, field removal, parameter or status changes at this version;page.next_updated_sinceis additive.
- 1.3.222026-09-08
- The weak entity tag on every record route and the member directory now incorporates the served API version. Until this version the tag was computed over the data-bearing body alone, so a minor release with unchanged rows produced an identical tag: a client revalidating a cached 1.3.21 response against 1.3.22 received 304 Not Modified and continued to hold the earlier version indefinitely, never observing an additive field until the underlying rows happened to change. Minor versions are additive by contract; a client that cannot observe a release cannot benefit from one. From 1.3.22 a 304 is returned only to a client whose cached response was produced under the version currently served. Every existing tag is therefore invalidated once at this release, and each cached client receives one full 200 on its next revalidation.
- Measured on production 2026-09-08 while reading the continuous canary, which had reported
directory: 304 keeps the version headeras broken on 62 of 331 runs. The origin sets X-API-Version on its 304 responses; a 304 answered from the CDN for a matching If-None-Match carries validator headers only, so the identity headers do not reach the client on that path -- 26 headers observed on the 200, 8 on the 304. This release does not change what the CDN forwards. It removes the case in which the absence mattered: a client that receives a 304 already holds the served version, from the 200 it is revalidating. The 1.3.1 sentence promising identity headers on every response is kept in effect on that basis rather than narrowed to exclude 304, and is read accordingly. - No path, problem code, field, parameter or status changes at this version.
- 1.3.212026-09-08
- The pagination cursor's nature is now stated in the contract: a
next_cursoris a position token, not a credential. It carries the read mode, a fingerprint of the normalized filters, the time boundaries, the pinned snapshot generation and the pagination position. It carries no API key, licence, tier, scope or authorization grant, and every replay authenticates the caller presenting it independently: a cursor minted under one key replays under another key, anonymously, and after the minting key is rotated, each subject to that caller's own scope and budget. Measured against the running handler on 2026-09-07 with synthetic keys and a mocked warehouse; a rotated key received 401 before any read, a wrong scope 403, an exhausted budget 429. Nothing about cursor behaviour changed at this version. What changed is that it is written down. - The cursor payload is readable, not encrypted: base64url JSON plus an HMAC. Position and time boundaries are plaintext; filter values are fingerprinted, and a fingerprint over a small domain such as a ticker symbol permits offline guessing. A cursor should therefore be handled as a URL containing its query would be. A confidentiality change -- encrypt-then-MAC -- is recorded as a separate proposal with its own compatibility window, and is not part of this release.
- No path, problem code, field, parameter or status changes at this version.
- The pagination cursor's nature is now stated in the contract: a
- 1.3.202026-09-07
- The exact-instant replay fix released at 1.3.17 is confirmed against production. Replaying each generation's served
snapshot_as_ofat the six fractional digits 1.3.17 introduced resolves to the generation it names for 791 of 791 activated generations, measured 2026-09-07 over activations from 2026-08-29T03:13:06.031453Z to 2026-09-07T15:58:56.638151Z. Under the millisecond truncation 1.3.17 removed, 787 of those 791 resolve to the previous generation instead, which reproduces the defect the original entry measured at 637 of 641 on 2026-09-04. The 1.3.17 entry recorded its verification as a pinned PostgreSQL fixture and said in its own words that the fixture was not a production measurement; this is that measurement. The 1.3.17 entry is left exactly as written, because it was true when written and a changelog that edits its own history is worth less than one that appends to it. - No path, problem code, field, parameter or status changes at this version. It exists because a published claim about point-in-time replay carried a stated gap in its evidence, and closing that gap changes what a reader knows about the guarantee even though it changes nothing about the behaviour.
- The exact-instant replay fix released at 1.3.17 is confirmed against production. Replaying each generation's served
- 1.3.192026-09-07
- A terminal evaluator-meter failure preserves an existing 4xx status, problem code and body, including when the metering RPC rejects or throws. Previously a malformed or unauthorized request could become a retryable 503 solely because the meter failed. The failure is recorded internally under the request identifier; it does not change the caller's remedy.
- Successful evaluator responses remain withheld when terminal usage cannot be confirmed: the API returns
api_usage_meter_unavailablewithRetry-After: 30, using the existing service-unavailable backoff policy. Rejected metering promises now take that typed failure path as well. The retry delay is a client backoff instruction, not an estimate of recovery time; developer and anonymous requests do not use this evaluator ledger.
- 1.3.182026-09-07
GET /api/v1answers with the service index instead of the website's HTML not-found page. It lists every operation with its href, summary, tag and authentication class, the documentation links, the free and licensed tiers, the pagination, point-in-time, incremental, error and method conventions, and counts. Every value is derived from the OpenAPI document, the problem catalog and the contract's constants, so an operation cannot be added to the contract and be missing from the index. Cached on the same terms as the OpenAPI document it summarises. It is not yet described in the OpenAPI document itself; that needs a response schema and is recorded as an open debt inUNDOCUMENTED_SERVED_PATHS.- A request using any method other than GET, HEAD or OPTIONS on a v1 path answers
method_not_allowed: a 405 carryingapplication/problem+json, anAllowheader, a stablecodeand arequest_id. Until this version the framework's default produced a 405 with an empty body, noContent-Typeand noAllow, which breaks RFC 9110 15.5.6 and this API's own promise that every refusal is typed. Every operation in this contract is a read; the API defines no endpoint that changes the record, so a 405 here is a permanent statement about the contract rather than a permission or a rate decision. Twenty problem codes total. - Deploy gate 29 reads the published surface back. Gate 24 checks that the changelog is internally sound, which cannot catch a contract change that wrote no entry at all: the two changes above shipped on 2026-09-07 with no version move and no release note, and production served the new endpoint carrying the previous version. The new gate starts from the paths and problem codes the API actually serves and refuses a build in which any of them is unaccounted for, undocumented without a written reason, or introduced at the served version without being named in that version's entry.
- 1.3.172026-09-05
- Served disclosure instants carry six fractional digits:
snapshot_as_of,requested_as_of, coverageas_of, receipt and provenance timestamps, and changeobserved_at. Previously these values were truncated to milliseconds while the warehouse retained microseconds: replaying the served snapshot boundary selected the previous generation for 637 of 641 generations, measured 2026-09-04. The exact-instant replay now resolves to the generation it names, 641 of 641 on the pinned PostgreSQL fixture; that fixture is not a new production measurement. as_ofandupdated_sinceaccept up to six fractional digits and reject seven or more with the existing 400 instead of truncating them.- Cursors issued before this version with three-digit signed instants remain accepted: stored instants are canonicalised on read after signature verification, so equivalent three- and six-digit values compare equal.
- Served disclosure instants carry six fractional digits:
- 1.3.162026-09-04
/api/v1/status.temporal_windowpublishespoint_in_time_floor,depth_days,measured_at,retention_regime, and the arithmetic basis. History grows from the retained-history floor rather than rolling off with age. The floor comes from the same dataset-state resolver as disclosure queries; depth is unrounded elapsed days, or null when the floor cannot be read. Queries before the floor still return the existing typed 400, never reconstructed history.- Impossible calendar dates in disclosure date filters return a corrective 400 instead of reaching the database as a retryable 503. Problem responses that lack a route-specific detail use the existing problem catalog's next action.
- The MCP initialize handshake and public/.well-known/mcp.json state the served version. Both had read 1.3.1 since that release while the API advanced to 1.3.15. Deploy gate 27 now checks the descriptor against the contract on every deploy.
- 1.3.152026-09-03
/v1/status.pipelineattributes a run to its due slot by a published rule rather than by the exact minute it started. Until now a run that began even a few seconds past its due minute was recorded as unscheduled and its slot published as missed. Measured 2026-09-03 on the busiest wrapped cron, eight slots an hour: 18 of its 192 due slots read missed, and 17 of those 18 had a run that started 65 to 185 seconds late, every one at a :00 or :30 minute. A run now belongs to the most recent due minute at or before its start when it started within 300 seconds of it; the tolerance and the measurement behind it are stated inbasis. On a schedule whose gap is shorter than a run's lateness the run is attributed to the later slot, so the rule can over-report a miss and never under-report one. Attribution is derived at read time from each run's start, so rows the instrument stored as unscheduled before this version are read the same way as rows stored after it; the store is not rewritten. Each job gainsunattributed_runs_24h: runs that started in the window and attribute to no slot, counted rather than dropped, because they ran.
- 1.3.142026-09-03
/v1/status.pipelinenames the unit it counts.instrumented_jobs,unwrapped_jobsandscheduled_jobscounted canonical invocations, a pathname with its query, so one cron route scheduled under several query shapes was counted several times. Read as route coverage, which is what a reader means by "is this job watched", the published 13 of 68 overstated us fourfold: the true figure is 3 routes of 58. The block now publishesinstrumented_routes,unwrapped_routesandscheduled_routesfor coverage,instrumented_invocations,unwrapped_invocationsandscheduled_invocationsfor the per-slot unit attendance is actually evaluated in, andschedule_entriesfor the number of lines in vercel.json, which exceeds the invocation count when one URL is scheduled twice. The three original fields keep their values and are listed indeprecated_fieldswith their successors infield_successors. Measured 2026-09-03: 3 of 58 routes instrumented, 13 of 68 invocations, 69 schedule entries.- A pipeline job's
statedescribes the job now rather than the last 24 hours.diedhad been set by any run in the window that started and never finished within its ceiling, so the daily export readdiedalthough the run killed at 07:40Z was followed by a successful one at 14:13:02Z; three of five published rows carried a state their own basis did not support.diednow requires the latest attempt to be the dead one, matching the rule that decides whether the endpoint degrades, and every such run in the window is still counted indied_count. No threshold moved and no verdict changes: the endpoint never used the label. - Each pipeline job publishes
scheduled_job. False means an invocation shape that appears in no vercel.json entry, so it has no due slots and cannot report a missed one. Such a row is listed rather than hidden, and the flag lets a reader see why its denominator is zero.
- 1.3.132026-09-03
- The OpenAPI document carries a response example on every record-shaped operation (disclosure page, single disclosure, history, member directory, export manifest) and on every problem response. Each example is a production response captured on 2026-09-03, edited only to describe the opaque cursor and the signed download URL rather than print them, and each is parsed in the build by the same schema that describes the live response, so an example cannot drift from the contract.
- /v1/export: the manifest's
generated_at,snapshot_as_ofandartifact_verified_atare served in theZform the schema has always declared. They had carried the warehouse's+00:00form, which a client validating against the published schema would have rejected. Found by the example test. A manifest whose timestamps cannot be read now returns the newmanifest_timestamp_unreadableproblem rather than a body the contract rejects; the snapshot file itself is unaffected.
- 1.3.122026-09-03
- /v1/status gains
observation_delay: hours from a filing's filed_date to Veridion's first observation, the measurement /data-api has shown since 1.3.4, now machine-readable. Per eligible source (House Clerk, Senate eFD):statemeasured or unevaluated; when measured, the filed-date window, row count, median, p95 (withheld under 100 rows) and maximum in hours; when unevaluated, the reason the distribution refused and the record behind the verdict: the configured observation start, the first and last day of continuity telemetry, days polled successfully, and whether the configured start predates that telemetry. Nothing in this block degrades the endpoint; an unevaluated measurement is a disclosed limit, not a failure of the served rows. Measured 2026-09-03: both sources unevaluated, 90 served rows without a current assessment, telemetry from 2026-08-26 against a configured start of 2026-07-08.
- /v1/status gains
- 1.3.112026-09-03
/v1/status.pipelinedegrades on non-delivery rather than on history: a job degrades the endpoint when it has not completed successfully since its most recent due slot (a slot counts once its run ceiling has elapsed), when its latest run is the one that died, when it has never run, or, for the export job, when its latest run failed. A job that missed a slot and completed since, or whose dead run was followed by a successful one, keeps its missed and died counts in the block without degrading the endpoint. Eachpipeline:<cron>reason now ends with the last due slot it was judged against. Measured on 2026-09-03: the 07:40Z export run was killed by the platform for running out of memory and re-run by hand; under the previous rule the day's dead row would have degraded the endpoint until the next scheduled run.- The daily export job builds and verifies its file as a stream; its result reports the process's own peak memory (
memory.rss_peak_mb). No served field changes.
- 1.3.102026-09-03
- /v1/status gains
pipeline: attendance of the jobs that produce this API's data (the daily export and House Clerk ingestion), from the cron attendance store: per job, its schedules, state (succeeded, wrote_zero, failed, missed, died, never_ran, running, unevaluated), due and observed slots in the last 24 hours, missed and died counts, the last successful finish, and the latest run with its HTTP status and the route's own error text. A missed, died or never-run job degrades the endpoint by name (pipeline:<cron>), as does a failed export run and an unreadable attendance store (pipeline:attendance). The block states how many scheduled jobs are instrumented and how many are not.
- /v1/status gains
- 1.3.92026-09-03
member.districtis served in one form on every disclosure row,STATE-Nwith N an integer and 0 an at-large seat (GA-14,AK-0), the form the /v1/members directory has always used, and the schema now declares it. Measured before the change on the serving view (64,677 live versions): 28,370 rows carried a trailing.0, 5,227 the canonical form, 1,012 an at-large-0, and 3,762 a bare number with no state; those last rows now serve null rather than a district that cannot be placed. district is not a content_hash field, so no hash and no version_id changes.
- 1.3.82026-09-03
- The stability policy is published inside the OpenAPI document (
## Stability policyin info.description) and on /data-api: what additive means, how a breaking change ships (a new major path, the old one served for at least twelve months), how a deprecation is announced (changelog entry plusDeprecationandSunsetheaders on the affected operation), whatmethodology_versionandcontent_hashpromise, and how licensed keys are stored, revoked and rotated. A test refuses any operation marked deprecated without both headers declared.
- The stability policy is published inside the OpenAPI document (
- 1.3.72026-09-03
- /v1/status fails closed on the transparency log: a snapshot generated at or after 2026-09-03T00:36Z (the 1.3.6 deploy) that carries no
merkle_rootdegrades the endpoint with checkbulk_export:merkle_rootand the manifest's day, generation, row count and generation time inmeasured. Older manifests stay null without comment; a newer one without a root is a broken promise, reported rather than tolerated. - MCP server 1.3.1: get_status tells the assistant that
bulk_export.merkle_rootis the day's commitment over every row's content_hash.
- /v1/status fails closed on the transparency log: a snapshot generated at or after 2026-09-03T00:36Z (the 1.3.6 deploy) that carries no
- 1.3.62026-09-02
- The daily bulk export manifest carries
merkle_root: the Merkle root over every row'scontent_hash(leaves in disclosure_id order, sha256 over concatenated child digests, odd node paired with itself), recomputed from the stored bytes before the manifest is written. Published on/v1/status.bulk_exportwithsnapshot_generationandcontent_hash_basis, and on/v1/export.snapshot. Null for snapshots generated before this version. Second layer of the transparency log: one value per snapshot day that a reader recomputes from the export with scripts/verify-disclosure-export.mjs and compares with what Veridion published for that day.
- The daily bulk export manifest carries
- 1.3.52026-09-02
- MCP server 1.3.0: every tool declares outputSchema, generated from the same Zod contract as the OpenAPI component it adapts, and every successful tools/call returns structuredContent equal to the route's JSON alongside the text block. Error results carry no structuredContent.
- Every served disclosure carries
content_hash: sha256 over a published canonical array of the government-filing facts and receipt coordinates (basis veridion-disclosure-content-v1). Recompute it to check the calculation; store it to prove later that the same version_id still serves the same content. First layer of the transparency log.
- 1.3.42026-09-02
- /v1/members:
member_id(exact) andname(case-insensitive substring, 2 to 80 characters) filters, so a filer named on a disclosure row or by a person can be resolved in one request instead of paging the directory. The MCP list_members tool accepts the same two. - /v1/members returns a weak ETag over the directory rows and answers If-None-Match with 304, the same contract the record routes have carried since 1.3.1.
- Every 503 problem response carries Retry-After (minimum 30 seconds unless the route states its own), declared on every documented 503.
- /v1/members:
- 1.3.32026-09-02
- Every v1 response identifies itself: X-API-Version, X-Request-ID, and X-Veridion-Tier on /v1/coverage, /v1/status, /v1/members, and /v1/openapi.json for free callers as well as licensed ones, on success and on failure; the request id in a problem body equals the header. Declared on every documented response in the OpenAPI document.
- Problem documents carry a stable
code, andtypenow dereferences: https://www.veridionmarkets.com/data-api/problems/{code} documents every problem the API can return (meaning, who must change what, whether to retry). The previous type host did not resolve. - /v1/status: congress_public_summary.state gains
unattested(summary refreshed without a recorded outcome) and its degraded_reasons entry quotes both timestamps; the state wordsucceededis no longer emitted as a summary state. - /v1/status: each source carries
index_reconciliation, the government's own index reconciled against what the API serves (House Clerk PTR search today; null where no readable index exists): documents on the index since a cutoff, parsed, terminal without rows, unaccounted, newest filing date on the index versus newest served, and a sample of unaccounted documents with the recorded reason. Unaccounted documents, an unreadable index, or a reconciliation older than 6 h now degrade the endpoint. Added fail-closed; no threshold widened.
- 1.3.22026-09-02
- Browser clients: every v1 route answers OPTIONS preflight (GET, OPTIONS; Authorization and If-None-Match allowed) and every response exposes ETag, Link, the rate-limit headers, and the request id via Access-Control-Expose-Headers.
- 1.3.12026-09-02
- Record routes (/v1/disclosures, /v1/disclosures/{id}, /history) return a weak ETag over the data-bearing response and answer If-None-Match with 304 Not Modified; paginated responses carry Link: rel="next".
- 1.3.02026-09-01
- GET /v1/disclosures/{disclosure_id}/history: every observed version of one filing, oldest first; amendments never erase their predecessors.
- GET /v1/disclosures: min_delay_days (filed at least N days after the transaction) and within_statutory_window (true|false, judged by each row's own statutory window).
- GET /v1/status: degraded_reasons names every check that pulled the state down, with the measurement behind each.
- MCP: get_disclosure_history tool; read-only tool annotations on all five tools; verify_claim prompt; veridion://openapi and veridion://instructions resources.
- Free responses state their rate-limit policy in X-RateLimit-Limit and X-RateLimit-Policy.
- 1.2.02026-08-27
- Point-in-time reads: as_of returns only rows first observed on or before that instant; updated_since polls change events; signed keyset cursors pin the snapshot.
- coverage.point_in_time_floor is published on every response; as_of below the floor is a typed 400.
- 1.1.02026-08-25
- Every served row is receipt-correspondent: receipt.url, receipt page, and source line number resolve to the government document the row was parsed from.
- 1.0.02026-07-29
- Initial v1: /v1/disclosures, /v1/disclosures/{disclosure_id}, /v1/members, /v1/coverage, /v1/status, /v1/export, and the OpenAPI document.
Dates are the day a version reached production. Questions with a request id go to support@veridionmarkets.com.