# Get a workflow

Returns one workflow with its trigger, its steps in canvas order, its current `revision`, and `redactedPaths` naming every credential the response replaced.

- Endpoint: `GET https://api.returning.ai/v1/workflow-builder/data-workflows/{workflowId}`
- Section: Workflow Builder / Workflows
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `workflowBuilder`
- Retries: Safe to retry: it writes nothing.
- Guide: generated from the published specification
- Last updated: 28 Oct 2026
- Web page: https://docs.returning.ai/api-reference/workflow-builder/get-a-workflow

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `workflowBuilder`

`Authorization: Bearer <API_KEY>` with `workflowBuilder`, for the community that owns the workflow. A workflow id from another community answers exactly like an id that does not exist, so a key cannot be used to discover another community's ids.

| Failure | Meaning | Recovery |
| --- | --- | --- |
| `401 AUTHENTICATION_REQUIRED` | No key, or an unknown key | Send a valid key. |
| `403 API_KEY_PERMISSION_DENIED` | No `workflowBuilder` | Grant the permission. |
| `404 WORKFLOW_NOT_FOUND` | Unknown id, or a workflow of another community | Use an id from this community's list. |

## Behaviour

It is the call that starts every edit: the revision it returns is the value to send as `If-Match` if you want a stale write refused.

It does not return run history, event ingests or in-clear credentials.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `workflowId` | `string` | Yes | Workflow identifier: 24 hexadecimal characters, as returned in `_id`. (`^[0-9a-fA-F]{24}$`) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key: `Bearer <API_KEY>`. |

### Watch for

- `revision` is 64 hexadecimal characters. Send it back bare; a quoted `ETag` value is also accepted, with or without the `W/` prefix.
- Credentials are replaced by the whole marker array `["__REDACTED__"]`, even where the stored value is a plain string, and every replaced location is listed in `redactedPaths`.
- A credential inside a raw request body template that an author typed in is returned as authored, exactly as the canvas shows it. Do not build a workflow whose secret only exists inside a body template.
- `trigger` is `null` for a workflow that has none. Such a workflow can be read, edited and deployed; it never runs until a trigger is set, and `POST /{workflowId}/validate` reports `TRIGGER_REQUIRED` as a warning.
- `status` is one of `draft`, `deployed`, `undeployed`. A deployed workflow accepts every edit, and the edit applies immediately: that is why `status` is worth reading before a write.
- Step `status` may be present from an earlier run. It is run state, not authored configuration, and it is not part of the revision.

### Example request

```bash
curl --request GET \
  --url 'https://api.returning.ai/v1/workflow-builder/data-workflows/<workflowId>' \
  --header 'Authorization: Bearer <API_KEY>'
```

### More examples

A workflow with one read-only step. The router case nests each branch's steps under the branch that owns them.

```json
{
  "data": {
    "_id": "65f1a2b3c4d5e6f708192a3b",
    "communityId": "65f1a2b3c4d5e6f708192a50",
    "name": "VIP bonus",
    "status": "draft",
    "outgoingWebhookUrl": null,
    "createdAt": "2026-10-24T09:15:00.000Z",
    "updatedAt": "2026-10-24T09:20:11.000Z",
    "trigger": {
      "_id": "65f1a2b3c4d5e6f708192a41",
      "name": "Trade bonus trigger",
      "type": "event-ingest",
      "status": "idle",
      "options": { "batchIdPath": "metadata.batch_id", "recordIdPaths": ["trade_id"] }
    },
    "nodes": [
      {
        "_id": "65f1a2b3c4d5e6f708192a3c",
        "name": "Read member XP",
        "type": "platform-action",
        "order": 1,
        "continueOnFailure": false,
        "retryOnFailure": false,
        "options": { "action": "get-user-fields", "selectUser": { "type": "email", "value": ["{{65f1a2b3c4d5e6f708192a41.email}}"] }, "fields": [{ "label": "Total XP", "field": "total_xp" }] }
      },
      {
        "_id": "65f1a2b3c4d5e6f708192a3e",
        "name": "XP over 100?",
        "type": "router",
        "order": 2,
        "continueOnFailure": false,
        "retryOnFailure": false,
        "options": {
          "executePaths": "first_matching",
          "branches": [
            { "branchName": "XP > 100", "branchType": "CONDITION", "workflowNodeIds": ["65f1a2b3c4d5e6f708192a3d"], "nestedNodes": [{ "_id": "65f1a2b3c4d5e6f708192a3d", "name": "Add 10 XP", "type": "platform-action", "order": 3, "continueOnFailure": false, "retryOnFailure": false, "options": { "action": "post-user-fields" } }] },
            { "branchName": "Otherwise", "branchType": "FALLBACK", "workflowNodeIds": ["65f1a2b3c4d5e6f708192a3f"], "nestedNodes": [] }
          ]
        }
      }
    ],
    "revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c",
    "redactedPaths": []
  }
}
```

## Response

HTTP `200` with the workflow in `data`, and the same `revision` in the `ETag` header.

