diff --git a/studio/docs/specs/react-studio-preview/plan.md b/studio/docs/specs/react-studio-preview/plan.md
index 375a8f1b15..219bb9f345 100644
--- a/studio/docs/specs/react-studio-preview/plan.md
+++ b/studio/docs/specs/react-studio-preview/plan.md
@@ -193,3 +193,22 @@ Preview descriptions/tags, notices, all three frameworks and embedded assets;
an actual 3.10.0 pack produces no React packages. Existing publication-selection
and workflow-gate checks still pass. The try guide and release preparation notes
are under Studio docs. This prepares packages without publishing them.
+
+## BPMN interchange
+
+Workflow definitions now imports BPMN through the existing server interchange API,
+shows Info/Degraded/Dropped findings and requires process selection for multi-process
+documents. Structured capability and expression-condition refusals retain server
+messages and offending IDs. Row actions export after a binding/expression warning,
+with absent/unknown-version/stale-source refusals. Browser tests cover the flow,
+cancellation and download/reimport. A disposable real Core check verifies process
+and flow preservation, stable canonical XML after reimport, structured condition
+refusal and cleanup. Server export can normalize XML and add missing layout, so
+original byte equality is not asserted. Editable BPMN canvas work remains #8929.
+
+BPMN review also verifies the native `Elsa.BpmnProcess` route. It opens a dedicated
+read-only view outside the generic designer, preventing normalization and accidental
+autosave before #8929 provides a native canvas. An actual Core-imported process
+retains its exact root after opening and advancing beyond the autosave interval,
+and still exports. Browser cases abort pending analysis/export on cancellation
+and reject late navigation/download effects.
diff --git a/studio/react/README.md b/studio/react/README.md
index fc6c1e8ea5..aa50b2447e 100644
--- a/studio/react/README.md
+++ b/studio/react/README.md
@@ -68,6 +68,7 @@ New to the project? Start with the [Getting started guide](docs/getting-started.
| [Configuration](docs/configuration.md) | **Adding servers**, authentication providers, feature flags, theming, environment variables. |
| [Modules](docs/modules.md) | What every module does, how to enable it, and how to build a new one. |
| [Module authoring](docs/module-authoring.md) | Generate, package and verify an independent extension. |
+| [BPMN interchange](docs/bpmn.md) | Import findings/refusals and export imported workflows. |
| [Parity audit](docs/parity.md) | Verified editor behavior, evidence boundaries and linked Preview gaps. |
| [Activity settings](docs/activity-settings.md) | Commit, log persistence, resilience, task and version settings; instance retry history. |
| [Designer](docs/designer.md) | Containers, the quick-add palette, data flow, call context, keyboard shortcuts. |
diff --git a/studio/react/docs/bpmn.md b/studio/react/docs/bpmn.md
new file mode 100644
index 0000000000..119df8d317
--- /dev/null
+++ b/studio/react/docs/bpmn.md
@@ -0,0 +1,48 @@
+# BPMN import and export
+
+From Workflow definitions, choose **Import BPMN**, select a `.bpmn` file up to
+10 MB, and review the server's Info, Degraded and Dropped findings. Element and
+process IDs identify affected parts. Dropped elements will not run; Degraded
+elements behave differently from their authored definition. A document with more
+than one process requires an explicit process selection before **Import** enables.
+Canceling the dialog persists nothing. Confirming imports the same selected file
+and opens a read-only view of the new draft. This view preserves the native
+BPMN root and never normalizes or autosaves it. Export remains available; canvas
+editing is separate work in #8929.
+
+The API remains the source of truth for supported BPMN capabilities. Capability
+refusals list missing capabilities and offending elements. Unsupported expression
+conditions list sequence-flow IDs and explain why unconditional execution would
+change routing. These messages use stable server error codes and retain the
+server's explanation. Unknown codes and older-server errors show the ordinary
+server error rather than guessing their meaning.
+
+Use a definition row's **Actions → Export BPMN** to download the latest imported
+document. The confirmation warns that bindings and expressions are included.
+Export is refused when BPMN source is absent, its recorded version is unknown,
+or it has become stale after workflow edits. The browser downloads the server's
+XML bytes; the server may normalize XML and add missing diagram layout. Reimport
+preserves the process/flow model, and a subsequent export is stable. It does not
+promise byte equality with an original document that omitted layout.
+
+Import requires `workflows/definitions:write`, export/analyze require `:view`,
+and the backend must have BPMN interchange enabled. Existing read-only server
+configuration hides the import action. Requests share the configured API origin,
+authentication and tenant headers; closing a dialog cancels its outstanding request.
+
+The public API client exposes `client.bpmn.analyze(file, signal)`,
+`client.bpmn.import(file, options, signal)` and
+`client.bpmn.export(definitionId, signal)`, with typed analysis/results and optional
+process/name/definition IDs for API consumers. The UI imports new drafts, so it
+does not overwrite an existing definition.
+
+Browser tests cover findings, process choice, cancellation of pending analysis and
+export requests, native-root preservation without autosave, capability/condition
+refusals, stale/missing-source refusals, unknown error fallback and download/reimport.
+To check a disposable real backend, set `ELSA_TEST_API_URL`, `ELSA_TEST_USERNAME`
+and `ELSA_TEST_PASSWORD`, then run `pnpm test:bpmn`. This checks analysis,
+import/export/reimport, stable canonical XML, structured expression-condition
+refusal and cleanup of its temporary definitions.
+
+This implements interchange for [#8853](https://github.com/elsa-workflows/elsa-core/issues/8853).
+An editable BPMN diagram canvas is separate work in #8929.
diff --git a/studio/react/dotnet/modules/Elsa.ReactStudio.WorkflowDefinitions/Client/components/bpmn-definition-view.tsx b/studio/react/dotnet/modules/Elsa.ReactStudio.WorkflowDefinitions/Client/components/bpmn-definition-view.tsx
new file mode 100644
index 0000000000..14c6813cdb
--- /dev/null
+++ b/studio/react/dotnet/modules/Elsa.ReactStudio.WorkflowDefinitions/Client/components/bpmn-definition-view.tsx
@@ -0,0 +1,21 @@
+import { useState } from "react";
+import Link from "@elsa-workflows/react-studio-sdk/navigation/link";
+import type { WorkflowDefinition } from "@elsa-workflows/react-studio-api-client/types/workflow-definitions";
+import { Button } from "@elsa-workflows/react-studio-ui/button";
+import { PageHeader } from "@elsa-workflows/react-studio-ui/common/page-header";
+import { JsonViewer } from "@elsa-workflows/react-studio-ui/common/json-viewer";
+import { BpmnExportDialog } from "./bpmn-dialogs";
+
+/** The generic designer normalizes unknown roots. Keep BPMN outside that workspace
+ * until the dedicated canvas in #8929 can edit its native process model. */
+export function BpmnDefinitionView({ definition }: { definition: WorkflowDefinition }) {
+ const [exporting, setExporting] = useState(false);
+ return
;
+}
diff --git a/studio/react/dotnet/modules/Elsa.ReactStudio.WorkflowDefinitions/Client/components/bpmn-dialogs.tsx b/studio/react/dotnet/modules/Elsa.ReactStudio.WorkflowDefinitions/Client/components/bpmn-dialogs.tsx
new file mode 100644
index 0000000000..85dac63af0
--- /dev/null
+++ b/studio/react/dotnet/modules/Elsa.ReactStudio.WorkflowDefinitions/Client/components/bpmn-dialogs.tsx
@@ -0,0 +1,95 @@
+import { useEffect, useRef, useState } from "react";
+import { useElsaClient } from "@elsa-workflows/react-studio-api-client/react";
+import { ApiError } from "@elsa-workflows/react-studio-api-client/http";
+import type { BpmnImportAnalysis, BpmnImportResult } from "@elsa-workflows/react-studio-api-client/types/bpmn";
+import { Button } from "@elsa-workflows/react-studio-ui/button";
+import { Input } from "@elsa-workflows/react-studio-ui/input";
+import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@elsa-workflows/react-studio-ui/dialog";
+import { ErrorAlert } from "@elsa-workflows/react-studio-ui/common/error-alert";
+import { downloadBlob } from "@elsa-workflows/react-studio-sdk/utils/download";
+
+/** Stable server codes select explanations; unknown envelopes retain the server message. */
+function Refusal({ error }: { error: unknown }) {
+ const raw = error instanceof ApiError ? error.problem.raw : undefined;
+ const envelope = raw && typeof raw === "object" ? raw as Record : {};
+ const data = envelope.data && typeof envelope.data === "object" ? envelope.data as Record : {};
+ const strings = (value: unknown) => Array.isArray(value) ? value.filter((item): item is string => typeof item === "string") : [];
+ const capabilities = strings(data.capabilities);
+ const elements = strings(data.elementIds);
+ const messages: Record = {
+ "bpmn.import.capability-unsupported": "This deployment does not support every BPMN capability the document requires. Import was refused.",
+ "bpmn.import.condition-unsupported": "These sequence flows carry expression conditions that Elsa does not evaluate. Import was refused to prevent unconditional routing. Remove the conditions or use conditionOutcome.",
+ "bpmn.export.not-imported": "This workflow has no recorded BPMN source to export.",
+ "bpmn.export.source-version-unknown": "The recorded BPMN source version is unknown, so a current export cannot be verified.",
+ "bpmn.export.source-stale": "The workflow changed after import. Export was refused because the recorded BPMN source is stale.",
+ };
+ const message = typeof envelope.code === "string" ? messages[envelope.code] : undefined;
+ return <>
+ {message &&