> For the complete documentation index, see [llms.txt](https://docs.promptshuttle.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.promptshuttle.com/api-reference/flow-execution.md).

# Flow Execution

Execute flows programmatically and retrieve results.

## Run a flow

```
POST /api/v1/flows/{flowId}/runs
```

The `flowId` can be either the flow's **slug name** (e.g. `product_description`) or its **ObjectId**.

### Request body

| Field               | Type         | Required | Description                                                                                                                                                          |
| ------------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `parameters`        | object       | no       | Key-value map of template parameters. Keys must match `[[param]]` tokens.                                                                                            |
| `environment`       | string       | no       | Environment name (e.g. `"production"`). Determines which version runs.                                                                                               |
| `messages`          | array        | no       | Additional chat messages appended after the template's system/user messages.                                                                                         |
| `entrypoint`        | string       | no       | Override which template to use as entrypoint (by name).                                                                                                              |
| `version`           | string       | no       | Override which version to use (by ID). Takes precedence over environment.                                                                                            |
| `overrideModel`     | string       | no       | Force a specific model, bypassing template and routing config.                                                                                                       |
| `temperature`       | float        | no       | Override temperature.                                                                                                                                                |
| `maxTokens`         | integer      | no       | Override max output tokens.                                                                                                                                          |
| `maxThinkingTokens` | integer      | no       | Budget for extended thinking (reasoning models).                                                                                                                     |
| `maxToolCalls`      | integer      | no       | Max tool-calling loop iterations.                                                                                                                                    |
| `responseSchema`    | object       | no       | JSON Schema for structured output. Overrides template's schema.                                                                                                      |
| `vendorTools`       | string array | no       | Provider-native tools to enable (e.g. `["web_search"]`).                                                                                                             |
| `tags`              | string array | no       | Tags for filtering the invocation log, and for the [cost-per-tag report](/platform/analytics.md#cost-per-tag). Can also be supplied via the `X-Shuttle-Tags` header. |
| `nonce`             | string       | no       | Cache-bust string. Affects response cache hash, never sent to LLM.                                                                                                   |
| `logLevel`          | string       | no       | Override logging level: `Trace`, `Debug`, `Information`, `Warning`, `Error`.                                                                                         |
| `customerId`        | string       | no       | End-customer ID for per-customer usage tracking.                                                                                                                     |

### Headers

| Header                     | Description                                                                    |
| -------------------------- | ------------------------------------------------------------------------------ |
| `Authorization`            | `Bearer YOUR_API_KEY` (required)                                               |
| `X-Shuttle-Customer-Id`    | End-customer ID (takes precedence over `customerId` in body)                   |
| `X-Shuttle-Callback-Url`   | Origin where **your** app serves its own tools, e.g. `https://app.example.com` |
| `X-Shuttle-Callback-Token` | Secret presented back on caller-hosted tool calls. Never stored.               |
| `X-Shuttle-Debug-Url`      | Deprecated alias of `X-Shuttle-Callback-Url`; also adds a `DebugUrl:` tag      |

#### Caller-hosted tools

If your flow uses tools served by **your own application**, give those tools a relative `webUrl` (`/api/v1/tools/search`) and send `X-Shuttle-Callback-Url` on every request — from production as much as from a laptop. Omit it and the tool call fails with an explicit error rather than falling back to a stored URL, because a run answered by the wrong instance looks exactly like a successful one.

Tools with an absolute `https://…` URL are unaffected and behave as they always have.

See [Caller-Hosted Tools](/tools/caller-hosted-tools.md) for the allowlist, per-run tokens, and migration steps.

### Example

```bash
curl -X POST https://app.promptshuttle.com/api/v1/flows/product_description/runs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "parameters": {
      "product_name": "Smart Water Bottle",
      "tone": "playful"
    },
    "environment": "production",
    "tags": ["campaign-spring-2025"]
  }'
```

### Response

```json
{
  "id": "67a1b2c3d4e5f6a7b8c9d0e1",
  "timestamp": "2025-03-15T10:30:00Z",
  "flowId": "67a1b2c3d4e5f6a7b8c9d0e2",
  "tenantId": "67a1b2c3d4e5f6a7b8c9d0e3",
  "creditsUsed": 1250,
  "milliseconds": 1832,
  "usage": {
    "creditsUsed": 1250,
    "creditsLeft": 998750,
    "tokensIn": 85,
    "tokensOut": 142,
    "reasoningTokens": 0,
    "toolCost": 0,
    "costUsd": 0.00125
  },
  "flowRequest": {
    "parameters": {
      "product_name": "Smart Water Bottle",
      "tone": "playful"
    },
    "environment": "production",
    "tags": ["campaign-spring-2025"]
  },
  "responses": [
    {
      "model": "gpt-4o",
      "provider": "openai",
      "textResponse": "Meet the Smart Water Bottle — your hydration sidekick that...",
      "status": "completed",
      "finishReason": "stop",
      "usage": {
        "tokensIn": 85,
        "tokensOut": 142,
        "costUsd": 0.00125
      }
    }
  ],
  "warnings": []
}
```

### Response fields

| Field                      | Type         | Description                                              |
| -------------------------- | ------------ | -------------------------------------------------------- |
| `id`                       | string       | Unique run ID. Use this for feedback, agent trees, etc.  |
| `timestamp`                | datetime     | When the run was created.                                |
| `creditsUsed`              | long         | Total credits consumed (1M credits = $1).                |
| `milliseconds`             | integer      | Total execution time.                                    |
| `usage`                    | object       | Detailed usage breakdown.                                |
| `usage.creditsLeft`        | long         | Remaining tenant credit balance.                         |
| `responses`                | array        | Inference responses (one per LLM call in the chain).     |
| `responses[].textResponse` | string       | The LLM's text output.                                   |
| `responses[].toolCalls`    | array        | Any tool calls returned (if tool loop hasn't completed). |
| `responses[].citations`    | array        | Citation URLs (Perplexity only).                         |
| `warnings`                 | string array | Warnings about unused/unresolved parameters.             |

## Stop a running run

```
POST /api/v1/runs/{runId}/cancel
POST /api/v1/runs/cancel-all
```

`{runId}/cancel` stops one run **and every sub-agent beneath it**. `cancel-all` is the emergency stop: it stops every run currently executing for the tenant, including runs started by other users and applications.

Both return:

```json
{ "signalled": 3, "message": "Stopping 3 request(s). Runs finish their current model call first." }
```

### What stopping does and does not do

* **It is not instant.** Each executing request checks for the signal between tool-calling iterations, so a model call already in flight completes first. Expect a run to settle within roughly one model turn.
* **Single-shot calls cannot be stopped.** A request with no tools reaches the provider on its first and only turn, so there is no later checkpoint at which to notice. Stopping bites on agentic and tool-calling runs, which is where the money is.
* **Partial results are returned, not discarded.** The caller waiting on the run receives everything it produced before stopping, with a final response saying it was cancelled. That output was already paid for; stopping saves the work that had not happened yet.
* **The run is still billed** for what it spent.
* **`signalled` counts runs told to stop**, not runs that stopped. `0` means nothing was still executing — usually because the run finished first. That is not an error.
* **Panels and bulk calls** execute as independent runs with no shared parent, so cancelling one of them stops only that one. `cancel-all` reaches them.

A stopped run records `status: "succeeded"` if it produced anything (`"failed"` if it did not) plus a `cancelled` warning and a `cancelledAt` timestamp — status stays binary so cost and usage reporting keep working. Read `cancelledAt`, or the MCP `Run.Outcome` field, to tell a stopped run from a clean one. If it produced nothing at all, the waiting caller gets `409 Conflict`.

The OpenAI-compatible endpoint sets `X-PromptShuttle-Cancelled: true` on a partial answer.

## Discover parameters

Before running a flow, you can check what parameters it expects:

```
GET /api/v1/flows/{flowId}/parameters
```

**Query parameters:**

| Param         | Description                                   |
| ------------- | --------------------------------------------- |
| `environment` | Environment to resolve the active version for |
| `version`     | Specific version ID                           |
| `entrypoint`  | Specific template name                        |

**Response:**

```json
[
  { "token": "product_name", "source": "template" },
  { "token": "tone", "source": "template" }
]
```

## Submit feedback

Collect feedback on run quality for monitoring and fine-tuning:

```
POST /api/v1/feedback
```

```json
{
  "shuttleRequestId": "67a1b2c3d4e5f6a7b8c9d0e1",
  "score": 1,
  "text": "Great response, very accurate",
  "endUserId": "user_123"
}
```

| Field              | Type    | Required | Description                                |
| ------------------ | ------- | -------- | ------------------------------------------ |
| `shuttleRequestId` | string  | yes      | The run ID to attach feedback to           |
| `score`            | integer | yes      | Must be `+1` (positive) or `-1` (negative) |
| `text`             | string  | no       | Free-text feedback                         |
| `endUserId`        | string  | no       | Your end-user's identifier                 |

Feedback is upserted: submitting again for the same request replaces the previous feedback.

## Get agent execution tree

For flows that use agent tools (multi-agent orchestration), retrieve the full execution hierarchy:

```
GET /api/v1/flows/{flowId}/runs/{runId}/agent-tree
```

Returns a tree structure showing:

* Each agent invocation with its role, duration, and cost
* Tool calls made by each agent
* Child agents spawned
* Cumulative vs. direct credit usage
* Status and error messages

```json
{
  "requestId": "root_request_id",
  "agentRole": "main",
  "depth": 0,
  "durationMs": 5420,
  "creditsUsed": 3500,
  "totalTreeCreditsUsed": 8200,
  "toolCalls": [
    {
      "toolName": "research_agent",
      "toolType": "Agent",
      "durationMs": 2100,
      "spawnedRequestId": "child_request_id"
    }
  ],
  "children": [
    {
      "requestId": "child_request_id",
      "agentRole": "research_agent",
      "depth": 1,
      "creditsUsed": 4700,
      "children": []
    }
  ]
}
```
