> For the complete documentation index, see [llms.txt](https://docs.middle.app/middle-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.middle.app/middle-docs/real-time-workflow-api.md).

# Real-time Workflow API

## Quick start

To call a workflow through the API:

1. [Create an API key](#create-an-api-key) in the account that owns the workflow endpoint.
2. [Create a real-time workflow](#configure-a-real-time-workflow-api-endpoint) API endpoint and link the API key to it.
3. Copy the generated invoke URL.
4. Send a JSON object to that URL with the API key in the `Authorization` header.

## Create an API key

1. Open the account in Middle.
2. Select **API keys** in the account navigation.
3. Select **Add API key**.
4. Enter a descriptive name for the calling system, such as `Rewards service`.
5. Leave **Active** on and select **Save**.
6. Copy the secret immediately and store it in your secret manager.

The secret is shown only once. Middle cannot display it again after you leave or refresh the page. If it is lost, rotate the key or create a new key.

{% hint style="danger" %}
Treat the entire value as a secret. Do not put it in a URL, query parameter, source control, browser-side code, or logs.
{% endhint %}

### What an API key grants access to

An API key belongs to a single Middle account and can

* list the live workflow API endpoint configurations authorized for it
* call only the endpoints to which it is explicitly linked

For the key to authorize a call, all three of these access records must be active:

* the API key
* the real-time workflow API endpoint
* the workflow's real-time API trigger

One key can be linked to multiple endpoints, and one endpoint can accept multiple keys. This makes it possible to give different calling systems different access and to replace keys without interrupting traffic.

## Authentication

Send the API key in the HTTP `Authorization` header on every request:

```http
Authorization: Bearer YOUR_API_KEY
```

## Endpoints

The real-time workflow API has two endpoints that you can call:

* `GET /api/workflow-invoke-options/` lists the live endpoints authorized for the key.
* `POST /api/workflow-invoke/{path_token}/` runs the workflow for one endpoint.

The list endpoint returns one option per endpoint, including the workflow name, invoke URL, input shape, and output shape. The same workflow name may appear more than once when different module connections expose it. The endpoint does not list every workflow in the account.

## List the workflow endpoints available to a key

Use the discovery endpoint to list the live endpoint configurations linked to an API key. This is also the easiest way to inspect each target workflow's input and output types.

```bash
curl --request GET \
  --url 'https://test.middle.app/api/workflow-invoke-options/' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

Example response:

```json
{
  "success": true,
  "endpoint_options": [
    {
      "script_graph_api_endpoint_id": 321,
      "workflow_name": "Redeem a reward",
      "invoke_url": "https://test.middle.app/api/workflow-invoke/PATH_TOKEN/",
      "input_object": {
        "attribute_type": "object",
        "children": [
          {
            "key": "input_record",
            "attribute_type": "foreign_key",
            "display_name": "Input record"
          },
          {
            "key": "customer_email",
            "attribute_type": "string",
            "display_name": "Customer email"
          },
          {
            "key": "reward_name",
            "attribute_type": "string",
            "display_name": "Reward name"
          }
        ]
      },
      "output_object": {
        "attribute_type": "object",
        "children": [
          {
            "key": "reward_granted",
            "attribute_type": "boolean",
            "display_name": "Reward granted"
          },
          {
            "key": "confirmation_id",
            "attribute_type": "string",
            "display_name": "Confirmation ID"
          }
        ]
      }
    }
  ]
}
```

The response includes only endpoints linked to this key whose endpoint and trigger are both active. A valid key with no available endpoints receives an empty `endpoint_options` array.

Discovery does not revalidate the workflow itself. Invocation separately checks that the workflow has a live, API-eligible version. A discovered endpoint can therefore still return `WORKFLOW_NOT_LIVE` or `WORKFLOW_NOT_API_ELIGIBLE` when called.

The `input_object` and `output_object` values may be `null` when a workflow does not define the corresponding type information.

## Invoke a workflow

Send a `POST` request to `/api/workflow-invoke/{path_token}/` using the generated invoke URL returned by the list endpoint or displayed in Middle. The request body must be a JSON object, even when the workflow has no inputs.

```bash
curl --request POST \
  --url 'https://test.middle.app/api/workflow-invoke/YOUR_PATH_TOKEN/' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "input_record": null,
    "customer_email": "customer@example.com",
    "reward_name": "Free smoothie"
  }'
```

Example successful response:

```json
{
  "success": true,
  "output": {
    "reward_granted": true,
    "confirmation_id": "reward-abc123"
  },
  "workflow_report_url": "https://test.middle.app/account/1/workflow-history/98765/"
}
```

The value of `output` is defined by the workflow's Return step. It may be an object, array, string, number, boolean, or `null`.

The report URL opens the saved workflow execution in Middle. Viewing that page requires normal Middle user access; the API key does not grant access to the user interface.

### Input handling and warnings

The workflow's Begin step defines the accepted top-level input keys.

* A declared key that is present is passed to the workflow.
* A declared key that is missing is set to `null` and produces a warning.
* An undeclared key is ignored and produces a warning.

The discovery metadata describes the intended value types. Middle normalizes top-level keys before execution, but it does not pre-validate every value against those types. An incorrectly typed value may cause the workflow to fail.

A workflow can succeed and still return warnings:

```json
{
  "success": true,
  "output": {
    "reward_granted": false,
    "confirmation_id": null
  },
  "workflow_report_url": "https://test.middle.app/account/1/workflow-history/98766/",
  "warnings": [
    "Input key \"reward_name\" was not provided; null was used.",
    "Input key \"debug\" was ignored."
  ]
}
```

The maximum request body is 256 KiB. The complete successful response, including the output and response envelope, is also limited to 256 KiB.

## Errors

Handled errors use a consistent JSON envelope:

```json
{
  "success": false,
  "error_code": "AUTHENTICATION_FAILED",
  "error_message": "The provided API key is invalid for this endpoint."
}
```

An error that occurs after workflow execution begins also includes `workflow_report_url`. When applicable, it may include `warnings` as well.

Common invoke endpoint responses include:

| HTTP status | Error code                     | Meaning                                       |
| ----------- | ------------------------------ | --------------------------------------------- |
| 401         | `INVALID_AUTHORIZATION_HEADER` | The Bearer header is missing or malformed.    |
| 401         | `AUTHENTICATION_FAILED`        | The key cannot call this invoke URL.          |
| 400         | `API_ENDPOINT_NOT_FOUND`       | The path token does not match an endpoint.    |
| 400         | `API_ENDPOINT_NOT_LIVE`        | The account deactivated the endpoint.         |
| 400         | `API_TRIGGER_NOT_LIVE`         | The workflow API trigger is inactive.         |
| 400         | `INVALID_JSON`                 | The request body is not valid JSON.           |
| 400         | `INVALID_REQUEST_BODY`         | The JSON value is not an object.              |
| 400         | `REQUEST_TOO_LARGE`            | The request exceeds 256 KiB.                  |
| 400         | `WORKFLOW_NOT_LIVE`            | The workflow has no live version.             |
| 400         | `WORKFLOW_NOT_API_ELIGIBLE`    | The live workflow violates an API rule.       |
| 400         | `RESPONSE_TOO_LARGE`           | The successful response would exceed 256 KiB. |
| 400         | `WORKFLOW_EXECUTION_FAILED`    | The workflow did not complete successfully.   |

A workflow failure may return a more specific execution error code instead of `WORKFLOW_EXECUTION_FAILED`. Treat every response with `success: false` as a failure.

The discovery endpoint returns `401` for a missing or malformed Bearer header. It currently returns `400` with `AUTHENTICATION_FAILED` for an invalid or inactive key.

Each request that reaches workflow execution, including a repeated request, starts a new workflow execution. The API does not accept an idempotency key, so only retry automatically when the workflow is safe to run more than once.

## Configure a real-time workflow API endpoint

An API key does not expose a workflow by itself. An account must create an endpoint and link one or more API keys to it.

### Expose an account workflow

1. Open the account and select **Workflow triggers**.
2. Find **Real-time workflow API endpoints**.
3. Select **Add real-time workflow API endpoint**.
4. Select the workflow to expose.
5. Select the API keys that may call it.
6. Select **Create**.
7. Copy the generated **Invoke URL**.

The new endpoint is active immediately. Creation requires at least one linked, active API key. The account can later edit the linked keys, deactivate the endpoint, delete it, or rotate its path token.

Rotating a path token immediately changes the invoke URL. It does not rotate any API keys.

### Workflow requirements

A real-time API workflow runs synchronously: the HTTP request waits for the live workflow version to finish, and the Return step supplies the response's `output` value.

An eligible workflow must:

* have a live version;
* contain exactly one Return step;
* not contain a Bulk workflow execution step;
* not contain a File export step;
* not contain a Create or update record step;
* use only actions whose Lambda timeout is known and no more than 8 seconds.

The API runs actions inline and cannot pause for asynchronous child work. Because the caller waits for the entire workflow, design real-time workflows to finish quickly.

## Real-time workflow API triggers and modules

Modules separate the reusable workflow definition from each account's decision to expose it.

### What the module author configures

The module author:

1. Creates an eligible workflow in the module and makes a version live.
2. Opens the module's **Workflow triggers** page.
3. Adds an active **Real-time workflow API trigger** for that workflow.

The trigger says that connected accounts may expose the workflow. It does not create a public URL, create an API key, or grant an account access by itself.

### What the connected account configures

Each connected account opts in separately:

1. Open the account and select **Workflow triggers**.
2. Find **Real-time workflow API endpoints from modules**.
3. Select **Connect real-time workflow API endpoint from module**.
4. Select the module connection, or account-specific installation, to use.
5. Select one of the real-time workflow API triggers defined by that module.
6. Select one or more API keys owned by the account.
7. Select **Create** and copy the generated invoke URL.

When the URL is called, Middle runs the module workflow in the context of that specific module connection. The workflow uses the app connections and module variables configured for that installation.

As a result:

* each endpoint belongs to one account and targets one module connection;
* each account manages its own invoke URLs and API keys;
* one account's key cannot call another account's module endpoint;
* the same module workflow can use different app connections and variables for each module connection, including multiple connections in one account;
* disabling the module's trigger stops calls through every account endpoint that uses it;
* disabling an account endpoint or key affects only that account.

If a module feature requires a real-time workflow API trigger, its setup is not ready until the module trigger is active and the account has a live endpoint for that module connection with at least one linked, active API key. Middle reports the missing setup, but does not create the key or URL automatically.

Public callers use the same discovery and invoke requests for account-owned and module-owned workflows. No module-specific headers or request envelope are required.

## Rotate or revoke access

### Rotate a key immediately

Open the key and select **Rotate secret**. The old secret stops working immediately, and the replacement is shown once. The key remains linked to the same endpoints.

### Replace a key without interrupting traffic

1. Create a second active API key.
2. Link both the old and new keys to each required endpoint.
3. Update the calling system to use the new key.
4. Confirm the new key's **Last used** value updates.
5. Deactivate or delete the old key.

Deactivating a key immediately rejects it but preserves its endpoint links. Reactivating it restores those grants. Deleting a key permanently removes its endpoint links.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.middle.app/middle-docs/real-time-workflow-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
