What changed between the legacy API and V3, and what you need to do
Hand off to an LLM→
V3 cleans up the function and routing primitives that grew up across V1 and V2. The mental model didn't change — bem still runs functions inside workflows — but a handful of names and endpoints did. Existing integrations keep working. Legacy types remain readable and callable; you only need to migrate when you create a new function or add a new endpoint.
The /v3 surface replaces a few legacy entry points. Where you used to call the legacy URL on the left, call the V3 URL on the right.
Legacy
V3
Notes
POST /v2/functions/{functionName}/call
POST /v3/workflows/{workflowName}/call
V3 always invokes through a workflow. For a single-function pipeline, wrap the function in a one-node workflow with no edges.
POST /v2/calls
POST /v3/workflows/{workflowName}/call
Per-workflow URL replaces the unified /calls body.
GET /v2/calls/{id}
GET /v3/calls/{callID}
Same shape, V3 wraps the call object consistently.
POST /v1-beta/transformations
POST /v3/workflows/{workflowName}/call
Transformations are now produced by workflow calls.
POST /v2/connectors
inline connectors on POST /v3/workflows
Connectors are workflow configuration, not a separate resource.
POST /v2/collections (Bearer auth)
POST /v3/collections (x-api-key)
Collections moved under V3. The item body field is now data (string or object), not content + metadata.
Subscriptions are available at /v3/subscriptions. The legacy /v1-alpha/subscriptions endpoint still works for backward compatibility and routes to the same handler. See Webhooks for the end-to-end flow.
Adding nodes later produces a new workflow version without breaking existing callers.
Update branching keys. When you build a workflow off a Classify function, the edge field is destinationName and it matches classifications[].name. (In legacy, this was routes[].name on route functions.)
Re-point your polling URL if you read call status: legacy GET /v2/calls/{id} → V3 GET /v3/calls/{callID}.
Switch collections endpoints from /v2/collections (with Authorization: Bearer) to /v3/collections (with x-api-key), and update item bodies to use data instead of content + metadata.
You don't need to migrate a function just to use the new APIs around it: V3 endpoints accept legacy function types for reads and (for extract-equivalent operations) calls. The only hard line is creates and updates — those are V3 types only.
The scoring endpoints are new in V3, so there is nothing to migrate — but they work differently enough from the legacy accuracy tooling to be worth reading before you wire them up.
A run is the extraction; the score is a read. Creating an eval run or a comparison dispatches function calls and nothing more. Comparing that output against your expected values is a pure function of two values bem already stores, so it happens on every read instead of being fixed when the run executed. Re-reading a finished run costs no model calls, and its numbers always reflect your dataset as it stands now.
That means comparison settings are query parameters, not request-body fields:
GET /v3/model-comparisons/{comparisonID}?matchMode=normalized&orderMatching=false
matchMode — strict (default), normalized, or fuzzy. Anything else is a 400; an unrecognized mode is rejected rather than scored at some other strictness. This is the whole of strictness. normalized compares numbers as numbers and dates by calendar value rather than by spelling; fuzzy adds a similarity pass over free text. There are deliberately no tolerance or similarity dials: a threshold you can widen until the numbers look acceptable is not measuring anything.
orderMatching — score array elements in order instead of as sets.
includeRowResults — include per-row, per-field detail. Off by default; it can be large.
GET /v3/eval/score/{scoreRunID} takes no parameters at all. Its comparison is exact: a value matches the expected one or it does not.
Only match counts toward precision, recall and f1. A field result also carries delta for every non-identical numeric pair and similarity (a Levenshtein ratio) for every non-identical string pair — they tell you how close a wrong value was, which never makes it right.
For the full workflow — building a dataset, comparing versions, reading lift, and spotting mislabeled rows — see Comparing Functions on a Dataset.
Legacy reference pages remain available under API Reference → Legacy for endpoints that don't have a V3 successor yet. Most function-calls, events, pipelines, connectors, actions, and subscriptions endpoints are accessible there. Each legacy page that does have a V3 successor carries a deprecation callout linking to it.