> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Working with request and response data

> Move values into an executor node's request and out of its response. Declare the mappings, reshape values in flight, and check the assembled request before you call the service.

An executor node sends data to an external service and receives data back. The service names its own fields, and your workflow names its own. Mappings are how you move values between the two: a list of source-to-target entries on the node, applied to the request before the call and to the response after it.

You need this whenever the shape your workflow carries is not the shape the service accepts — a document that must travel without punctuation, an amount that belongs under a nested object, a score that a later node reads under a short name.

## Before you start

***

* A provider configuration for the service, and an executor node that references it. See [Reference the provider configuration from a workflow node](/en/flowker/integration-guide#step-3-reference-the-provider-configuration-from-a-workflow-node).
* The field names the service expects. When the service's OpenAPI document is in the registry, [Derive an operation schema](/en/reference/flowker/derive-openapi-operation-schema) returns `inputSchema` — the operation's request body — and `outputSchema` — its success response. Both give you the field names you write as mapping targets and sources.
* A workflow in `draft` status. An active workflow is locked, so use [Move workflow to draft](/en/reference/flowker/move-workflow-to-draft) before you edit a node, then activate it again.

<Note>
  Do not send `executorId` on a node that calls an operation of an uploaded OpenAPI document. Such a node names the operation with `operation_path` and `operation_method`. Flowker fills the `executorId` in for you, from the provider configuration the node points at, before it validates the workflow. It does this when you create the workflow and when you update it. [Connecting your own API](/en/flowker/connecting-your-own-api) walks that whole path. The nodes on this page name `http`, the generic HTTP connector, which does need an explicit `executorId`.
</Note>

## Step 1: Know what a mapping can read

***

Every mapping reads from the workflow context — one JSON object that grows as the execution advances:

| Path                  | What it holds                                                                                                                                                                                                                                        |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `workflow`            | The trigger payload: the `inputData` of an execution request, or the body a webhook route received. A webhook route also adds `workflow._webhook` with the call's metadata — see [Webhook metadata](/en/flowker/integration-guide#webhook-metadata). |
| `execution.id`        | The execution identifier.                                                                                                                                                                                                                            |
| `execution.startedAt` | When the execution started, in UTC.                                                                                                                                                                                                                  |
| `<nodeId>`            | The output of each node that has completed, under that node's id.                                                                                                                                                                                    |

Address a value by its path from one of those top-level keys:

| Source                         | What it selects                                   |
| ------------------------------ | ------------------------------------------------- |
| `workflow.customer.document`   | A nested field of the trigger payload.            |
| `workflow.items[0].sku`        | One element of an array.                          |
| `workflow.items[*].sku`        | The same field across every element, as an array. |
| `workflow`                     | The whole trigger payload as an object.           |
| `score-transaction.body.score` | A field of the `score-transaction` node's output. |

<Note>
  A mapping `source` is a plain path. Do not wrap it in `${...}` — braces belong to the node's template fields (`body`, `headers`, `query`, `path`), and inside a mapping a `${...}` string is read as a literal path name that selects nothing.
</Note>

## Step 2: Declare the input mapping

***

Input mappings live in an `inputMapping` array inside the executor node's `data` object. Each entry moves one value into the outgoing request body.

| Field            | Type    | Required | Description                                                                                                                                    |
| ---------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `source`         | string  | Yes      | The path in the workflow context to read.                                                                                                      |
| `target`         | string  | Yes      | The path in the outgoing request body to write. A dotted target creates the nested object — `payment.amount` sends `{"payment":{"amount":…}}`. |
| `transformation` | object  | No       | A change applied to the value after it lands on the target. See [Step 4](#step-4-reshape-a-value-on-its-way-through).                          |
| `required`       | boolean | No       | Whether the source path must exist. Defaults to `false`. Applies to the whole node — see below.                                                |

The mapped result **is** the request body. Write each `target` exactly as the service expects to receive it: there is no wrapper object and no prefix to add.

<Accordion title="Example — map the trigger payload into a fraud check">
  ```json theme={null}
  {
    "id": "score-transaction",
    "type": "executor",
    "name": "Score transaction",
    "position": { "x": 200, "y": 0 },
    "data": {
      "executorId": "http",
      "providerConfigId": "019c96a0-0ac0-7de9-9f53-9cf842a2ee5a",
      "method": "POST",
      "path": "/score-transaction",
      "inputMapping": [
        { "source": "workflow.transactionId", "target": "reference" },
        { "source": "workflow.amount", "target": "payment.amount" },
        { "source": "workflow.customer.document", "target": "payment.document" }
      ]
    }
  }
  ```

  With a trigger payload of `{"transactionId":"txn-98765","amount":1500.00,"customer":{"document":"12345678900"}}`, the service receives:

  ```json theme={null}
  {
    "reference": "txn-98765",
    "payment": { "amount": 1500.00, "document": "12345678900" }
  }
  ```
</Accordion>

### When a source selects nothing

A source path that is absent from the context is not an error. The target is still written, with the value `null`, and the request goes out.

Set `required: true` when the node must not call the service without a value. Flowker then checks every source path on that node before it builds the request, and fails the step when any of them is absent — the execution stops with `FLK-0504` and the step reports `input transformation failed`.

<Note>
  `required` applies to the node, not to the one entry that carries it. If any entry in a node's `inputMapping` sets `required: true`, every source path in that array must resolve. To keep some fields optional, leave `required` off across the whole node.
</Note>

Give each `target` one entry. When two entries write the same target, the last one wins.

## Step 3: Decide what builds the request body

***

A node has three sources for a request body. Only `data.body` is exclusive: when present, it is the whole body. Without it, Flowker composes the body from the other two sources, with the mapping overlay written over the `config` literals:

<Steps>
  <Step title="data.body">
    An explicit body template wins outright. Flowker resolves its `${...}` references against the workflow context and sends the result. While `data.body` is present, `inputMapping`, `transforms`, and `config` contribute nothing to the body.

    Every `${...}` reference here must resolve. One that does not fails the node with `FLK-0143`, before any call is made — the opposite of a mapping source, which resolves to `null`. Use `data.body` when a missing value must stop the workflow, and a mapping when the request must go out regardless.
  </Step>

  <Step title="inputMapping or transforms">
    Otherwise Flowker builds an overlay from `inputMapping`. When `inputMapping` is empty it builds the overlay from `transforms` instead. The two are mutually exclusive: a node with at least one `inputMapping` entry never runs its `transforms` on the input.
  </Step>

  <Step title="config literals">
    Literal values in `data.config` seed the body. With an overlay present, the literals are the base and the overlay wins on any key both set — so one node can combine fixed values with mapped ones. With no overlay, the literals are the body on their own.
  </Step>
</Steps>

Transport never becomes body content. Before `config` can seed the body, Flowker removes these names from it: `method`, `path`, `url`, `endpointName`, `query`, `headers`, `auth`, `retry`, `timeout`, `timeout_seconds`, `request_format`, `success_status_codes`, `allowedHosts`, and `allowedPrivateHosts`. A node that keeps transport in `config` by mistake therefore sends none of it to the destination, and an `auth` block placed there can never ship as request content.

The node reads its own transport from the top of its `data` object — `path`, `endpointName`, `method`, `headers`, `query`, `auth`, `timeout_seconds`, `retry`, `success_status_codes`, and `request_format`. The outbound host allow-lists are not among them: you set `allowedHosts` and `allowedPrivateHosts` on the provider configuration, where each applies to every node that calls through it.

<Accordion title="Example — fixed values plus mapped ones">
  ```json theme={null}
  {
    "data": {
      "executorId": "http",
      "providerConfigId": "019c96a0-0ac0-7de9-9f53-9cf842a2ee5a",
      "method": "POST",
      "path": "/score-transaction",
      "config": {
        "channel": "web",
        "payment": { "currency": "BRL" }
      },
      "inputMapping": [
        { "source": "workflow.transactionId", "target": "reference" },
        { "source": "workflow.amount", "target": "payment.amount" }
      ]
    }
  }
  ```

  The literals and the mapped values merge, and nesting merges with nesting:

  ```json theme={null}
  {
    "channel": "web",
    "reference": "txn-98765",
    "payment": { "currency": "BRL", "amount": 1500.00 }
  }
  ```
</Accordion>

## Step 4: Reshape a value on its way through

***

When the service needs a value in a different form, attach a `transformation` to the mapping entry. It applies to the value after it lands on the target.

| Type                | What it does                                | Config                                   |
| ------------------- | ------------------------------------------- | ---------------------------------------- |
| `remove_characters` | Removes the given characters from the text. | `characters` — the characters to remove. |
| `add_prefix`        | Puts text in front of the value.            | `prefix` — the text to add.              |
| `add_suffix`        | Puts text after the value.                  | `suffix` — the text to add.              |
| `to_uppercase`      | Converts the text to uppercase.             | —                                        |
| `to_lowercase`      | Converts the text to lowercase.             | —                                        |

These five are the whole set. A `type` outside it is rejected when you save the workflow, with `FLK-0140`.

Two rules to write them by:

* They act on text. A value that is not text reaches the target unchanged.
* `prefix` and `suffix` each need at least one character, and a single space counts. `characters` needs at least one character that is not a space, a tab, or a line break. A value that does not meet this fails the step at run time with `FLK-0504`.

<Accordion title="Example — normalize a document and stamp a reference">
  ```json theme={null}
  {
    "inputMapping": [
      {
        "source": "workflow.customer.document",
        "target": "payer.document",
        "transformation": {
          "type": "remove_characters",
          "config": { "characters": ".-/" }
        }
      },
      {
        "source": "workflow.customer.name",
        "target": "payer.name",
        "transformation": { "type": "to_uppercase" }
      },
      {
        "source": "workflow.transactionId",
        "target": "payer.reference",
        "transformation": {
          "type": "add_prefix",
          "config": { "prefix": "BR-" }
        }
      }
    ]
  }
  ```

  From `{"customer":{"document":"123.456.789-00","name":"ada lovelace"},"transactionId":"txn-98765"}`, the node sends:

  ```json theme={null}
  {
    "payer": {
      "document": "12345678900",
      "name": "ADA LOVELACE",
      "reference": "BR-txn-98765"
    }
  }
  ```
</Accordion>

### Whole-document transforms

For work that entry-by-entry mapping does not express — combining two fields, choosing the first value that is present, filling a default — declare a `transforms` array instead. Each operation reads the whole workflow context and writes the whole overlay.

Flowker accepts `shift` (move or rename), `concat` (join values), `coalesce` (first value present), `default` (fill a missing key), `extract` (lift a subtree to the root), `delete` (drop a key), `timestamp`, `uuid`, and `pass`. The five transformation types above are available here too; as operations they take the target path in the spec as `path`.

<Accordion title="Example — shift and a default in one node">
  ```json theme={null}
  {
    "data": {
      "executorId": "http",
      "providerConfigId": "019c96a0-0ac0-7de9-9f53-9cf842a2ee5a",
      "transforms": [
        {
          "operation": "shift",
          "spec": {
            "reference": "workflow.transactionId",
            "payment.amount": "workflow.amount"
          }
        },
        { "operation": "default", "spec": { "channel": "web" } }
      ]
    }
  }
  ```

  The node sends:

  ```json theme={null}
  {
    "reference": "txn-98765",
    "payment": { "amount": 1500.00 },
    "channel": "web"
  }
  ```

  An operation may set `require: true` to demand that every path its `spec` names exists, the same way `required` works on a mapping entry.
</Accordion>

<Note>
  `transforms` runs only when the node has no `inputMapping`. Use one or the other in a given node, never both.
</Note>

## Step 5: Read the response back out

***

Output mappings extract fields from the response and store them in the workflow context under the node's id, so later nodes read short, stable names. Declare them in an `outputMapping` array in the node's `data`, with the same four entry fields as an input mapping.

An output `source` is a path into the response envelope, not into the response body:

| Path               | What it holds                                                                                                                                                                   |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`           | The HTTP status code.                                                                                                                                                           |
| `status_text`      | The HTTP status line, such as `200 OK`.                                                                                                                                         |
| `url`              | The URL Flowker requested, with the query string it assembled.                                                                                                                  |
| `headers`          | The response headers, as a key-value object under their canonical names, such as `Content-Type`. A header the service sent more than once arrives as one comma-separated value. |
| `body`             | The response body, parsed when the response is `application/json` or an XML type — `application/xml`, `text/xml`, or a type whose name ends in `+xml`.                          |
| `raw_body`         | The response exactly as it arrived, as text. Present for an XML response.                                                                                                       |
| `body_format`      | Set to `xml` when an XML body was decoded.                                                                                                                                      |
| `body_parse_error` | Present in place of `body` when an XML body could not be decoded, so the workflow can branch on it.                                                                             |

Response fields therefore sit under `body`:

```json theme={null}
{
  "outputMapping": [
    { "source": "body.score", "target": "score" },
    { "source": "body.decision", "target": "decision" },
    { "source": "status", "target": "httpStatus" }
  ]
}
```

On a node with the id `score-transaction`, that stores:

```json theme={null}
{ "score": 42, "decision": "review", "httpStatus": 200 }
```

Downstream nodes then read `${score-transaction.score}` and `${score-transaction.httpStatus}`.

A node that declares no `outputMapping` stores the whole envelope under its id instead, and downstream nodes read the envelope path directly — `${score-transaction.body.score}`. Add an output mapping when you want the shorter name; skip it when the envelope path is clear enough.

An output source that selects nothing behaves like an input one: the target is stored as `null` unless an entry on that node sets `required: true`.

## Step 6: Check the mapping before you call the service

***

[Preview an executor request](/en/reference/flowker/preview-executor-request) assembles the request a node would send and returns it to you. It runs the same mapping, transformation, and authentication assembly an execution runs, and it never opens a connection to the service — so you can iterate on a mapping without a single call leaving your deployment. No credential appears anywhere in what it returns, including the `curl`.

Send the node, the provider configuration it targets, and a sample payload. The provider configuration goes in the request itself, so the preview depends on nothing but what you send:

```json theme={null}
POST /v1/workflows/preview-request

{
  "node": {
    "executorId": "http",
    "method": "POST",
    "path": "/score-transaction",
    "inputMapping": [
      {
        "source": "workflow.customer.document",
        "target": "payer.document",
        "transformation": {
          "type": "remove_characters",
          "config": { "characters": ".-/" }
        }
      },
      {
        "source": "workflow.customer.name",
        "target": "payer.name",
        "transformation": { "type": "to_uppercase" }
      },
      {
        "source": "workflow.transactionId",
        "target": "payer.reference",
        "transformation": {
          "type": "add_prefix",
          "config": { "prefix": "BR-" }
        }
      }
    ]
  },
  "providerConfig": {
    "providerId": "http",
    "config": { "base_url": "https://api.fraudshield.example.com" },
    "allowedHosts": ["api.fraudshield.example.com"]
  },
  "sampleInput": {
    "transactionId": "txn-98765",
    "customer": { "document": "123.456.789-00", "name": "ada lovelace" }
  }
}
```

Your `sampleInput` becomes the trigger payload, so the mapping sources read it as `workflow.*` — exactly as they will at run time.

The response is the assembled request:

```json theme={null}
{
  "method": "POST",
  "url": "https://api.fraudshield.example.com/score-transaction",
  "headers": { "Content-Type": "application/json" },
  "body": "{\"payer\":{\"document\":\"12345678900\",\"name\":\"ADA LOVELACE\",\"reference\":\"BR-txn-98765\"}}",
  "curl": "curl -X POST -H 'Content-Type: application/json' --data '{\"payer\":{\"document\":\"12345678900\",\"name\":\"ADA LOVELACE\",\"reference\":\"BR-txn-98765\"}}' 'https://api.fraudshield.example.com/score-transaction'",
  "unresolved": []
}
```

Each transformation resolved: the punctuation is gone from the document, the name is uppercase, and the reference carries its prefix — and no call reached the service.

Read it in this order:

<Steps>
  <Step title="Check the URL">
    It is the provider configuration's base URL plus the node's `path`. A path you did not expect is a node field to fix, not a mapping.
  </Step>

  <Step title="Check the body against the field names the service expects">
    Every `target` should appear where the service wants it. A field carrying `null` is a `source` path that selects nothing.
  </Step>

  <Step title="Check `unresolved`">
    It lists the `${...}` references in the node's template fields that your sample payload did not resolve. They are left literal in the rendered request. An empty array means every reference found a value.
  </Step>
</Steps>

To check a node's fixed fields against the catalog executor's schema as well, call [Validate a node configuration](/en/reference/flowker/validate-executor-config) with the node's configuration in `config` and, in `mappedTargets`, the target paths your `inputMapping` supplies. Flowker counts those as satisfied, so a node that maps a required field from the trigger passes the check.

## What goes wrong

***

| Symptom                                                  | Cause                                                                                                     | Fix                                                                                                                       |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| The service receives a field set to `null`.              | The `source` path is absent from the workflow context.                                                    | Compare the path with a preview's rendered body. Set `required: true` on the node when it must not run without the value. |
| The service receives a nested object it did not ask for. | The `target` carries a prefix.                                                                            | Write the target exactly as the service expects it. A target of `executor.accountId` sends an `executor` object.          |
| The request body is empty.                               | The node has no `data.body`, no mapping and no `transforms`, and its `config` holds only transport names. | Add the values as `config` literals or as mapping entries.                                                                |
| The mappings appear to be ignored.                       | The node also carries `data.body`, which is the only body source while it is present.                     | Remove `data.body` to let the mappings build the body.                                                                    |
| `transforms` appears to be ignored.                      | The node has at least one `inputMapping` entry.                                                           | Remove the `inputMapping` entries, or move the logic into them.                                                           |
| A downstream node reads nothing.                         | The reference does not name the producing node.                                                           | Read `${<nodeId>.<target>}`. There is no shared top-level namespace.                                                      |
| An output mapping stores `null`.                         | The `source` omits the envelope prefix.                                                                   | Map `body.score`, not `score`.                                                                                            |

| Error code | When                        | What it means                                                                                                                                                                                                       |
| ---------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FLK-0140` | Create, update, or activate | The node's `inputMapping` does not form a valid specification — most often a `transformation.type` outside the five supported types.                                                                                |
| `FLK-0141` | Create, update, or activate | The same, for the node's `outputMapping`.                                                                                                                                                                           |
| `FLK-0142` | Create, update, or activate | The same, for the node's `transforms`.                                                                                                                                                                              |
| `FLK-0143` | Run time                    | A `${...}` reference in the node's `data.body` does not resolve against the workflow context. The node fails without calling the service.                                                                           |
| `FLK-0504` | Run time                    | The node failed. The step message names the stage: `input transformation failed` for a required source that is absent or a transformation that could not run, `output transformation failed` for the response side. |

See the [Flowker error list](/en/reference/flowker/flowker-error-list) for every code.

## What's next

***

<CardGroup cols={2}>
  <Card title="Integration guide" icon="plug" href="/en/flowker/integration-guide">
    Create the provider configuration a node calls through, and set its authentication.
  </Card>

  <Card title="Configuring a webhook trigger" icon="webhook" href="/en/flowker/configuring-a-webhook-trigger">
    Choose the payload contract that fills the `workflow` namespace your mappings read.
  </Card>
</CardGroup>