- `nodes` is the main line in canvas order. A router carries its branches in `options.branches`, and each branch lists its member ids in `workflowNodeIds` with the steps themselves in `nestedNodes`.
- `revision` changes when an authored field changes, including a save made in the canvas. It does not change when a run updates a step's `status` or the trigger's run state, so a revision read before a run is still valid after it.
- This is the readback that proves every mutation: read the workflow, match the field you changed, and keep the new revision.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `data` | `object` | - | A workflow with its trigger, its steps in canvas order and its revision. |
| `data._id` | `string` | - | Workflow identifier. |
| `data.communityId` | `string` | - | Community the workflow belongs to. |
| `data.name` | `string` | - | Workflow name. |
| `data.status` | `string` | - | `draft`, `deployed` or `undeployed`. |
| `data.outgoingWebhookUrl` | `string` | - | Failure webhook URL, when one is set. (Nullable) |
| `data.createdAt` | `string` | - | Creation time. |
| `data.updatedAt` | `string` | - | Last authored change. |
| `data.trigger` | `object` | - | The trigger, or `null` when the workflow has none. (Nullable) |
| `data.trigger._id` | `string` | - | Trigger identifier. Use it as the source of a reference. |
| `data.trigger.name` | `string` | - | Display name. |
| `data.trigger.type` | `string` | - | Trigger type. |
| `data.trigger.status` | `string` | - | Trigger lifecycle state, for example `idle`. |
| `data.trigger.options` | `object` | - | Trigger options. A webhook key is shown only in the response that created it. (Nullable) |
| `data.nodes` | `object[]` | - | Main-line steps in canvas order, routers carrying their branches. |
| `data.nodes._id` | `string` | - | Step identifier. |
| `data.nodes.name` | `string` | - | Step name. |
| `data.nodes.type` | `string` | - | Step type. |
| `data.nodes.order` | `integer` | - | Position on the main line, from 1. |
| `data.nodes.continueOnFailure` | `boolean` | - | Whether a failure stops the run. |
| `data.nodes.retryOnFailure` | `boolean` | - | Whether the step is retried on failure. |
| `data.nodes.options` | `object` | - | Stored step options, with every credential replaced by `["__REDACTED__"]`. (Nullable) |
| `data.nodes.status` | `string` | - | Last run state of the step, when one exists. |
| `data.nodes.nestedNodes` | `object[]` | - | Present on a router: each branch carries its own `workflowNodeIds` and `nestedNodes`. |
| `data.revision` | `string` | - | 64 hexadecimal characters. Send it back as `If-Match`; it also arrives as the `ETag` header. |
| `data.redactedPaths` | `string[]` | - | Every location where a credential was replaced by `["__REDACTED__"]`. |

### Example response (200)

```json
{
  "data": {
    "_id": "65f1a2b3c4d5e6f708192a3b",
    "communityId": "65f1a2b3c4d5e6f708192a50",
    "name": "VIP bonus",
    "status": "draft",
    "outgoingWebhookUrl": null,
    "createdAt": "2026-10-24T09:15:00.000Z",
    "updatedAt": "2026-10-24T09:20:11.000Z",
    "trigger": {
      "_id": "65f1a2b3c4d5e6f708192a41",
      "name": "Trade bonus trigger",
      "type": "webhook",
      "status": "idle",
      "options": {
        "tokenExchangeEnabled": false,
        "apiKey": {
          "enabled": false
        },
        "aesKey": {
          "enabled": false
        }
      }
    },
    "nodes": [
      {
        "_id": "65f1a2b3c4d5e6f708192a3c",
        "name": "Read member XP",
        "type": "platform-action",
        "order": 1,
        "continueOnFailure": false,
        "retryOnFailure": false,
        "options": {
          "action": "get-user-fields",
          "selectUser": {
            "type": "email",
            "value": ["{{65f1a2b3c4d5e6f708192a41.email}}"]
          },
          "fields": [
            {
              "label": "Total XP",
              "field": "total_xp"
            }
          ]
        }
      }
    ],
    "revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c",
    "redactedPaths": []
  }
}
```

## Errors

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | `VALIDATION_FAILED` | The id is not 24 hexadecimal characters. Use the id exactly as returned. |
| 401 | `AUTHENTICATION_REQUIRED` | No key, or an unknown key. Send a valid key. |
| 401 | `API_KEY_REQUIRED` | A valid admin session was used, because this API accepts community API keys only. Use a community API key. |
| 403 | `API_KEY_PERMISSION_DENIED` | No `workflowBuilder`. Grant the permission. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 404 | `WORKFLOW_NOT_FOUND` | Unknown id, or a workflow of another community. Use an id from this community. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 429 | `RATE_LIMITED` | The community's API rate limit was reached. Wait for the window to reset, then retry. |
| 500 | `INTERNAL_ERROR` | Unexpected failure. Retry with bounded backoff. |

**Retries:** Safe to retry: it writes nothing. The revision is stable between authored changes, so a retry returns the same value unless someone edited the workflow in between. If a later write answers `409 VERSION_CONFLICT`, re-read here and rebuild the change against the new revision.

## Next step

- [Rename a workflow or set its failure webhook](https://docs.returning.ai/api-reference/workflow-builder/rename-a-workflow-or-set-its-failure-webhook.md): `PATCH /v1/workflow-builder/data-workflows/{workflowId}`.
