> ## 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.

# Integration guide

> Connect external services to Flowker through provider configurations. Set up authentication, map fields, and run workflows against real integrations.

Flowker calls external services (such as fraud engines, payment processors, and KYC providers) through provider configurations. A provider configuration is your connection to one live instance of an external service.

In this guide, you explore the catalog, create a provider configuration, and reference it from a workflow node. You then map fields between your data and the service, and learn how Flowker retries and protects those calls.

## Step 1: Explore the catalog

***

The catalog is a read-only registry of the providers, catalog executors, and triggers that ship with Flowker. You discover them. You never create them.

<Steps>
  <Step title="List available providers">
    Call the [List catalog providers](/en/reference/products/flowker/list-catalog-providers) endpoint to see the service types Flowker connects to. The catalog always includes the generic HTTP connector. Native providers such as `ledger` (Midaz) and `tracer` are synthesized from published OpenAPI specifications and appear only when the native schema registry is configured and synthesis succeeds.
  </Step>

  <Step title="List available catalog executors">
    Call the [List catalog executors](/en/reference/products/flowker/list-catalog-executors) endpoint to see the operations a workflow node can invoke. Use [List executors by provider](/en/reference/products/flowker/list-executors-by-provider) to narrow the list to one provider.
  </Step>

  <Step title="List available triggers">
    Call the [List catalog triggers](/en/reference/products/flowker/list-catalog-triggers) endpoint to see the built-in trigger types: webhooks and schedules. The Execute workflow API starts a workflow but is not a catalog trigger.
  </Step>

  <Step title="Pick what you need">
    Note the `providerId` and the catalog executor id that match your integration. You use the first in [Step 2](#step-2-create-a-provider-configuration) and the second in [Step 3](#step-3-reference-the-provider-configuration-from-a-workflow-node).
  </Step>
</Steps>

<Tip>
  Think of the catalog as a menu: it shows what Flowker can call. Provider configurations are your specific orders: the base URL, the credentials, and the settings for each service instance you use.
</Tip>

## Step 2: Create a provider configuration

***

Call [`POST /v1/provider-configurations`](/en/reference/products/flowker/create-provider-configuration) to define your connection to one instance of an external service.

| Field                 | Required      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| --------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                | Yes           | A name for this connection, 1–100 characters.                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `kind`                | No            | Which kind of connection this is. Omit it, or send `catalog`, for a connection to a catalog provider — the case this guide covers. Send `external_openapi` for a connection to an OpenAPI document you uploaded; see [Connecting your own API](/en/products/flowker/connecting-your-own-api). You choose the kind when you create the configuration.                                                                                                                                               |
| `providerId`          | For `catalog` | The catalog provider this connection is an instance of, such as `ledger` or `http`. A configuration of kind `external_openapi` may omit it, and a read of it returns the reserved id `external.openapi`.                                                                                                                                                                                                                                                                                           |
| `config`              | Yes           | The connection details for that instance, such as the base URL and the authentication credentials. Flowker validates this map against the provider's JSON Schema from the catalog and returns `422` when it does not match. The secret inside the `auth` block is held in your secrets backend, not in the configuration document; everything else in the map is stored with the configuration.                                                                                                    |
| `allowedHosts`        | For `http`    | The public hosts this configuration is allowed to call. The generic HTTP connector (`providerId: "http"`) requires at least one entry, and an empty list is rejected with `FLK-0323`. Native providers accept an empty list. An entry with a leading dot matches subdomains — `.kyc-provider.io` matches `api.kyc-provider.io`. Host names only: no IP literals, wildcards, or ports. The host in `config.base_url` must be covered by the list, otherwise the create is rejected with `FLK-0320`. |
| `allowedPrivateHosts` | No            | Named private hosts your operations team allows this configuration to reach. Cloud metadata and link-local addresses stay blocked.                                                                                                                                                                                                                                                                                                                                                                 |
| `schemaBindings`      | No            | The XSD or OpenAPI schemas bound to this configuration, each with an optional restriction to specific OpenAPI operations.                                                                                                                                                                                                                                                                                                                                                                          |
| `description`         | No            | Free text, up to 500 characters.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `metadata`            | No            | Your own key-value pairs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |

<Note>
  A `providerId` is a catalog identifier, and it does not always match the product name. The catalog registers Midaz as `ledger`. Always take the value from [List catalog providers](/en/reference/products/flowker/list-catalog-providers) rather than guessing it from the product name.
</Note>

<Warning>
  The `providerId` on the configuration and the `executorId` on the node that uses it must belong to the same catalog provider. The generic HTTP connector uses `http` for both. Flowker rejects a workflow that pairs a configuration of one provider with an executor of another, with `FLK-0151`.
</Warning>

The example below builds the connection this guide uses from here on: a fraud scoring service reached through the generic HTTP connector.

<Accordion title="Example request">
  ```json theme={null}
  POST /v1/provider-configurations

  {
    "name": "FraudShield Production",
    "description": "Production fraud scoring service",
    "providerId": "http",
    "config": {
      "base_url": "https://api.fraudshield.example.com",
      "auth": {
        "type": "api_key",
        "config": {
          "key": "sk-prod-xxx",
          "header_name": "X-API-Key",
          "location": "header"
        }
      }
    },
    "allowedHosts": ["api.fraudshield.example.com"],
    "metadata": {
      "environment": "production"
    }
  }
  ```

  The response returns the new configuration's `id`. Keep it. [Step 3](#step-3-reference-the-provider-configuration-from-a-workflow-node) and [Step 4](#step-4-run-the-workflow) put it in the `providerConfigId` of the node that calls the service.
</Accordion>

### Authentication

The `config.auth` block holds the authentication the external service requires, as a `{ type, config }` pair. Use the method your service expects.

| Type                      | Description                                                                | Config fields                                                                                              |
| ------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `none`                    | No authentication.                                                         | —                                                                                                          |
| `api_key`                 | API key in header or query.                                                | `key`, `header_name`, `location`, `query_param_name`, `prefix`                                             |
| `bearer`                  | Bearer token in Authorization header.                                      | `token`                                                                                                    |
| `basic`                   | Username and password (Base64).                                            | `username`, `password`                                                                                     |
| `oidc_client_credentials` | OAuth 2.0 client credentials flow with automatic token management.         | `issuer_url`, `client_id`, `client_secret`, `scopes`                                                       |
| `oidc_user`               | OAuth 2.0 resource owner password flow.                                    | `issuer_url`, `client_id`, `username`, `password`, `client_secret`, `scopes`                               |
| `oauth2_token_endpoint`   | OAuth 2.0 client credentials against a token endpoint (no OIDC discovery). | `token_url`, `client_id`, `client_secret`, `scopes`                                                        |
| `hmac`                    | Signs each request with a shared HMAC secret.                              | `secret`, `algorithm`, `encoding`, `header_name`, `signature_prefix`, `signing_string`, `timestamp_header` |

Flowker stores secret leaves in `config.auth` outside the persisted configuration document. An authorized provider-configuration read can resolve those values from the vault and return them in clear. Unresolved leaves remain masked. Grant read access accordingly.

Anything else you place in the configuration document (a header, for example) stays with the configuration, and a read can return it. Put each credential in `config.auth`.

To rotate a secret, send the new value in an update. To keep the current one, omit the field or send it blank. This works while `auth.type` stays the same. An update that changes `auth.type` must carry a value for each secret the new type requires and the previous one did not. Otherwise Flowker rejects it with `FLK-0952`. A change between two types that use the same secret, such as `oidc_user` to `oidc_client_credentials`, does not need that value again.

<Tip>
  For OAuth 2.0 integrations, use `oidc_client_credentials`. Flowker handles token acquisition and renewal automatically.
</Tip>

<Accordion title="Example: OIDC client credentials">
  ```json theme={null}
  {
    "auth": {
      "type": "oidc_client_credentials",
      "config": {
        "issuer_url": "https://auth.fraudshield.com/realms/fraudshield",
        "client_id": "flowker-integration",
        "client_secret": "secret-value",
        "scopes": ["transactions:read", "transactions:score"]
      }
    }
  }
  ```
</Accordion>

### Enabling and disabling

Provider configurations have two statuses: `active` (in use) and `disabled` (temporarily offline). A new provider configuration starts in `active` status. Use [Disable provider configuration](/en/reference/products/flowker/disable-provider-configuration) to take a connection out of service and [Enable provider configuration](/en/reference/products/flowker/enable-provider-configuration) to bring it back.

See the [Provider configurations API](/en/reference/products/flowker/list-provider-configurations) for the full reference.

## Step 3: Reference the provider configuration from a workflow node

***

Every executor node carries a `providerConfigId`, the identifier of the provider configuration it calls through. Flowker rejects a workflow whose executor node has no `providerConfigId`, and rejects a value that is not a UUID. At run time it builds each outgoing request from the base URL of that provider configuration plus the path on the node. The node fails if the provider configuration is not `active`.

These are the fields an executor node sets in its `data` object when it calls through the generic HTTP connector:

| Field                                              | Required | Description                                                                                                                                                                                                                              |
| -------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `executorId`                                       | Yes      | The catalog executor this node invokes, taken from [Step 1](#step-1-explore-the-catalog). Flowker rejects the workflow when the id is not in the catalog. A node that calls an uploaded OpenAPI document omits it — see below.           |
| `providerConfigId`                                 | Yes      | The UUID of the provider configuration this node calls through.                                                                                                                                                                          |
| `path`                                             | No       | The request path appended to the provider configuration's base URL. The destination host is always that base URL — a node cannot supply an absolute URL.                                                                                 |
| `endpointName`                                     | No       | The same request segment by name, used when the node sets no `path`. A node that carries both sends `path`.                                                                                                                              |
| `method`                                           | No       | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, or `OPTIONS`. Defaults to `POST`.                                                                                                                                                       |
| `headers`                                          | No       | Request headers, merged over the ones the provider configuration defines. A node header wins over a provider-configuration header of the same name.                                                                                      |
| `query`                                            | No       | Query parameters appended to the URL.                                                                                                                                                                                                    |
| `auth`                                             | No       | A `{type, config}` authentication block for this node, in the same shape the provider configuration uses. When present it takes precedence over the authentication on the provider configuration.                                        |
| `body`                                             | No       | An explicit request body, resolved against the workflow context. When set, it is the only body source — field mappings are not applied to it.                                                                                            |
| `content_digest`                                   | No       | A SHA-256 pin for the assembled request body. Set a 64-character hexadecimal digest, or a value resolved from the workflow context. Flowker hashes the final bytes after assembling the body and sends the request only when they match. |
| `config`                                           | No       | Fixed literal values that seed the request body. See [Field mapping and data transformation](#field-mapping-and-data-transformation).                                                                                                    |
| `inputMapping`, `outputMapping`, `transforms`      | No       | Field mappings and transformations. See [Field mapping and data transformation](#field-mapping-and-data-transformation).                                                                                                                 |
| `timeout_seconds`, `retry`, `success_status_codes` | No       | Per-node resilience settings. See [Retry and circuit breaker](#retry-and-circuit-breaker).                                                                                                                                               |
| `request_format`                                   | No       | How Flowker serializes the request body: `json` (the default), `xml_converted`, or `xml_passthrough`. `xml_converted` also requires `root_element`.                                                                                      |

A node that calls an operation of an uploaded OpenAPI document names it with `operation_path` and `operation_method` instead of an `executorId`. Flowker fills the `executorId` in for you from the provider configuration the node points at. [Connecting your own API](/en/products/flowker/connecting-your-own-api) walks that whole path.

### Validate a node configuration before you save

Call the [Validate a node configuration](/en/reference/products/flowker/validate-executor-config) endpoint (`POST /v1/catalog/executors/{id}/validate`) to check a node's configuration against the catalog executor's JSON Schema.

This performs **JSON Schema validation only**. It checks that your configuration object matches the structure the catalog executor expects (required fields, types, formats). It does not call the external service, so the first real round trip happens when a workflow runs the node.

Pass `mappedTargets` to name the fields your node supplies through an `inputMapping` rather than a fixed value. Those fields count as satisfied, so a node that maps a required field from the trigger validates before you save it.

## Field mapping and data transformation

***

Use field mappings and transformations when workflow data doesn't match the format an external service expects. Use them too when a service returns data in a shape the next step can't consume.

You define field mappings and transformations inside the `data` object of executor nodes. Flowker applies input mappings before calling the external service, and output mappings after receiving the response.

An input `target` is a path in the outgoing request body, written exactly as the external service expects it. There is no wrapper object and no prefix to add. An output `source` is a path into the response envelope, so response fields sit under `body`.

<Accordion title="Quick example: mapping workflow fields to an executor node">
  ```json theme={null}
  {
    "id": "executor-balance",
    "type": "executor",
    "name": "Check Balance",
    "data": {
      "executorId": "http",
      "providerConfigId": "a1b2c3d4-e5f6-4789-a012-345678901234",
      "path": "/accounts/balance",
      "inputMapping": [
        { "source": "workflow.customerId", "target": "accountId" },
        { "source": "workflow.amount", "target": "minimumBalance" }
      ],
      "outputMapping": [
        { "source": "body.currentBalance", "target": "balance" },
        { "source": "body.accountStatus", "target": "status" }
      ]
    }
  }
  ```

  Downstream nodes read the mapped output under this node's ID: `${executor-balance.balance}`.
</Accordion>

For complex integrations, you can also attach transformations to individual mapping entries (e.g., stripping characters, adding prefixes, changing case). You can define Kazaam operations for advanced JSON-to-JSON transformations.

[Working with request and response data](/en/products/flowker/working-with-request-and-response-data) walks the whole path. It covers declaring the mappings, choosing what builds the request body, and reshaping values in flight. It also covers reading the response back out, and checking the assembled request before you call the service.

## Step 4: Run the workflow

***

Reference the provider configuration in a workflow node of type `executor`.

The example below creates a payment validation workflow on top of the FraudShield connection from [Step 2](#step-2-create-a-provider-configuration). When a payment arrives, Flowker calls the fraud check service, evaluates the risk score, and either approves or rejects the payment based on the result.

The workflow has five nodes. A webhook **trigger** receives the payment, and an **executor** node calls the fraud check service. A **conditional** node evaluates the score, and there are two **action** nodes for the approve and reject outcomes. Edges connect them in sequence, with the conditional node branching to either path based on the score threshold.

Use the [Create workflow](/en/reference/products/flowker/create-workflow) endpoint to define the workflow, then [Activate](/en/reference/products/flowker/activate-workflow) it, and finally [Execute](/en/reference/products/flowker/execute-workflow) it.

<AccordionGroup>
  <Accordion title="Example: Create a payment validation workflow">
    ```json theme={null}
    POST /v1/workflows

    {
      "name": "payment-validation",
      "description": "Validates a payment before processing.",
      "nodes": [
        {
          "id": "trigger-payment",
          "type": "trigger",
          "name": "Payment received",
          "position": { "x": 0, "y": 0 },
          "data": {
            "triggerType": "webhook",
            "path": "payments/received",
            "method": "POST",
            "input_contract": "open",
            "format": "json"
          }
        },
        {
          "id": "check-fraud",
          "type": "executor",
          "name": "Fraud check",
          "position": { "x": 200, "y": 0 },
          "data": {
            "executorId": "http",
            "providerConfigId": "019c96a0-0ac0-7de9-9f53-9cf842a2ee5a",
            "path": "/score-transaction",
            "method": "POST"
          }
        },
        {
          "id": "evaluate-score",
          "type": "conditional",
          "name": "Score evaluation",
          "position": { "x": 400, "y": 0 },
          "data": {
            "condition": "check-fraud.body.score < 80"
          }
        },
        {
          "id": "approve",
          "type": "action",
          "name": "Approve payment",
          "position": { "x": 600, "y": -100 },
          "data": {
            "actionType": "set_output",
            "output": { "decision": "approved" }
          }
        },
        {
          "id": "reject",
          "type": "action",
          "name": "Reject payment",
          "position": { "x": 600, "y": 100 },
          "data": {
            "actionType": "set_output",
            "output": { "decision": "rejected" }
          }
        }
      ],
      "edges": [
        { "id": "e1", "source": "trigger-payment", "target": "check-fraud" },
        { "id": "e2", "source": "check-fraud", "target": "evaluate-score" },
        { "id": "e3", "source": "evaluate-score", "target": "approve", "sourceHandle": "true" },
        { "id": "e4", "source": "evaluate-score", "target": "reject", "sourceHandle": "false" }
      ]
    }
    ```

    The `check-fraud` node names `http`, the generic HTTP connector from the catalog. It also names the FraudShield configuration from Step 2, which holds the base URL and the credentials. Both sides name the same provider, so the workflow saves. Flowker sends the request to `https://api.fraudshield.example.com/score-transaction`.

    The node declares no `outputMapping`, so its output keeps the response envelope shape. The score therefore sits at `check-fraud.body.score`, which is what the `evaluate-score` condition reads. Add an `outputMapping` when you prefer a flatter name. See [Field mapping and data transformation](#field-mapping-and-data-transformation).
  </Accordion>

  <Accordion title="Example: Execute the workflow">
    ```json theme={null}
    POST /v1/workflows/{workflowId}/executions
    Idempotency-Key: {unique-uuid}

    {
      "inputData": {
        "transactionId": "txn-98765",
        "amount": 1500.00,
        "currency": "BRL",
        "customerId": "cust-12345"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Triggering workflows

***

You trigger workflow executions via the [Execute workflow](/en/reference/products/flowker/execute-workflow) endpoint:

```
POST /v1/workflows/:workflowId/executions
```

The request body contains the `inputData` for the execution. All fields are available to subsequent nodes via the `workflow` namespace (for example, `workflow.transactionId` or `workflow.amount`). Node outputs are available via the node's ID (for example, `check-fraud.body.score` for a node that declares no `outputMapping`).

### Idempotency

Every execution request must include an `Idempotency-Key` header. A request without it fails with `400 Bad Request` (error `FLK-0509`). Generate a fresh UUID for each new execution, and reuse the same key only when retrying the identical request.

## Webhook triggers

***

Webhooks are the primary way external systems trigger Flowker workflows. Instead of your system calling the executions API directly, you register a webhook path in a workflow. External services then send HTTP requests to that path.

### How it works

1. Add a trigger node of type `webhook` to your workflow with a `path` and a `method` in its `data`. Set `input_contract` explicitly for new nodes when you need `open`, `xsd`, or `openapi` validation.
2. When you activate the workflow, Flowker registers the path in its webhook registry.
3. External systems send requests to [`POST /v1/webhooks/{path}`](/en/reference/products/flowker/trigger-webhook) (or the method you configured).
4. Flowker resolves the path to the matching workflow and executes it.

### Defining a webhook trigger node

The webhook trigger is a node with `type: "trigger"` and `triggerType: "webhook"` in its `data`, plus a `path`, a `method`, and an optional `input_contract`. [Configuring a webhook trigger](/en/products/flowker/configuring-a-webhook-trigger) covers every field, the three `input_contract` modes and what each one requires, and carries a worked node for each mode.

The trigger configuration follows a closed contract. Saving a workflow fails with `FLK-0934` when its webhook trigger omits `path` or `method`, or misses a field its selected `input_contract` mode requires. It also fails when the trigger names another mode's schema id or operation field. It fails too when the trigger carries a key or a value the schema does not accept. An invalid `accepted_headers` declaration instead fails with `FLK-0957`.

The schema also declares the optional `response_mode`, `response_view`, and `accepted_headers` fields. See [Configuring a webhook trigger](/en/products/flowker/configuring-a-webhook-trigger).

### Securing a webhook

Webhook delivery uses the same authentication as the rest of the API. With Access Manager enabled (`PLUGIN_AUTH_ENABLED=true`), every request to `/v1/webhooks/*` must carry a Bearer token (OIDC JWT), and the caller must hold the `execute` permission on the `webhooks` resource. A request without a valid token fails with `401 Unauthorized`.

Grant that permission to a machine-to-machine identity for each system you let call your webhooks, and manage the grant in Access Manager. This keeps webhook access under the same role and policy model as workflow management, rather than a credential attached to the path.

### Webhook metadata

Flowker automatically injects a `_webhook` object into the execution's `inputData` with metadata about the incoming request:

| Field                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `_webhook.method`    | HTTP method used (e.g., `POST`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `_webhook.path`      | The resolved webhook path.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `_webhook.headers`   | Request headers, filtered through a safe allowlist (`Content-Type`, `Accept`, `User-Agent`, `X-Request-Id`, `X-Forwarded-For`, `Idempotency-Key`) plus this trigger's optional `accepted_headers` and, for an `openapi` route, its operation's header parameters. The fixed denylist blocks `Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, and `X-Auth-Token`. Other admitted headers are persisted verbatim; never admit secret-bearing names such as `X-Amz-Security-Token`. |
| `_webhook.query`     | Preserves the names of received query parameters. It preserves values only for `customerId`, `page`, `cursor`, `limit`, `offset`, and `sortOrder` (case-insensitive); every other value is stored as `[redacted]`.                                                                                                                                                                                                                                                                                           |
| `_webhook.remote_ip` | IP address of the caller.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |

This metadata is available to all nodes in the workflow via the `workflow._webhook` namespace.

### Important notes

* Only one active workflow can register each webhook path + method combination. Activating a second workflow with the same path fails with a conflict error.
* Webhook paths support nested segments (e.g., `payments/stripe/received`).
* The request body maximum size is 1 MB.
* Deactivating a workflow automatically unregisters its webhook routes.

See the [Trigger a webhook](/en/reference/products/flowker/trigger-webhook) API reference for the complete endpoint documentation.

### Synchronous response mode

By default, a webhook trigger responds with a `202` receipt as soon as the execution starts (the async mode). The caller must poll the execution status separately. Set `response_mode` to `"sync"` in the trigger node's `data` to have Flowker hold the HTTP connection open and return the execution's outcome directly in the response:

| Field           | Type   | Required | Description                                                                                                                                                                                |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `response_mode` | string | No       | `"async"` (default) returns a `202` receipt immediately. `"sync"` blocks (up to an internal cap) for the execution to reach a terminal state and returns the outcome in the response body. |
| `response_view` | string | No       | Shapes the sync response body. Only meaningful when `response_mode` is `"sync"`. See the table below. Defaults to `"full"`.                                                                |

If the execution does not reach a terminal state before the internal wait cap elapses, Flowker falls back to the async mode's `202` receipt. That receipt carries a `Location` header pointing at the results endpoint.

`response_view` selects the shape of the sync response body:

| Value            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `full` (default) | The complete execution dump — `executionId`, `workflowId`, `status`, `stepResults`, `finalOutput` — the same shape you would fetch from the execution results endpoint.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `final_output`   | Only the execution's `finalOutput` map, with no envelope wrapper.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `receipt`        | The lean execution receipt (`executionId`, `workflowId`, `status`, `startedAt`) — the same shape the async path returns.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `passthrough`    | Shapes the response by the **terminal (last-executed) step's node type**. For a terminal executor that captured a provider response, it returns the provider status (including `4xx`) and `Content-Type`. It relays at most 8 KiB of the captured body; longer bodies are truncated. JSON bodies are decoded and re-serialized before capture, so byte-for-byte relay is not guaranteed. If the terminal step is a `set_output` action with a configured output, the response is that node's business output, honoring its `responseStatusCode` override. If neither applies (no captured provider response — circuit open, timeout, pre-dispatch failure — and no terminal output), it falls back to the full envelope at HTTP `200` so the caller still gets a meaningful outcome. |

A failed execution's `finalOutput` (in `full` or `final_output` view) always carries `status: "failed"` and `errorMessage`, and `errorClass` when Flowker could classify the failure, never a bare `{}`. Absent a `responseStatusCode` override (see below), the sync HTTP status stays `200` for `full`/`final_output`/`receipt` (it reports transport health, not business outcome). A valid `responseStatusCode` on the terminal `set_output` node overrides that status for those three views.

An action node with `actionType: "set_output"` can carry an optional `responseStatusCode` (integer, `200`–`599`) to override the HTTP status a `sync` webhook response returns. An out-of-range or non-integer value fails at save time (`FLK-0122`). For `passthrough`, the override applies only when the `set_output` node itself is the terminal step. A terminal executor's relayed provider status always wins, and the no-response fallback always uses a plain `200` so an override never masks a failure.

Passthrough detection is strict: only the terminal step counts. A `set_output` terminal downstream of an executor shapes the response as its own output. Flowker never walks back to an earlier executor's response. On a failed execution the halting step is the terminal step, so Flowker relays a provider `4xx` that stopped the workflow as the real `4xx`.

Values in a `set_output` node's output support `${...}` references resolved against the workflow context, including `${workflow.<field>}` (trigger payload), `${execution.id}`, `${execution.startedAt}`, and `${execution.now}` (stamped at interpolation time). An unresolvable `${...}` reference fails the step (fail-closed).

## Error handling

***

If a node fails, the execution stops and its status becomes `failed`.

There is no automatic fallback. After the retries run out, the execution fails.

Execution results report the execution `status` and `stepResults`. A failed step provides `stepNumber`, `nodeId`, `status`, and `errorMessage`, with `statusCode` and `errorClass` when available. The `output` field is optional. Do not promise an `errorCode`, including `FLK-0504` or `FLK-0507`, in every execution-results payload.

## Retry and circuit breaker

***

Flowker includes built-in resilience for executor calls.

### Retries

When an executor call fails with a transient error (a network error, a timeout on the attempt, any `5xx` status, or status `408` or `429`), Flowker retries automatically. Retry behavior is configurable per node, in the executor node's `data`:

| Setting                 | Default                                   | Bounds                       | Description                                                                                                 |
| ----------------------- | ----------------------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `timeout_seconds`       | 30                                        | 1–300                        | Per-request timeout.                                                                                        |
| `retry.max_attempts`    | 3 (1 for unconfigured `POST` and `PATCH`) | 1–10 accepted; 1–5 effective | Accepts `1`–`10` in the node schema, but Flowker clamps the effective runtime attempt count to `1`–`5`.     |
| `retry.backoff_seconds` | 1                                         | 1–60                         | First backoff ceiling; each wait is a random value between zero and the ceiling, which doubles per attempt. |
| `success_status_codes`  | `[200, 201, 202, 204]`                    | 100–599                      | HTTP status codes treated as success.                                                                       |

Retries only apply when the operation is safe to repeat. By default, Flowker treats `POST` and `PATCH` calls as non-idempotent and does **not** retry them (a single attempt), while `GET`, `PUT`, `DELETE`, and other verbs retry normally. A `retry.max_attempts` greater than `1` opts that node into retries whatever the method is. A `retry.max_attempts` of `1` is not an opt-in. It sets a single attempt.

**Non-retryable errors** short-circuit to a single attempt regardless of configuration. They are: circuit breaker open, context cancelled, configuration errors, and secret-resolution failures. They also include a request body over the configured size cap, a provider response body over the same cap, and non-transient `4xx` provider responses. That means any `4xx` except `408` and `429`.

The retry applies per node execution. If all attempts fail, the step fails and the execution stops.

### Circuit breaker

Flowker uses a circuit breaker so that repeated failing calls do not overwhelm external services:

| Parameter          | Value                                                                  |
| ------------------ | ---------------------------------------------------------------------- |
| Failure threshold  | 20 consecutive failures opens the circuit (configurable at deployment) |
| Recovery timeout   | 30 seconds before trying again (half-open state)                       |
| Half-open requests | 1 request allowed to test if the service recovered                     |

Provider `4xx` client/auth errors do **not** trip the circuit: they are the caller's problem, not a sign the provider is down. Only transport-level and `5xx` failures count toward the threshold.

When the circuit is open, executor calls fail immediately with `FLK-0507` instead of reaching the external service. This prevents cascading failures and gives the external service time to recover.

<Frame caption="Circuit breaker state transitions">
  <img src="https://mintcdn.com/lerian-49cb71fc/Mmb3JaVhlcaSV8yn/images/en/d2/flowker-circuit-breaker.svg?fit=max&auto=format&n=Mmb3JaVhlcaSV8yn&q=85&s=60f59fb0d092e1f2ad73ea4b7628627e" alt="Circuit breaker states" width="1110" height="394" data-path="images/en/d2/flowker-circuit-breaker.svg" />
</Frame>

The circuit starts in the **Closed** state, where all requests pass through normally. After the circuit reaches the failure threshold, it transitions to **Open** and blocks all requests immediately. After 30 seconds, it moves to **Half-Open** and allows one test request. If that request succeeds, the circuit returns to Closed. If it fails, the circuit reopens for another 30-second cycle.

<Warning>
  The circuit breaker operates per provider configuration, scoped to your tenant. Failures against one connection do not affect another, and one tenant cannot open the circuit for another. Circuit breaker thresholds (failure count, recovery timeout) are global deployment defaults. You cannot customize them per connection in this version.
</Warning>

## Executor configuration registry

***

This registry is a third, separate use of the word "executor". Its records are not the catalog executors of [Step 1](#step-1-explore-the-catalog). They are not the workflow nodes of `type: "executor"`, and not the provider configurations of [Step 2](#step-2-create-a-provider-configuration). The engine reads provider configurations to call external services, not these records, and the registry carries its own field vocabulary (`baseUrl`, `endpoints`, `authentication`). The registry exposes four operations:

| Operation | Endpoint                                                                                    |
| --------- | ------------------------------------------------------------------------------------------- |
| List      | [`GET /v1/executors`](/en/reference/products/flowker/list-executor-configurations)          |
| Get       | [`GET /v1/executors/{id}`](/en/reference/products/flowker/get-executor-configuration)       |
| Update    | [`PATCH /v1/executors/{id}`](/en/reference/products/flowker/update-executor-configuration)  |
| Delete    | [`DELETE /v1/executors/{id}`](/en/reference/products/flowker/delete-executor-configuration) |

Every record carries a `status`, which the API reports in each response:

| Status         | Description                               |
| -------------- | ----------------------------------------- |
| `unconfigured` | The record has no connection details yet. |
| `configured`   | The record carries connection details.    |
| `tested`       | The record was verified.                  |
| `active`       | The record is in service.                 |
| `disabled`     | The record is out of service.             |

`PATCH` accepts `name`, `baseUrl`, `endpoints`, and `authentication`, plus the optional `description` and `metadata`. It does not accept `status`, but the list operation accepts `status` as a query filter. Update applies to records in `unconfigured` or `configured` status. Delete applies to records in `unconfigured`, `configured`, or `disabled` status. No operation in this version moves a record into `tested`, `active`, or `disabled`. The table lists those values because responses report them and the list filter accepts them.

## What's next

***

<CardGroup cols={2}>
  <Card title="Core concepts" icon="diagram-project" href="/en/products/flowker/flowker-concepts">
    Understand workflows, nodes, edges, and executions.
  </Card>

  <Card title="Provider configurations API" icon="code" href="/en/reference/products/flowker/list-provider-configurations">
    Explore the provider configuration API.
  </Card>
</CardGroup>
