> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anyformat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# June 2026

> Changes shipped in June 2026.

### June 29, 2026

**\[New + API]** Deterministic validation rules — instant, free checks alongside AI rules

A **Validate** step can now mix two kinds of rules. **AI** rules stay as they were: you describe the condition in plain language and a model judges it. **Deterministic** rules are new — structured checks that run in plain code with no model call, so they're instant and **free**. Seven check types ship: **number range**, **date range** (with a relative `today` bound for not-expired checks), **arithmetic** (`subtotal + tax = total`, with a tolerance), **comparison** (a field vs a value or another field), **one-of** (value in an allowed set), **pattern** (regex), and **required** (present and non-empty). Build them in Studio with the new **AI / Deterministic** toggle on each rule, or author them via the API and SDKs — deterministic rules reference fields by name and carry a `check` payload, AI rules carry a `description`. Pattern checks run on a linear-time regex engine, so a user-supplied pattern can't hang an extraction. Existing workflows are unchanged: rules default to `kind: "ai"`. See [Validation rules](/guides/studio/validate-rules).

### June 22, 2026

**\[New]** Self-serve Business subscriptions, recurring monthly credits, and a billing incident banner

Business is now a self-serve plan. From the dashboard, an organisation owner or admin can click **Upgrade to Business** to open Stripe Checkout, enter a card, and become a paying Business customer in a single round-trip — no support ticket required. Once active, the organisation receives a fresh **100,000-credit monthly grant** on each successful renewal, replacing the previous month's grant rather than stacking on top of it. Enterprise is unchanged in spirit (still sales-led) but rides the same plumbing: support agrees on a custom monthly grant and price, mints a dedicated Stripe price, and pins both to the org on a `BillingPlan` row. Top-ups still work on top of the recurring grant for one-off credit additions.

Subscription customers get a **Customer Portal** link from the **Usage** page to manage their payment method and cancel the subscription themselves. When Stripe reports a failed renewal, a **billing incident banner** appears on every authenticated page describing the current state (`PastDue` during the grace window, `Suspended` if grace expires, `Canceled` after cancellation) with a deep-link to the action that resolves it — update the card, reactivate, or restart the subscription. Late-payment recovery is exact: if a card is fixed after the grace window has zeroed the grant, the next successful invoice restores `cycle_monthly_grant − consumed_during_unpaid_cycle` credits (floored at zero), so a customer never pays for and then loses what they already used.

This rollout is gated behind `BILLING_SUBSCRIPTIONS_ENABLED`. Existing Business and Enterprise organisations whose tier was set by the legacy one-shot upgrade keep their tier through a fallback path until their first real subscription invoice clears.

### June 18, 2026

**\[New + API]** `GET /v2/workflows/{workflow_id}/definition/` — round-trippable workflow definition

A new sub-resource returns the workflow's typed graph in exactly the shape `PUT /v2/workflows/{workflow_id}/` accepts. SDK callers can now GET, mutate one field, and PUT the result back without re-authoring the rest of the schema. The PATCH side preserves `persistent_id` per `(node_id, field_name)` so an unchanged echo cuts no new version (`ChangeKind.NONE` modulo edge order), and genuine edits keep `persistent_id` stable for unchanged-by-id fields — analytics, ground-truth, and monitoring continuity all hold across edits.

**\[Changed + API]** Field `description` is no longer required to be non-empty

