# Workflow imports and workspace sync (/api-reference/workflow-sync)



Import an exported workflow as a draft, or fork and synchronize workspaces through the v2 preview-and-apply protocol. Preview the destination bindings, apply those exact choices, then poll the returned operation to verify copies and deployment readiness. The [CLI workflow guide](/cli/workflow-sync) uses the same protocol.

## Choose the interface [#cli-and-http-interfaces]

HTTP paths below are relative to `/api/v2`. `{workspaceId}` is the current workspace you explicitly selected.

| CLI command | HTTP request |
| - | - |
| `sim workflows export <id> --include-references` | `GET /workflows/{workflowId}/export?includeReferences=true` |
| `sim workflows import-preview` / `sim workflows import` | `POST /workflows/import/preview` / `POST /workflows/import` |
| `sim workspaces fork-availability` / `sim workspaces lineage` | `GET /workspaces/{workspaceId}/fork/availability` / `.../fork/lineage` |
| `sim workspaces children` / `sim workspaces fork-resources` | `GET /workspaces/{workspaceId}/fork/children` / `.../fork/resources?kind=tables` |
| `sim workspaces fork-preview` / `sim workspaces fork` | `POST /workspaces/{workspaceId}/fork/preview` / `.../fork` |
| `sim workspaces push-preview` / `sim workspaces push --yes` | `POST /workspaces/{workspaceId}/fork/push/preview` / `.../fork/push` |
| `sim workspaces pull-preview` / `sim workspaces pull --yes` | `POST /workspaces/{workspaceId}/fork/pull/preview` / `.../fork/pull` |
| `sim workspaces mappings get` / `sim workspaces mappings update` | `GET` / `PUT /workspaces/{workspaceId}/fork/mappings` |
| `sim selectors list` / `sim selectors get` | `POST /selectors/list` / `POST /selectors/get` |
| `sim workspaces operations get <id>` / `sim workspaces operations wait <id>` | `GET /workspaces/{workspaceId}/operations/{operationId}`; wait polls this endpoint |
| `sim workspaces operations list` | `GET /workspaces/{workspaceId}/operations` |

The [sync reference](/api-reference/workspace-sync/previewWorkspacePull) documents fields and includes rollback, unlink, and exclusions. Children, resource discovery, mappings, and operations are paginated: follow `nextCursor` with the same scope and filters.

## Prerequisites and access [#permissions]

Set `BASE_URL` to your deployment origin, without `/api/v2`. Use a personal API key in `X-API-Key`, or an OAuth token in `Authorization: Bearer`. Examples use separate authorized source/destination keys where applicable. Replace all example resource IDs with IDs from your export and destination.

| Operation | Required access |
| - | - |
| Export | Workflow read access |
| Import preview/apply | Destination write access; binding credentials also requires an acting user with credential access |
| Fork discovery, preview, creation | Source workspace Admin; creation policy must allow a new workspace |
| Sync preview/apply and mapping read/update | Admin on both workspaces on a direct parent/child fork edge |
| Rollback / unlink | Target Admin / acting-side Admin, respectively |
| Selector discovery | Read access in the discovery workspace and access to the selected credential |
| Operation get/list | Read access in the receipt workspace |

Workspace keys can import where authoring policy allows, but cannot bind credentials, administer forks, or execute selectors. The key owner is not a substitute for an acting user. Workspace policies, permission groups, credential access, and workspace creation limits still apply.

OAuth import/fork/sync previews require `api:write`, even though previews do not commit changes. Export, fork discovery, mapping reads, selectors, and operation reads use `api:read`.

Forking and sync require the owning organization's Enterprise entitlement on billing-enabled deployments. On billing-disabled deployments, deployment configuration must enable forking. Check [Fork availability](/api-reference/workspace-sync/getWorkspaceForkAvailability) before starting. Imports, selector discovery, and operation reports have their own access rules and do not inherit the fork-specific gate.

## Import a portable workflow [#portable-imports]

### Export and inspect the source [#export-and-inspect-the-source]

Default exports clear registered credential fields and workspace bindings. `includeReferences=true` adds a versioned manifest of resource IDs and their source occurrences, including nested tools. The manifest contains identities, not secret values. Unresolved environment-variable references remain, and ordinary workflow variable values or literal configuration can remain in the full export. Inspect the payload before sharing it.

Source IDs and imported provenance are labels; they do not grant access to an alleged source workspace. `includeWorkspaceBindings=true` can retain non-secret bindings for a same-workspace round trip; otherwise configure any required bindings reported by import.

Raw HTTP wraps the result in `data`; the CLI unwraps a single resource. Extract that payload before saving it:

