Langfuse v4 replaces the ClickHouse traces and observations tables with two new ones, events_full and events_core, and changes nothing about the infrastructure: same web and worker containers, same PostgreSQL, ClickHouse, Redis and S3. The trap is that events_only is the v4 default, so pulling the v4 image without pinning LANGFUSE_MIGRATION_V4_WRITE_MODE=legacy or =dual performs the whole cutover in one rollout, where 22 endpoints start returning 404, Python SDK v2 and JS/TS SDK v3 producers are rejected at ingestion, trace-level evaluators stop running and the historic backfill starts. Before the image changes at all, run one query against PostgreSQL on the latest v3 release: any background migration with a null finished_at means the v4 schema migrations will permanently delete the rows that were never copied to ClickHouse. ClickHouse has to reach 25.12 while you are still on v3, and the CLICKHOUSE_USER needs 11 additional GRANT statements or the migrations fail with Not enough privileges. Producers on Python SDK 4.0.0 to 4.6.x and JS/TS SDK 5.0.0 to 5.3.x still ingest but land roughly 15 minutes late through a staging pipeline, so the real floors are Python 4.7.0 and JS/TS 5.4.0. The historic backfill runs exactly once, in five ordered steps, and wants roughly 3x your current ClickHouse volume in headroom. Rolling back after the schema migrations apply is not an image swap: the v3 web container exits with no migration found for version 46, and the way home is a migrate goto 37 rewind or the backup. Start by running the background-migrations query today, against the v3 release you are already on.
The Langfuse v3 to v4 upgrade on a self-hosted deployment is not an infrastructure migration. The component list does not change: the same web and worker containers, the same PostgreSQL, ClickHouse, Redis and S3-compatible object store that Langfuse v3 has run on since December 2024. No new service, no new queue, no second database. What changes is the data model inside ClickHouse, and your producers, API consumers, evaluators and export jobs all have opinions about it.
The sharp edge is in the defaults. events_only, the write mode that reads and writes only the new tables, is the v4 default and needs no configuration. A deployment that pulls the v4 image without pinning LANGFUSE_MIGRATION_V4_WRITE_MODE performs the entire cutover in the rollout that upgrades the server: 22 deprecated endpoints start returning 404, Python SDK v2 and JS/TS SDK v3 producers are rejected at ingestion, trace-level LLM-as-a-judge evaluators stop running, the legacy export source stops producing data, and the historic backfill starts writing. Langfuse documents this as a migration you schedule in three independent steps. The default collapses all three into one deploy.
What follows is the runbook for an operator already running Langfuse v3 inside their own perimeter, with years of traces to preserve. If the platform choice is still open, our comparison of Helicone, Langfuse and LangSmith covers that. Langfuse Cloud has run on this data model since early 2026, where none of this is the customer's problem: on-premise means the backfill headroom, the schema rewind and the backup window are yours.
What actually changes between v3 and v4
Two new ClickHouse tables replace traces and observations as the source for all reads. events_full is the immutable, full-fidelity table: every LLM call, tool execution and agent step is one row, with the trace-level attributes (user, session, tags, release, version) on the row instead of joined at read time. events_core is a lightweight projection with truncated input, output and metadata, populated by a materialized view, serving the table and chart queries.
Traces stop being a separate entity. Existing traces convert into virtual root spans of type SPAN with the span ID t-<trace_id>, so historic data stays browsable, and the UI collapses into a single Observations view that opens filtered to root observations only, reproducing the old one-row-per-trace table.
Initial table loads for large amounts of data go from seconds to milliseconds, and dashboard load times for large projects improve by 10x or more over longer time ranges. The new model is also what brings full-text search across inputs, outputs and metadata, the filter search bar, alerts, and the Observations API v2 and Metrics API v2 to self-hosted deployments.
| Layer | Langfuse v3 | Langfuse v4 |
|---|---|---|
| Services | web, worker, PostgreSQL, ClickHouse, Redis, S3 | Identical, no new components |
| ClickHouse read source | traces and observations | events_full and events_core |
| A trace | A separate entity with its own table | A root span in events_full, span ID t-<trace_id> |
| Trace attributes at read time | Joined from traces | Written onto every event row |
| PostgreSQL application data | Projects, users, prompts, datasets, score configuration | Unaffected by the data-model change |
| ClickHouse version | v3 runs on current releases, no v4-level requirement | 25.12 minimum, 26.4 recommended |
Prove every v3 background migration finished before you touch the image
The v4 schema migrations are not purely additive. Alongside creating the new tables, they drop seven tables superseded during the v3 line, whose live copies already moved to ClickHouse back then: traces, observations, scores and dataset_run_items in PostgreSQL, plus event_log, project_environments and the legacy dataset_run_items table in ClickHouse. Provided every v3 background migration completed, those drops remove only data that exists elsewhere.
If one did not complete, they do not. A migration that failed and was never retried, or that never ran because the deployment jumped forward from an older v3 release, leaves rows that were never copied into ClickHouse, and those are exactly the rows the v4 migrations delete permanently. This is the one step with no recovery path other than the backup.
Go to the latest v3 release first, let the background migrations drain, and verify. The UI shows this under the Langfuse version tag, in Background Migrations, where every entry has to show a finished state. The runbook version is one query against PostgreSQL:
-- Must return zero rows before upgrading to v4: SELECT name, failed_at, failed_reason FROM background_migrations WHERE finished_at IS NULL;
Then back up both databases immediately before the server upgrade. There is no automatic downgrade once the v4 schema migrations have applied.
The ClickHouse traces, observations and scores tables are not among the seven that get dropped. They keep receiving every incoming event while you run legacy or dual, and the historic backfill only reads them.
Minimum versions: ClickHouse 25.12, Postgres 15, Redis 7.0
ClickHouse carries the real requirement: 25.12 is what provides the lightweight updates, the JSON type and the full-text search the new model depends on. Sequencing matters as much as the number. Do the ClickHouse upgrade while you are still running Langfuse v3, which stays compatible with current ClickHouse versions, so you can complete the storage upgrade, run on it for a month, and only then touch the application image.
Those numbers are release lines, not pinned builds: choose the current patch inside the 25.12 or 26.4 line from the Langfuse ClickHouse deployment guidance.
-- Schema migrations: the v4 migrations add columns and indexes, replace views, -- and change materialized view queries GRANT DROP VIEW ON default.* TO 'user'; GRANT ALTER ADD COLUMN, ALTER MODIFY COLUMN, ALTER VIEW MODIFY QUERY ON default.* TO 'user'; GRANT ALTER ADD INDEX, ALTER DROP INDEX, ALTER MATERIALIZE INDEX ON default.* TO 'user'; -- System table reads for event propagation, the historic backfill, and the -- v4 transition usage detection GRANT SELECT(database, table, name, partition, partition_id, active, rows) ON system.parts TO 'user'; GRANT SELECT(database, table, is_done) ON system.mutations TO 'user'; GRANT SELECT(database, name, engine) ON system.tables TO 'user'; GRANT SELECT ON system.processes TO 'user'; GRANT SELECT ON system.query_log* TO 'user'; -- Merge control on the intermediate table used by the historic backfill GRANT SYSTEM SYNC REPLICA, SYSTEM MERGES, ALTER SETTINGS ON default.observations_pid_tid_sorting TO 'user'; -- Clustered deployments (CLICKHOUSE_CLUSTER_ENABLED=true) only GRANT READ ON REMOTE TO 'user'; GRANT CLUSTER ON *.* TO 'user';
# Chart v2.0.0 ships Langfuse v4 as its default appVersion.
# Pin the image so the chart migration and the v4 upgrade stay two separate changes.
langfuse:
image:
tag: "<your-current-v3-tag>" # not the chart default
clickhouse:
deploy: true # this is what makes the chart v1 to v2 move a prerequisitecd examples/upgrade-v1-to-v2/scripts # Required: kubectl, helm >= 3.17, jq, python3 # YAML parsing also needs one of: yq (mikefarah), PyYAML, or Ruby ./migrate-v1-to-v2.sh --values /path/to/your-v1-values.yaml # Skip prompts (still prints the plan) ./migrate-v1-to-v2.sh --values /path/to/your-v1-values.yaml --yes # Preflight + generate values only ./migrate-v1-to-v2.sh --values /path/to/your-v1-values.yaml --dry-run
The grants v3 never needed
Langfuse v4 requires more ClickHouse grants than v3; without them the schema migrations or the background jobs fail with Not enough privileges. That is 11 GRANT statements on the CLICKHOUSE_USER, applied in the same window as the ClickHouse upgrade: The breakdown is three statements for the schema migrations, five system-table reads, one for merge control on the backfill's scratch table, and two that apply only where CLICKHOUSE_CLUSTER_ENABLED=true. Running with CLICKHOUSE_USE_LIGHTWEIGHT_UPDATE=true adds the enable_lightweight_update setting on top. If you intend to run the automated historic backfill later, the ClickHouse disks need roughly 3x the current data volume, because observations are copied into an intermediate re-sorted table first. That figure is stated once and says nothing about whether replication multiplies it, so on a replicated cluster measure the scratch table's growth on a single shard during a staging run.
Helm chart v1 to v2 comes first when ClickHouse is bundled
If your release runs the ClickHouse bundled with the Langfuse Helm chart (clickhouse.deploy: true), a separate project sits in front of the v4 upgrade: the v1 chart cannot bring that ClickHouse to a version v4 accepts, and the v2 chart can. External ClickHouse is unaffected. Chart v2.0.0 replaces four bundled stores at once: PostgreSQL from bitnami/postgresql to groundhog2k/postgres, ClickHouse from bitnami/clickhouse plus ZooKeeper to a ClickHouseCluster and KeeperCluster under the upstream ClickHouse Kubernetes operator, Redis from bitnami/valkey to valkey-io/valkey, and object storage from bitnami/minio to SeaweedFS in all-in-one mode. A raw helm upgrade from a v1 release that still deploys any of those is blocked by the chart itself: StatefulSet identities, PVC layouts and the ClickHouse coordination backend all change, and the new volumes would start empty. Two paths are supported: with every store already external (*.deploy: false) the original release upgrades in place, and with any store bundled you install a sibling v2 release, copy the data across while v1 keeps serving, then shift traffic. Chart v2.0.0 ships Langfuse v4 as its default appVersion, so pin the image: To stay on the v1 chart instead, pass a 1.x chart version explicitly through helm upgrade --version. The langfuse-k8s repository ships a migration script that reads your v1 values file, migrates only the bundled components, and prompts before each larger step: Three constraints will cost you the rehearsal if you miss them. Reuse the same Langfuse salt, encryptionKey and nextauth.secret on the v2 release, or the encrypted PostgreSQL columns become undecryptable. Keep the same Langfuse application version on the v1 source and the v2 sibling while data copies, meaning both the chart appVersion and any langfuse.image.tag override have to match. And the chart guide states a minimum supported source Langfuse version: check it against the release you are actually on, because the chart move can run ahead of the latest-v3 prerequisite. Under five minutes of application downtime is the project's design target at roughly 500 GB of ClickHouse, 100 GB of object storage and tens of GB of PostgreSQL, not a number for your change ticket.
| Component | Minimum | Recommended |
|---|---|---|
| ClickHouse | 25.12 | 26.4 |
| PostgreSQL | 15 | 16 |
| Redis | 7.0 | 7.2 |
The three write modes, and why the default is the cutover
legacy retains the full v3 ingestion and read behaviour on the v4 release, which makes the image change an ordinary deploy:
LANGFUSE_MIGRATION_V4_WRITE_MODE=legacy LANGFUSE_MIGRATION_V4_NATIVE_OTEL_BEHAVIOUR=dual_write # Keep the v4 read paths (UI toggle, v2 APIs) off; the events tables # are not written in legacy mode: LANGFUSE_MIGRATION_V4_ALLOW_PREVIEW_OPT_IN=false LANGFUSE_BACKGROUND_MIGRATION_V4_ENABLE_HISTORIC_BACKFILL=false
dual is where the migration actually happens, and the stated cost is plain: it doubles the write load and the storage cost. The configuration below keeps the v4 read paths off while you prove the write path, so the UI toggle and the v2 APIs stay hidden until you set LANGFUSE_MIGRATION_V4_ALLOW_PREVIEW_OPT_IN=true, which is worth doing only after a few days of healthy dual writing:
LANGFUSE_MIGRATION_V4_WRITE_MODE=dual # Only if native OTel producers rely on server-side attribute propagation: LANGFUSE_MIGRATION_V4_NATIVE_OTEL_BEHAVIOUR=dual_write # Recommended until dual_write behaviour is confirmed to be working # (see step 3): LANGFUSE_BACKGROUND_MIGRATION_V4_ENABLE_HISTORIC_BACKFILL=false # Optional: set false to keep the v3 read experience for all users while # dual-writing (hides the toggle, disables the v2 APIs); defaults to true. # Flip to true or remove overwrite, if you want to allow users to opt-in to v4 experience. LANGFUSE_MIGRATION_V4_ALLOW_PREVIEW_OPT_IN=false
The OpenTelemetry setting decides who propagates the shared attributes. direct, the v4 default, writes every OTel span straight into the new tables and assumes the producer attaches userId, sessionId and the rest client-side. dual_write keeps the server doing that propagation, routing those spans through the staging pipeline unless they carry the x-langfuse-ingestion-version: 4 header. Which one is right depends on how stable your instrumentation's attribute keys are, and the gen_ai conventions moved enough in 2026 to be worth checking.
The cutover is the smallest diff here and the largest change in behaviour: remove the three overrides, or set them explicitly to the v4 defaults.
# Remove these overrides (or set them explicitly): # LANGFUSE_MIGRATION_V4_WRITE_MODE=events_only # LANGFUSE_MIGRATION_V4_NATIVE_OTEL_BEHAVIOUR=direct # LANGFUSE_MIGRATION_V4_ALLOW_PREVIEW_OPT_IN=true
Two traps sit around those variables. Langfuse validates this configuration on startup and exits on invalid combinations, specifically events_only with dual_write OTel behaviour and events_only with LANGFUSE_MIGRATION_V4_ALLOW_PREVIEW_OPT_IN=false, so a half-removed override becomes a container that will not boot. And the names carry an uppercase V4 that is easy to lowercase in a templated values file, where a typo reads as unset and hands you the default, which is the cutover.
Neither legacy nor dual is a destination. Both will be removed in an upcoming major version, with no version number or date attached. The only dated commitment nearby is that Langfuse v3 receives security patches until the end of January 2027, which makes this a scheduled change rather than an optional one.
| Write mode | Where ingestion writes | Deprecated endpoints, trace-level evaluators, legacy export source | Choose it when |
|---|---|---|---|
legacy | Old traces and observations only | All keep working | You want the server upgrade to change nothing else and to schedule the data-model migration entirely separately |
dual | Both old and new tables | All keep working | Some producers still run older SDKs, or you want the new read path with the old tables as a written safety net |
events_only (v4 default) | New events tables only | All stop | Every producer already runs a compatible SDK and no consumer touches the deprecated endpoints |
Who breaks at ingestion: SDK floors and the 15 minute delay
Those five producer classes are why the SDK inventory comes before the server upgrade: a rejected producer loses data outright at the cutover, a delayed one loses nothing and arrives late.
The mechanism behind the delay bounds your recovery window. Events from older SDKs go to traces and observations as before, and additionally into a staging table, observations_batch_staging, partitioned into three-minute windows. A worker job picks up completed partitions, joins them against traces to propagate the trace-level attributes, and inserts the result into events_full. Staging partitions are kept for 48 hours, so a propagation job that dies over a weekend is recoverable, provided you notice inside two days.
That is the argument for wiring one health check before you switch to dual:
# Worker container, port 3030. Returns 503 when event propagation is stuck. curl -sf "http://<worker-host>:3030/api/health?failIfEventPropagationStuck=true" # Threshold is configurable; default is 15 minutes: # LANGFUSE_EVENT_PROPAGATION_STUCK_THRESHOLD_MINUTES=15
The check passes on deployments where the dual write does not run, so leave it configured after the cutover. This is a platform-health probe and nothing more; what to alert on inside the application is a separate set of signals.
The producer side can be done first. Python SDK 4.7.0 and JS/TS SDK 5.4.0 both shipped in late May 2026, roughly two months before Langfuse v4.0.0 arrived in July 2026, and both lines run against v3 servers. Two caveats: client.api.observations.* and client.api.metrics.* point at v2 endpoints a v3 server does not serve, so use client.api.legacy.* meanwhile, and trace input and output have to be set explicitly if you depart from the default root-span behaviour. Framework integrations are not enumerated, so canary one service rather than assuming blanket compatibility.
Build that inventory from the network, not from memory. If calls route through an LLM gateway, the version that matters is the gateway's: one upgrade there clears a dozen services or blocks them all.
| Producer | Behaviour on Langfuse v4 |
|---|---|
| Python SDK v2 and older, JS/TS SDK v3 and older | Rejected at ingestion once the deployment runs events_only; keeps working on legacy and dual |
| Python SDK 4.0.0 to 4.6.x, JS/TS SDK 5.0.0 to 5.3.x | Still ingests, but can appear with a delay of roughly 15 minutes until upgraded |
| Python SDK 4.7.0 and above, JS/TS SDK 5.4.0 and above | Propagates attributes client-side and writes into the new tables directly, no delay |
| Native OpenTelemetry exporter without the version header | Follows LANGFUSE_MIGRATION_V4_NATIVE_OTEL_BEHAVIOUR: dual_write routes it through staging, direct writes it directly |
Native OpenTelemetry exporter sending x-langfuse-ingestion-version: 4 | Writes directly regardless of that setting |
The historic backfill runs exactly once, in five ordered steps
Data ingested before the dual write became active exists only in the old tables. The automated backfill brings it across as a chain of background migrations on the worker, each step starting only after its predecessor finished, so a failure halts the chain instead of running on partial data.
The ordering rule is what costs data. Enable the backfill only after the dual write is active and confirmed healthy: it runs exactly once, so anything ingested after its cutoff but before the dual write started would be permanently missing from the new tables. That is why LANGFUSE_BACKGROUND_MIGRATION_V4_ENABLE_HISTORIC_BACKFILL=false belongs in the legacy configuration from the first deploy: on legacy the new tables are not written at all, so a backfill that fires there leaves a gap nothing later fills.
Step 3 carries a detail for anyone whose dashboards filter on trace metadata. Trace properties (name, user, session, version, release, tags, and the public and bookmarked flags) are propagated onto the backfilled child events, but trace metadata is deliberately not copied, to keep that join inexpensive. A saved view filtering on metadata needs rebuilding, not debugging.
Historic data appears gradually and newest first, so for a period only recent dual-written data is visible. There is no downtime and no impact on new ingestion, the migrations are resumable across restarts, and because the new tables use a ReplacingMergeTree, re-runs and overlaps with the dual write are de-duplicated with the most recent version winning.
Langfuse publishes no rate for this: the only stated commitment is that it can take from minutes to days depending on data volume. If a duration has to go into a change ticket, restore a ClickHouse snapshot into staging, run the backfill there and measure it.
When the traces are a regulated record
The second option for historic data is the one a regulated deployment should look at first, because it uses a control already in place. Where a global data retention policy is enforced, say 30 or 90 days, keep the backfill disabled and dual-write until one full retention window has elapsed since dual was enabled. Everything inside retention then exists in the new tables and everything older has aged out by policy. No backfill, no scratch table, no 3x disk spike, which in a fixed rack is a procurement conversation. A legal hold breaks that plan. Traces under hold cannot age out, so the retention window never rolls over for those projects and you are back to the backfill or to keeping the old tables as an archive. That bites hardest after the cutover, because the truncate is irreversible and the old tables are both the backfill source and the last route to a v3 read path. Anything under hold gets exported to the archive of record first, or does not get truncated. The pre-upgrade backup deserves the same treatment. It is a full copy of trace payloads, meaning inputs, outputs and whatever the application put in metadata, so it inherits the classification of the traces and belongs under the same access controls, encryption and retention clock as the live store. It has to survive until the cutover is verified, which may be weeks: a snapshot on a seven-day lifecycle rule is not a rollback plan.
| Step | What it does |
|---|---|
| 1. Create root spans | Generates virtual root spans in events_full from the existing traces, rewriting all traces as type SPAN with an empty parent ID |
| 2. Rewrite observations | Copies observations into a scratch table, observations_pid_tid_sorting, re-sorted by (project_id, trace_id, id) so the next step can join efficiently |
| 3. Backfill events from observations | Reads the scratch table, joins it against the live traces table, and writes the child events into events_full |
| 4. Backfill from dataset run items | Walks experiment trace trees and enriches the corresponding spans with experiment metadata |
| 5. Drop scratch tables | Removes observations_pid_tid_sorting, gated separately by LANGFUSE_BACKGROUND_MIGRATION_V4_DROP_PID_TID_SORTING_TABLES, default off |
What returns 404 at the cutover, and why rollback is a schema rewind
Twenty-two endpoints across five of those seven groups return 404, and two more change behaviour without returning it. Replacements exist for all of them: the Observations API v2, the Scores API v3, the Metrics API v2, and the experiments API for dataset runs.
Two non-endpoint casualties land at the same moment. Trace-based and legacy-dataset-based LLM-as-a-judge evaluators look up the old tables, are marked Legacy in the UI, and stop running after the cutover, so recreate them as observation-level evaluators first. And the blob storage, PostHog and Mixpanel integrations read through a per-integration export source: the legacy traces and observations source (LEGACY_TRACES_OBSERVATIONS) stops producing data at the cutover, so switch each one to the enriched observations source before the cutover.
The consumer inventory is harder, because consumers are not in your dependency file. Grep the estate for /api/public/traces, /api/public/observations, /api/public/metrics and /api/public/scores across dashboards, scheduled jobs, notebooks and the ad hoc scripts people reach for when reading traces during an incident.
Rollback is where expectations break. A plain image swap does not work once the v4 schema migrations have applied: the v3 web container exits on startup with no migration found for version 46 while the worker container starts normally, so a half-green deployment reads as a web-container problem, not a schema problem. The rewind uses the migrate binary shipped inside the v4 web image:
# Run inside a v4 web container of the SAME version that is deployed,
# so the migration files match the recorded schema version:
cd /app/packages/shared
# Single-node ClickHouse (CLICKHOUSE_CLUSTER_ENABLED=false):
migrate -source file://clickhouse/migrations/unclustered \
-database "${CLICKHOUSE_MIGRATION_URL}?username=${CLICKHOUSE_USER}&password=${CLICKHOUSE_PASSWORD}&database=${CLICKHOUSE_DB:-default}&x-multi-statement=true&x-migrations-table-engine=MergeTree" \
goto 37
# Clustered ClickHouse:
migrate -source file://clickhouse/migrations/clustered \
-database "${CLICKHOUSE_MIGRATION_URL}?username=${CLICKHOUSE_USER}&password=${CLICKHOUSE_PASSWORD}&database=${CLICKHOUSE_DB:-default}&x-multi-statement=true&x-cluster-name=${CLICKHOUSE_CLUSTER_NAME:-default}&x-migrations-table-engine=ReplicatedMergeTree" \
goto 37Version 37 is the schema version of the latest v3 releases. The rewind removes the events tables and recreates the ones v4 dropped. PostgreSQL needs no changes, because the latest v3 releases run against the v4 PostgreSQL schema, with the v4 backfill entries dormant unless LANGFUSE_BACKGROUND_MIGRATION_V4_ENABLE_HISTORIC_BACKFILL is true. Append &secure=true&skip_verify=true to the URL if your deployment sets CLICKHOUSE_MIGRATION_SSL=true, then deploy the latest v3 images.
Prefer rolling forward. While you run legacy or dual, every v3 surface still works on the v4 release, so staying on v4 in legacy mode while you debug retains the complete v3 behaviour. After the cutover that stops being true: new data lands only in the new tables, and a rollback to a v3 read path would miss everything written since. That is the point of commitment.
The cleanup afterwards is optional and irreversible. Set LANGFUSE_BACKGROUND_MIGRATION_V4_DROP_PID_TID_SORTING_TABLES=true once the backfill result is confirmed, then reclaim the old tables once every record you need is visible:
TRUNCATE TABLE traces; TRUNCATE TABLE observations; -- On clustered ClickHouse deployments; substitute your CLICKHOUSE_CLUSTER_NAME -- for `default` if it differs: TRUNCATE TABLE traces ON CLUSTER default; TRUNCATE TABLE observations ON CLUSTER default;
TRUNCATE rather than DROP, because Langfuse expects the tables to exist. And not before you are sure.
| Endpoint group | Endpoints | Behaviour under events_only |
|---|---|---|
| Legacy ingestion writes | POST /api/public/traces, /spans, /generations, /events | 404 |
| Batch ingestion | POST /api/public/ingestion | Keeps 207, returns 400 for every event type except score-create or sdk-log |
| Trace and observation reads | GET /api/public/traces, /traces/:id, /observations, /observations/:id, /sessions, /sessions/:id, /spans, /generations | 404 |
| Score reads | GET /api/public/scores, /scores/:id, /v2/scores, /v2/scores/:id | 404 |
| Metrics reads | GET /api/public/metrics, /metrics/daily | 404 |
| Dataset run reads | GET /api/public/dataset-run-items, GET /api/public/datasets/:name/runs, GET and DELETE /api/public/datasets/:name/runs/:runName | 404 |
| Dataset run item writes | POST /api/public/dataset-run-items | Returns a stale compatibility object; not for use on v4 |
Three things to do this week that need no change window
Run the background-migrations query against the PostgreSQL behind the Langfuse v3 release you are already on. Any rows in that result are the longest lead time in this migration, because clearing them means getting onto the latest v3 release first.
Inventory both sides: producers against the Python 4.7.0 and JS/TS 5.4.0 floors and the v2 and v3 rejection lines, consumers by grepping for the deprecated endpoint paths rather than asking teams what they call.
Then decide whether your historic data comes across through the backfill or through a retention window. That determines whether you are buying 3x ClickHouse headroom or scheduling a longer dual-write phase, and it is cheaper to settle before the image changes. The rest of the platform decisions sit in our AI development tools pillar.
FAQ
Quick answers to the questions this post tends to raise.