The typed surface (`AnyField.description`) previously enforced `min_length=1`, but Path A persistence (the Studio frontend's save path) never enforced it — so legacy workflows can carry fields with empty descriptions that the new GET-definition endpoint needs to round-trip cleanly. Empty descriptions are now accepted on `POST /v2/workflows/`, `PUT /v2/workflows/{id}/`, and the generated SDKs. SDKs and docs still encourage non-empty descriptions — an empty value degrades extraction quality, it isn't structurally invalid.

**\[New + API]** `POST /v2/workflows/{workflow_id}/files/from-url/` — upload by URL instead of multipart

A new route accepts an HTTPS URL and tells anyformat to fetch the bytes server-side, returning the same `CreateCollectionResponse` shape as the multipart `POST /v2/workflows/{workflow_id}/files/` upload. Use it when the source document already lives in object storage you can presign (S3, GCS, R2, …) or at a public HTTPS URL — no need to stream bytes through your own backend. The fetch is bounded by a 10-second timeout and the same 20 MB cap as multipart upload, and runs through the outbound-URL SSRF guard so URLs resolving to non-globally-routable IPs (loopback, RFC1918, link-local, cloud-metadata, IPv4-mapped IPv6) are refused before any connection opens. HTTPS-only at the gateway; non-2xx upstream, DNS failure, or timeout surfaces as `422`. Documented under [Upload File from URL](/api-reference/files/create-from-url) and in [Files](/concepts/files#how-to-provide-a-file).

### June 15, 2026

**\[Fixed + API]** SDK consumers can pull per-file results through `/v2/workflows/{wf}/results/?file_id=...` again

The v3 route `GET /api/v3/files/{file_id}/results/` declared its `workflow_version_id` query parameter as `int`, but the v2 gateway forwards the 10-character `WorkflowVersion.external_id` string returned by `/api/v2/.../latest_version/`. Ninja's int coercion rejected the string with `422`, breaking any SDK call that chained `latest_version` into the per-file-results lookup. The route now accepts `workflow_version_id` as a string, resolves the `external_id` to the internal PK in-route, and returns `404` (not `422`) for an unknown id — matching every other v3 endpoint's "the public `external_id` is the single public identifier" contract.

### June 11, 2026

<Warning>
  **\[Breaking + API]** Object fields support only one level of nesting

  Object fields have always been limited to **one level of nesting** — children of an object must be non-object types — but the API surface advertised the opposite: `ObjectField.nested_fields` was typed recursively, so the generated OpenAPI spec listed object-in-object schemas as valid. The contract is now enforced at the source: posting a schema with a nested object to `POST /v2/workflows/` returns `422`, and the same constraint is mirrored in the regenerated TypeScript SDK types and the Python SDK CLI (which now returns a clean "only one level of nesting is supported" error instead of a raw pydantic traceback).

  **Action required:** schemas that declared an object inside an object must be flattened before submission. Single-level object fields are unaffected.
</Warning>

<Warning>
  **\[Breaking + API]** `POST /api/v3/extractions/progress` removed; replaced by the workflow-progress digest

  The batch progress endpoint `POST /api/v3/extractions/progress` is gone. Callers that want per-extraction progress alongside per-status file counts for a workflow version should switch to the new `POST /api/v3/extractions/workflow-progress-digest` (see the \[New + API] entry below). Single-extraction progress via `GET /api/v3/extractions/{uuid}/progress` is unchanged.
</Warning>

**\[New + API]** `POST /api/v3/extractions/workflow-progress-digest` — one round-trip for workflow polling

A new digest endpoint returns per-extraction progress for a caller-supplied set of `extraction_uuids` AND unfiltered per-status file counts for the surrounding `workflow_version_id` in a single response. The dashboard now polls this endpoint once every 4 seconds while files are processing, replacing three separate polls (`/extractions/progress` at 2.5s, and the v2 `/files/count/` and `/files/` polls at 5s). Polling stops automatically when no extraction is queued or in-progress. Counts come back `null` for unknown or cross-organisation workflow versions; cross-organisation extraction UUIDs are silently dropped, matching the existing single-extraction-progress semantics.

**\[New + API]** Cell-level grounding on extraction evidence

Table-row and scalar evidence now carries true cell provenance end-to-end: `evidence` and `mapped_locations` entries on extraction responses gain optional `cell` and `row` keys identifying the exact source cell that fed each value. Evidence without cell-level grounding (non-table datapoints, or table rows the model couldn't ground precisely) stays byte-identical to before — the new keys are emitted only when present. On the reference invoice that motivated this work, table rows with precise grounding went from 0% to 100% (626/626) with zero wrong-row assignments, and scalars whose value lives in a header table (invoice ids, dates) now resolve to one precise evidence on an LLM-cited page instead of a page-backfilled guess.

**\[New + API]** Test-set membership for files

Files can now be designated as part of a curated, locked test set. A new boolean `is_test_file` is returned on file responses, and two v3 routes manage membership: `POST /api/v3/files/{file_id}/test-set` to add and `DELETE /api/v3/files/{file_id}/test-set` to remove. Files in the test set are **frozen from re-verification**: every verification write (verify, unverify, edit, discard at the datapoint level; verify-extraction; add/delete/swap/verify-row/verify-all at the datarow level, across both v2 and v3) now returns `409 Conflict` with code `ground_truth_locked`, so an extraction designated as ground truth cannot be mutated out from under a downstream accuracy scorer. Ground-truth values and accuracy reporting land in a follow-up release.

**\[New]** Download lookup files from the Extract node UI

Lookup files (master files attached to an Extract node) were upload-only — once added, you couldn't get them back. The **Manage files** dialog in the Extract node now has a download button next to each lookup file, backed by a new presigned-URL endpoint `GET /api/v2/saas_manager/workflows/{id}/master_file/download/?uri=<s3-uri>`. Org membership and the workflow-scoped S3 key prefix are both enforced; the presign is pinned to the canonical bucket so a crafted cross-bucket URI cannot redirect the signed GET.

**\[Improved]** Table extraction now always uses the refine engine

Table parsing previously branched upfront on a heuristic between a cheap `simple_table` call (one shot, no retry — silent data loss on empty/truncated output) and a much slower `dense_table` agent (\~10–20× the cost and latency). The classification was fuzzy and data integrity depended on getting that one-shot guess right, so the routing prompt was biased toward dense, over-escalating and burning cost. Every table now takes the same path — cheap parse → audit critic → in-place refinement with tools — with a best-draft-wins guarantee, so a misclassification can no longer lose data. On an 18-document Iberia benchmark refine was **27% cheaper** (cheaper on every doc) and substantially faster on the slow tail (worst single doc 1346s → 317s), with all four `full_content` quality metrics improving. As part of the change the routing prompt now requires ≥2 rows × ≥2 columns of tabular evidence before sending a block to the table pipeline, so label/value pairs, signatures and logos are no longer misrouted as tables.

**\[Improved + API]** Workflow analytics timeseries response is much smaller and faster

`GET /api/v3/workflows/{id}/analytics/timeseries` now serves the default monitoring view in **a few KB of gzipped JSON** instead of 1.7–3.9 MB. The "Overall" series across all fields is shipped pre-collapsed as a new `overall_rows` array (one entry per extraction), and an opt-in repeatable `field_ids` query parameter narrows the legacy `rows` payload to only the fields you want — present-but-empty (`?field_ids=`) returns counts and `overall_rows` only. Calls that omit `field_ids` still receive the full legacy rows, so existing integrations keep working unchanged. The cold-path recomputation now scopes the live aggregation to cold versions instead of the full workflow, self-heals to all-warm on the first full-universe read, and the response is gzip-compressed.

### June 10, 2026

**\[New]** Per-user timezone preference for API datetimes

User profiles now carry an IANA timezone preference, defaulting to `Europe/Madrid`. Datetime fields in API responses are serialized in the user's chosen zone, and the dashboard **Account** page gains a searchable timezone selector that persists through the existing profile-update flow. Storage stays UTC end-to-end — only the wire serialization (and transitively, what the dashboard renders) reflects the preference.

**\[Fixed + API]** Oversized request bodies now return `400` instead of `500`

Any v3 endpoint that reads a request body returned a `500` when the JSON payload exceeded the 20 MB limit (`DATA_UPLOAD_MAX_MEMORY_SIZE`): Django raised `RequestDataTooBig` while django-ninja read `request.body` during parameter resolution, before the view ever ran, so it could not be caught inside the endpoint. A global exception handler on the v3 API now returns `400` with `{"detail": "Request body is too large. Maximum allowed size is 20 MB."}`. The limit in the message is derived from the setting, so it stays accurate if the cap is tuned.

**\[API]** Rate limiting now enforced on the external API in production

The external API's rate limiter is now active in production. It had been silently disabled for roughly three months because `RATE_LIMIT_ENABLED` and `REDIS_URL` were added to the dev config but never propagated to prod. Callers exceeding the configured limits may now receive `429` responses.

<Warning>
  **\[Breaking + API]** Deprecated v2 datapoint and file-verification endpoints removed

  The following v2 endpoints are gone. They were already marked deprecated and verified unreachable from the dashboard, the external API, the SDKs, and the cronjobs before removal:

  * `PATCH /api/v2/.../datapoints/{id}/` and `POST` / `DELETE /api/v2/.../datapoints/{id}/validation/`
  * `POST /api/v2/.../files/{id}/verify/`

  **Action required:** integrations that still target these paths must switch to the v3 replacements: `POST /api/v3/extractions/{uuid}/datapoints/{verify,unverify}` for verification toggles, `POST /api/v3/extractions/{uuid}/datapoints/{edit,discard}` for value edits and discards, and `POST /api/v3/extractions/{uuid}/verify` for whole-extraction verification.
</Warning>

**\[Fixed]** Verify Document button now works on split + extract workflows

The header **Verify Document** button used to be a permanent no-op on workflows that combine a Split node with an Extract: it targeted the file-level extraction, but extracted data on a split lives on per-split child extractions, so verifying the parent verified nothing. The button now targets the **active split's child extraction** (and is labelled **Verify Extraction** on a split tab to match), and is disabled on parse, split, and validation tabs where there is nothing to verify. A split file is reported as verified only once every non-empty child extraction is verified — empty category tabs no longer wedge the rollup. Shift+Enter advances through unverified splits before moving on to the next file.

**\[Improved + API]** New `verifier` API surface; `validator` deprecated

The "human-validation → verification" rename now reaches the file-level API. A new endpoint `POST` / `PATCH /api/v2/.../files/{id}/verifier/` assigns and updates the verifier for a file, a `verifier__in` filter narrows file lists by assigned verifier, and `?include=verifier` returns the assigned verifier in file responses. The legacy `/validator` action, `validator__in` filter, and `?include=validator` option remain live for backward compatibility but are now flagged `deprecated` in the OpenAPI spec; integrations should plan the move.

**\[Fixed + API]** Non-member reads of workflow analytics now return `404`

`GET /api/v3/workflows/{id}/analytics/summary` and `GET /api/v3/workflows/{id}/analytics/timeseries` previously returned `403 Forbidden` to a caller outside the workflow's organisation, leaking the existence of the workflow. They now return `404 Not Found`, matching `GET /api/v3/workflows/{id}`. Members continue to receive analytics as before.

**\[Fixed]** New free accounts now see their signup credits on the Usage page

The **Usage** page counted only purchased and granted credits, so a brand-new free account — whose only balance is its signup bonus — showed a zero balance and the empty-state screen instead of the credits it could actually spend. Signup credits are now included in both the headline total and the per-source breakdown.

### June 9, 2026

**\[API]** New v3 endpoints for row-level edits on object fields

The v3 API now exposes positional row operations on object-field tables: add, delete, swap, verify-row, and verify-all. Rows are addressed positionally by `(extraction_uuid, field_persistent_id, index)` instead of an opaque datarow id, and the dense `[0, n_live]` invariant is enforced at the boundary (negative indexes now return `422` instead of a silent `404`). The corresponding v2 actions on `DatarowViewSet` are now marked deprecated but remain live; integrations should plan to migrate.

**\[Improved]** Document parsing now retries transient LLM failures more reliably

The parse pipeline now retries `LengthFinishReasonError` (model hit max-tokens, often non-deterministic) and detects silent parse failures where the LLM returned a 200 but the response could not be coerced into the schema. Both previously skipped straight to the fallback model on the first attempt; they now go through the configured retry budget on the primary model first. The per-page parsing token budget was also raised from 8K to 32K to reduce length-truncation failures, and `ParseFailedError` now carries the underlying per-page error detail.

**\[Fixed]** Lookup + Validate workflows now complete end-to-end

Two independent bugs that broke the same Lookup + Validate workflow are fixed:

* A `Validate` node downstream of an `Extract` node with lookup fields previously returned every rule as *inconclusive ("No extraction data available to validate.")*, because the validate step resolved its upstream to a synthetic lookup node and read a state key nothing ever wrote. It now resolves through smart-lookup hops back to the real producing extract.
* The Gemini page-sum verification schema was missing a top-level `title`, which made model-build raise outside the per-task `try/except` and aborted the whole extraction whenever Phase-1 sum verification failed and Gemini verification was attempted.

**\[Improved]** Faster admin pages, file listings, and date-filtered results

Several database hotspots flagged by APM are now indexed and the corresponding queries rewritten to be sargable:

* `Extraction.uuid` and `File.name` get btree indexes (built `CONCURRENTLY`), eliminating sequential scans on the hundreds of lookups per day they served.
* A new composite `FileCollection(workflow_id, created_at DESC)` index backs the per-workflow collection listing.
* `created_at` date filters across the admin dashboard, billing workflow-usage queries, and the public `ResultsFilter` are rewritten from `(created_at AT TIME ZONE 'UTC')::date` casts (which disabled the index, costing up to \~2s on a single filter path) to half-open `created_at` ranges. Behavior is unchanged because the deployment timezone is UTC.

<Warning>
  **\[Breaking + API]** Deprecated v2 datarow endpoints removed

  The v2 `DatarowViewSet` actions marked deprecated in the same release as the v3 datarow rollout — `create_empty`, `delete_by_position`, `swap`, `validate_row`, `validate_all` — and their five `/api/v2/.../files/{file_id}/datarows/` routes are now removed.

  **Action required:** integrations that still drive row-level edits through the v2 paths must migrate to the v3 surface: `POST /api/v3/extractions/{extraction_uuid}/datarows/{add,delete,swap,verify-row,verify-all}`.
</Warning>

**\[Improved]** Per-category grid restores split-granular column filtering

Workflows that include a Classify or Split node show their extracted data in the Per-Category grid in the data viewer. Column filters on that grid had been deliberately disabled because the previous file-paginated path compared against the wrong extraction. A new split-granular results endpoint backs the grid: a filter on an extracted field now narrows to exactly the matching split results, paginated and counted server-side, with filter and sort behavior matching the main file grid.

### June 5, 2026

**\[Improved]** Transient frontend request failures now auto-retry with backoff

The web app retries idempotent React Query requests that fail with transient connectivity errors or 5xx responses, using capped exponential backoff (up to \~10s) and up to 3 attempts. Deterministic 4xx responses and unknown errors still fail fast. Connectivity errors are now detected via the Fetch spec's `TypeError` instead of message matching, which cuts down on false-positive error reports during brief network blips.

**\[API]** Malformed `Authorization: Bearer` headers now return `401`

`Authorization` headers with malformed bearer tokens (for example, an empty token or whitespace inside the token value) now return `401 Unauthorized` instead of `500 Internal Server Error`. The successful auth path and well-formed token-rejection responses are unchanged.

**\[Fixed]** Newly created workflows lay out their nodes with wider default spacing

Newly created workflow nodes are placed with a larger default gap so the auto-layout reads more clearly out of the box and matches the spacing the rest of the editor assumes.

**\[Fixed]** Full-page loading spinner no longer jumps during initial load

The full-page spinner stays anchored in place while the app boots, instead of shifting position as the surrounding layout settles in.

**\[New]** How credits work — new pricing & credits docs page

A new Concepts → Account page, **How credits work**, documents per-operator credit costs (including Validate at 5 credits per rule per page), plan €/credit conversion, included credits, usage tracking, and tips for keeping cost down. The Usage & Billing page links to it in place of the previous "contact us for pricing" stub.

**\[Improved]** Monitoring over-time chart now plots on a true time axis

The accuracy and confidence over-time chart on the Monitoring tab positions each extraction at its real timestamp on a time-scaled axis, instead of forcing points onto an evenly-spaced grid. Time-bucketing now adapts to the visible span, so long, dense histories coarsen gracefully while short spans stay at full resolution. Version markers sit at their exact creation time and merge into a single ranged label (for example `v1.0–v1.2`) when several versions ship the same day, and the empty states now distinguish between no rows, no reviewed extractions, and no recorded confidence scores.

**\[Improved]** Large workflow versions no longer time out when loading file counts

Loading the file counts for a high-volume workflow version no longer tips over the request timeout. The count is now served by a single annotated aggregate backed by an index-only scan, replacing the per-file status scan that caused worker timeouts on workflows with many files.

**\[Fixed]** Filtering and sorting restored on smart-lookup columns

Columns backed by a smart-lookup field keep their sort and filter controls in the data viewer while still showing the lookup indicator. Previously the custom lookup header replaced the entire header cell, hiding the filter button on those columns even though filtering still worked underneath.

**\[Fixed]** Overlapping bounding-box highlights no longer wash out the PDF page

In the PDF visualizer, hovering a stack of overlapping bounding boxes now reads as a single translucent tint on both light and dark pages, instead of compounding toward opaque as more boxes overlapped. Boxes shared by many fields, such as table sections, also render more efficiently.

### June 2, 2026

<Warning>
  **\[Breaking + API]** Monitoring summary `total_extractions` now counts processed extractions only

  `GET /api/v3/workflows/{id}/analytics/summary` previously counted every queued extraction in `total_extractions`, including in-flight rows that had not yet finished processing. It now counts processed extractions only — matching the `verified`, `through`, and per-field counts already returned by the same endpoint, and matching what the cold path (no version filter) has always reported.

  **Action required:** if your integration reads `total_extractions` from the summary endpoint and expects in-flight rows to be included, switch to a non-summary query or add the in-flight count from your own tracking. The Studio monitoring UI is unaffected.
</Warning>

**\[Improved]** Monitoring page now reveals data progressively

Each card on the Monitoring tab paints its title and controls immediately and shows a skeleton only for the body that is still loading, off the data source that feeds it. A fast endpoint can paint without waiting on a slower sibling, instead of the whole page sitting behind a single page-level skeleton. The time-series chart also now keeps daily resolution on 30-day ranges instead of collapsing into \~5 weekly points.

**\[Improved]** Faster workflow version analytics summary

`GET /api/v3/workflows/{id}/analytics/summary` is significantly faster on high-volume workflow versions. Overall metrics, per-field metrics, verified counts, and throughput are now computed in two table scans instead of five, with no change to the response shape.

<Accordion title="Technical detail">
  The endpoint previously routed through the paginated read path and ran the per-extraction aggregate twice plus a discarded pagination subquery. It now serves the summary from a dedicated read that combines the by-field `GROUP BY` and a single per-extraction `COUNT(...) FILTER(...)` aggregate, with the paginated read path unchanged for the timeseries and detail endpoints.
</Accordion>

**\[Improved]** Faster workflow version analytics timeseries

`GET /api/v3/workflows/{id}/analytics/timeseries` is now significantly faster on high-volume workflow versions, applying the same optimization as the summary endpoint. The response shape is unchanged.

<Accordion title="Technical detail">
  The timeseries endpoint consumes only the per-extraction detail rows, but was still served by the full five-scan paginated read — computing the overall sum, per-field `GROUP BY`, and through-rate counts only to discard them. It now uses a detail-only read, and resolves the latest N extractions from the `Extraction` table (which owns `processed_at`) instead of grouping the multi-million-row analytics cache — roughly 34× faster on the top-N lookup, with existing indexes only and no migration.
</Accordion>

**\[API]** Deprecated combined `/analytics` endpoint removed

The deprecated `GET /api/v3/workflows/{id}/analytics` route (flagged for removal when the frontend migration completed) is gone. Use `GET /analytics/summary` for per-version overall and per-field metrics, and `GET /analytics/timeseries` for the cross-version series.

**\[Fixed]** File validation status stays in sync with its datapoints

When you confirm the last unverified datapoint on a file, the file badge now reliably flips to **Validated**, and when you unverify a previously-verified datapoint the file rolls back to **Processed**. Previously, the file-level validation status could drift from its datapoints — most commonly, the last cell confirmation would not flip the file badge.

**\[Improved]** Organization switcher now scales to many organizations

The sidebar organization switcher caps at 10 visible organizations by default with a **Show all** expander, and adds a search box that fuzzy-matches organization names across your full list. Organizations you're a direct member of are prioritized over support-access ones, and a per-user most-recently-used ordering surfaces the organizations you actually work in first. The current organization is always shown.

**\[Fixed]** Deep links to a file's results now switch to the workflow's organization

Opening a workflow file via a direct URL or in a new tab (middle-click) now elevates the active organization to the one the workflow belongs to, matching click-through navigation. Previously a cold-loaded file results page kept the UI on your own organization, hiding the support-access banner and showing the wrong organization in the sidebar.

**\[Fixed]** Organization selector rows stay a readable size on long lists

When you belong to many organizations, each row in the sidebar organization selector keeps a consistent, tappable height with visible spacing, instead of compressing to near edge-to-edge as the list grows.

### June 1, 2026

**\[New]** Workflow version selector

The workflow page header now has a version selector (on the Studio and Monitoring tabs) so you can view any saved version of a workflow. Selecting a non-latest version on Studio disables editing with an explainer — only the latest version can be edited — while Monitoring scopes its analytics to the version you pick.

**\[New]** Per-version monitoring history

The Monitoring tab now shows version history: the accuracy/confidence chart spans all versions with markers at each version change, a changelog panel lists what changed, and KPIs and by-field metrics can be scoped to a selected version.

<Accordion title="Technical detail">
  The analytics endpoint was split into purpose-built reads — `GET /analytics/summary?version_id=` (per-version overall + per-field means and through-rate counts) and `GET /analytics/timeseries?limit=` (cross-version series) — and analytics are now paginated by extraction rather than by (extraction, field) row, so wide schemas no longer collapse the window. The old `/analytics` endpoint is deprecated but retained until the frontend migration completes.
</Accordion>

**\[API]** Python SDK now installs as `anyformat`

The official Python SDK's PyPI distribution has been renamed from `anyformat-sdk` to `anyformat` — `pip install anyformat` — mirroring `@anyformat/sdk` on npm. The import path is unchanged (`from anyformat.sdk import Client`).

<Accordion title="Technical detail">
  The distribution starts at version 0.6.0 (the next free slot past the legacy `anyformat==0.5.0` on PyPI). The internal CLI binary that previously occupied the `anyformat` console-script slot was renamed to `af`; `from anyformat.cli import ...` still works.
</Accordion>

**\[Improved]** Workflow data sub-tabs keep their structural entry points

When a workflow has many data sub-tabs, the collapsed row keeps the leading tabs (Documents, Split/Classify, the first categories) and pins the selected tab to the end, with the overflow in a "+N" menu — instead of hiding everything but Documents and the active tab. Rows with five or fewer sub-tabs show all of them.