```sh
curl -sS --fail-with-body \
  -H "X-API-Key: $SOURCE_API_KEY" \
  "$BASE_URL/api/v2/workflows/$WORKFLOW_ID/export?includeReferences=true" \
  | jq '.data' > workflow.json
```

### Map destination resources [#map-destination-resources]

Save `import-mappings.json`, using source IDs from the manifest and authorized destination IDs:

```json
[
  { "kind": "credential", "sourceId": "source-connection", "targetId": "destination-connection" },
  { "kind": "sandbox", "sourceId": "source-sandbox", "targetId": "destination-sandbox" }
]
```

`mappings` applies to every occurrence of a resource's `kind` and `sourceId`. Destination type, provider, and parent/child compatibility are validated. `targetId:null` requests clearing and cannot satisfy a required binding. Conflicting instructions for one occurrence are rejected.

For older exports, `bindings` addresses individual registered occurrences. Entries are flat objects, without `sourceId` or an `occurrence` wrapper:

```json
[
  {
    "kind": "credential",
    "blockId": "source-agent",
    "subBlockKey": "tools",
    "valuePath": [0, "params", "oauthCredential"],
    "encoding": "scalar",
    "targetId": "destination-connection"
  }
]
```

A top-level field uses `valuePath:[]`. Other registered encodings are `array`, `csv`, `files`, and `environment`; multi-value occurrences can include `positions`. Use a returned registered occurrence rather than inventing a path.

### Preview and complete configuration [#preview-and-complete-configuration]

Build one request file and preview it:

```sh
jq -n --arg workspaceId "$DESTINATION_WORKSPACE" \
  --slurpfile workflow workflow.json --slurpfile mappings import-mappings.json \
  '{workspaceId: $workspaceId, workflow: $workflow[0], mappings: $mappings[0]}' \
  > import-request.json

curl -sS --fail-with-body -H "X-API-Key: $DESTINATION_API_KEY" \
  -H 'Content-Type: application/json' --data @import-request.json \
  "$BASE_URL/api/v2/workflows/import/preview" > import-preview.json
```

Inspect `data.unresolvedBindings`, `data.configuration`, `data.unresolvedConfiguration`, and `data.discovery`. Dependent configuration choices have a different shape from bindings:

```json
[
  { "blockId": "source-agent", "subBlockKey": "tools[0].folder", "value": "destination-label" }
]
```

Add them as `dependentValues` to the request and preview again. A `multiSelect:true` field uses a comma-separated string of selected IDs.

For credential-backed selectors, use the import destination workspace and the preview's `selectorKey` and `context` to discover options:

```sh
curl -sS --fail-with-body -H "X-API-Key: $DESTINATION_API_KEY" \
  -H 'Content-Type: application/json' \
  --data "$(jq -n --arg workspaceId "$DESTINATION_WORKSPACE" \
    '{workspaceId: $workspaceId, selectorKey: "gmail.labels", context: {oauthCredential: "destination-connection"}, limit: 50}')" \
  "$BASE_URL/api/v2/selectors/list"
```

Selector lists return `{ "data": [...], "nextCursor": null, "truncated": false }`; check pagination **and** truncation. Detail uses `/selectors/get` with the same workspace/key/context and an `id`. MCP tool discovery uses `mcp.tools` with `mcpServerId`. A missing OAuth connection can require human authorization; changing a resource ID cannot create the connection.

Keep selector workspace, key, context, and search unchanged when paging. A changed provider page can return `409`, requiring discovery to restart. Detail can return `data:null` for a missing choice or `413` when verification exceeds provider bounds. See [List selector](/api-reference/workspace-sync/listSelector) and [Get selector](/api-reference/workspace-sync/getSelector).

### Apply the reviewed import [#apply-the-reviewed-import]

When `data.ready` is true, choose a stable `IMPORT_REQUEST_ID` for this mutation and retain it with the complete request. Apply the exact reviewed choices and fingerprint:

```sh
jq --arg requestId "$IMPORT_REQUEST_ID" \
  --arg fingerprint "$(jq -er '.data.previewFingerprint' import-preview.json)" \
  '. + {requestId: $requestId, previewFingerprint: $fingerprint}' \
  import-request.json > import-apply.json

curl -sS --fail-with-body -H "X-API-Key: $DESTINATION_API_KEY" \
  -H 'Content-Type: application/json' --data @import-apply.json \
  "$BASE_URL/api/v2/workflows/import" > import-result.json
```

Mapped import atomically creates the draft, graph, variables, required inline custom tools, and receipt. Remapping happens before graph ID regeneration; the result includes `idMap`. Plain imports without mapping options retain their earlier behavior and return no operation receipt. Supplying mapping options, even empty arrays, requires `requestId` and `previewFingerprint`.

Imported workflows remain drafts. Check reported configuration and deploy before using ordinary deployed execution.

## Fork a workspace [#fork-and-sync]

Fork preview and apply start with the same choices:

```json
{
  "name": "Review environment",
  "copy": { "tables": ["source-table"] }
}
```

POST the request to `/workspaces/{workspaceId}/fork/preview`. Inspect its plan and fingerprint, then POST the same choices to `/workspaces/{workspaceId}/fork` with a stable `requestId` and the returned `previewFingerprint`.

Eligible deployed source workflows become **child drafts**. Resources are copied only when selected explicitly. Fork creation uses `copy`; sync uses `copyResources`. Fork `copy.files` contains workspace file IDs, while sync `copyResources.files` contains storage keys. Credentials and secret values are not copied. Inspect destination configuration before deploying a fork's workflows.

## Push or pull a sync [#push-or-pull-a-sync]

Push sends deployed workflows from the current workspace to `otherWorkspaceId`. Pull brings them from `otherWorkspaceId` into the current workspace. Either endpoint can run from either side of a direct parent/child edge, with Admin access to both.

Sync can create target workflows, replace mapped target workflows, and archive mapped targets whose source was deleted or archived. It schedules deployments of the admitted snapshots. An undeployed source alone does not archive its mapped target. Excluded workflows remain excluded; review target replacements and retiring trigger URLs before apply.

### Select mappings and copies [#select-mappings-and-copies]

For a pull into the workspace in the URL, save `sync-request.json`:

```json
{
  "otherWorkspaceId": "source-workspace",
  "mappings": [
    { "resourceType": "oauth_credential", "sourceId": "source-connection", "targetId": "destination-connection" }
  ],
  "dependentValues": [
    { "sourceWorkflowId": "source-workflow", "sourceBlockId": "source-agent", "subBlockKey": "tools[0].folder", "value": "destination-label" }
  ],
  "copyResources": { "tables": ["source-table"] }
}
```

Sync mappings use edge `resourceType` names such as `oauth_credential`, `service_account_credential`, `knowledge_base`, `custom_tool`, and `sandbox`. Imports instead use `kind` names such as `credential`, `knowledge-base`, and `custom-tool`. Workflow identity is system-managed. Choose a knowledge document through its parent knowledge base's dependent selector instead of writing a `knowledge_document` edge mapping.

Mapping inspection requires `otherWorkspaceId` and `direction` query fields. Read rows include a storage `id`; writes accept only `resourceType`, `sourceId`, and `targetId`. Project a read page with `jq '[.data[] | {resourceType, sourceId, targetId}]'` before reusing it, and follow `nextCursor` for further pages.

Preview does not persist inline mappings. Apply saves them in the sync transaction. Dependent overrides use source workflow/block/field identities, including original nested-tool indices. Omitting `dependentValues` reuses saved choices; supplying it replaces choices for affected workflows, and `[]` clears them. Values in a target draft alone are not saved sync configuration.

`dropReferences` acknowledges references deleted in the source; it cannot discard live source resources.

### Preview and inspect trigger choices [#preview-and-inspect-trigger-choices]

```sh
curl -sS --fail-with-body -H "X-API-Key: $SIM_API_KEY" \
  -H 'Content-Type: application/json' --data @sync-request.json \
  "$BASE_URL/api/v2/workspaces/$CURRENT_WORKSPACE/fork/pull/preview" > sync-preview.json
```

Review `unresolvedBindings`, `configuration`, planned workflow actions, and retiring URLs. For selector discovery, use each configuration field's `discoveryWorkspaceId`: the source when its parent will be copied, the destination when mapped. Pass the returned context and preview any changed choices again. `ready:true` means the apply plan is ready, not that all destination deployments will be ready.

`triggerSlots` uses stable `sourceWorkflowId` and `sourceBlockId` identities and includes `ownPath`, `adoptablePaths`, and `defaultAdoptPath`:

* A slot with `ownPath` preserves that URL and accepts no override.
* An arriving trigger can adopt an offered retiring path, or request a new URL with `adoptPath:null`.
* Unknown identities, duplicate choices, or paths outside the slot's candidates are rejected. Candidates stay within the same target workflow and provider.

Include the same `triggerMappings` choices on preview and apply:

```json
[
  { "sourceWorkflowId": "source-workflow", "sourceBlockId": "source-trigger", "adoptPath": "retiring-trigger-path" }
]
```

### Apply the sync [#apply-the-sync]

Choose a stable `SYNC_REQUEST_ID` and preserve the exact reviewed request:

```sh
jq --arg requestId "$SYNC_REQUEST_ID" \
  --arg fingerprint "$(jq -er '.data.previewFingerprint' sync-preview.json)" \
  '. + {requestId: $requestId, previewFingerprint: $fingerprint, confirm: true}' \
  sync-request.json > sync-apply.json

curl -sS --fail-with-body -H "X-API-Key: $SIM_API_KEY" \
  -H 'Content-Type: application/json' --data @sync-apply.json \
  "$BASE_URL/api/v2/workspaces/$CURRENT_WORKSPACE/fork/pull" > sync-result.json
```

For push, use `/fork/push/preview` and `/fork/push` with the same direction-relative rules. `confirm:true` acknowledges target replacement. Changes to mappings or trigger choices require another preview before apply.

## Verify operations and recover [#receipts-polling-and-retries]

Apply returns a single-resource envelope. A completed sync report can look like:

```json
{
  "data": {
    "operationId": "operation-id",
    "requestId": "release-2026-10-13",
    "workspaceId": "current-workspace",
    "kind": "workspace_pull",
    "applied": true,
    "status": "completed",
    "resourceIds": ["destination-workflow"],
    "issues": [],
    "deployments": [
      {
        "operationId": "deployment-id",
        "workflowId": "destination-workflow",
        "version": 2,
        "status": "active",
        "ready": true,
        "pendingComponents": []
      }
    ]
  }
}
```

Poll under the **returned** `workspaceId`:

| Mutation | Receipt workspace |
| - | - |
| Import | Destination |
| Fork creation | Source workspace on which `/fork` ran |
| Push/pull | Current workspace in the request URL, including a push that changes the other workspace |

```sh
OPERATION_ID=$(jq -er '.data.operationId' sync-result.json)
OPERATION_WORKSPACE=$(jq -er '.data.workspaceId' sync-result.json)

curl -sS --fail-with-body -H "X-API-Key: $SIM_API_KEY" \
  "$BASE_URL/api/v2/workspaces/$OPERATION_WORKSPACE/operations/$OPERATION_ID"
```

Poll with a delay while `status` is `processing`. Terminal outcomes are `completed`, `completed_with_warnings`, `requires_configuration`, and `failed`. Inspect `issues`, `copyProgress`, `deployments[].ready`, `pendingComponents`, and `triggerUrlChanges`.

`applied:true` stays true after a follow-up failure: the transaction committed. Completed imports and forks remain drafts. For sync, check the **exact admitted deployments** in the receipt before treating the destination as ready.

### Retry an uncertain response [#retry-an-uncertain-response]

Request IDs deduplicate within the receipt workspace. Retain the complete apply request and original ID. Identical authorized retries return the original operation before preview-freshness checks. A changed payload under the same ID, stale preview, or blocked apply returns HTTP `409` with the v2 error envelope; other invalid input can return `400`. Follow-up failures appear on a committed operation.

After an uncertain response, retry the identical request with its original ID, or query `GET /workspaces/{workspaceId}/operations?requestId=...`. Lists return `{ "data": [...], "nextCursor": ... }`; use operation get for refreshed copy/deployment status.

If a stale preview is refused before commit, obtain a new preview and use a new request ID for the revised request. Do not replace a lost-response request with a fresh ID just to retry; that can create another mutation.

## Rollback, unlink, and exclusions [#rollback-unlink-and-exclusions]

[Rollback](/api-reference/workspace-sync/rollbackWorkspaceFork) restores the latest inbound sync's prior deployed versions. It can archive sync-created workflows and restore archived targets, but does not promise to restore arbitrary target drafts or undo all resource copies. Inspect `pendingActivations`; another rollback attempt may be needed to finish activation.

[Unlink](/api-reference/workspace-sync/unlinkWorkspaceFork) removes the direct relationship and its mappings while retaining workspace content. [Exclusions](/api-reference/workspace-sync/updateWorkspaceForkExclusions) keep selected workflows out of later syncs.

## Related docs [#related-docs]

* [CLI workflow sync](/cli/workflow-sync)
* [Workspace forking](/platform/enterprise/forks)
* [Import preview](/api-reference/workspace-sync/previewWorkflowImport)
* [Operation status](/api-reference/workspace-sync/getWorkspaceOperation)
* [Workflow-sync repository harness](https://github.com/simstudioai/sim/tree/main/apps/sim/lib/workspaces/__integration__) for `bun run test:workflow-sync`, disposable PostgreSQL, fixture boundaries, and contributor prerequisites
