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

# Creating Workflows

> Step-by-step guide to creating your first workflow in KnoxCall. Learn to add nodes, connect them, and execute automated processes.

# Creating Workflows

This guide walks you through creating your first workflow, from setting up a trigger to executing and monitoring the workflow.

## Prerequisites

Before creating a workflow, you'll need:

1. A KnoxCall account
2. Basic understanding of APIs (HTTP requests, JSON)

<Note>
  Workflow executions consume a metered monthly **operations** quota (each run counts against your tenant's limit based on the number of nodes). When the limit is reached, an execution is recorded as failed without running. Workflow access also depends on your plan's feature entitlements.
</Note>

## Step 1: Create a New Workflow

1. Navigate to **Workflows** in the sidebar
2. Click **+ Create Workflow**
3. Enter workflow details:

```text theme={"dark"}
Name: My First Workflow
Description: A simple workflow that fetches data and processes it
Environment: production (optional)
```

4. Click **Create**

You'll see the workflow editor with an empty canvas and a trigger node.

## Step 2: Configure the Trigger

The trigger node is already on the canvas. Click it to configure:

### Manual Trigger (Recommended for First Workflow)

```yaml theme={"dark"}
Trigger Type: Manual
```

This lets you run the workflow on-demand from the UI.

### Webhook Trigger

```yaml theme={"dark"}
Trigger Type: Webhook
Method: POST
```

This creates an endpoint that triggers the workflow when called.

### Route Event Trigger

```yaml theme={"dark"}
Trigger Type: Route Event   # underlying triggerType: proxy_event
Event Types:                 # proxyEventConfig.eventTypes (an array)
  - request.completed
Route Filter: (optional, select specific routes)
```

This triggers when requests pass through your routes. The underlying trigger type is `proxy_event`, and event selection is an `eventTypes` array. Valid event types are `request.received`, `request.success`, `request.error`, `request.client_error`, `request.server_error`, `request.timeout`, and `request.completed`. You can also narrow matches with an optional route filter, environment filter, and field conditions.

## Step 3: Add an HTTP Request Node

Let's add a node that makes an API call:

1. Click the **+** button on the trigger node's output
2. Select **HTTP Request**
3. Configure the node:

```yaml theme={"dark"}
Name: Fetch User Data

Method: GET
URL: https://jsonplaceholder.typicode.com/users/1

Headers:
  Content-Type: application/json
```

This calls a free test API that returns user data.

## Step 4: Add a Code Block Node

<Note>
  **Coming soon:** Code Block execution is currently **disabled for security** and is planned for a future release. The steps below show the intended authoring experience — a Code Block does not run custom JavaScript yet (an empty Code Block simply passes its input through unchanged).
</Note>

Let's transform the response:

1. Click the **+** on the HTTP Request node
2. Select **Code Block**
3. Configure:

```yaml theme={"dark"}
Name: Transform Data
```

4. Enter the code:

```javascript theme={"dark"}
// `input` is the raw output of the single upstream node (the HTTP Request).
// For an HTTP Request node the response body is under `input.body`.
const user = input.body;

// Transform and return new data
return {
  id: user.id,
  fullName: user.name,
  email: user.email,
  company: user.company.name,
  processed_at: new Date().toISOString()
};
```

<Note>
  A Code Block receives the **upstream node's output** directly as the global `input` (when a single edge feeds it). There is no `input.nodes['<Node Name>']` lookup. For fan-in (multiple incoming edges), `input` is an array of `{ nodeId, output }` entries keyed by node **id**, not name.
</Note>

## Step 5: Add a Condition Node

Let's add conditional logic:

1. Click the **+** on the Code Block node
2. Select **Condition**
3. Configure:

```yaml theme={"dark"}
Name: Check User ID
Logic: AND
Conditions:
  - Type: equals
    Path: $.id
    Value: 1
```

Condition nodes evaluate a structured list of conditions (each with a `type`, a JSONPath `path`, and a comparison `value`) combined with the `logic` operator (`AND` or `OR`). They do not take a free-form JavaScript expression string. Reference upstream data with the JSONPath `path` rather than an inline `{{...}}` placeholder.

This creates two paths:

* **True**: When user ID equals 1
* **False**: When user ID doesn't equal 1

## Step 6: Add Nodes to Each Branch

### True Path

1. Click the **+** on the Condition's **True** output
2. Select **Code Block**
3. Name it: "Success Response"
4. Code:

```javascript theme={"dark"}
// The Condition node passes its data through as `input.input`, and reports the
// branch result as `input.conditionMet`.
return {
  status: 'success',
  message: 'User verified!',
  data: input.input
};
```

### False Path

1. Click the **+** on the Condition's **False** output
2. Select **Code Block**
3. Name it: "Error Response"
4. Code:

```javascript theme={"dark"}
// `input.input` is the data the Condition node evaluated and passed through.
return {
  status: 'error',
  message: 'User verification failed',
  expected_id: 1,
  received_id: input.input.id
};
```

## Step 7: Save Your Workflow

1. Click **Save** in the top right
2. Your workflow is saved as version 1

Your workflow should look like this:

```text theme={"dark"}
[Trigger]
    ↓
[Fetch User Data] (HTTP Request)
    ↓
[Transform Data] (Code Block)
    ↓
[Check User ID] (Condition)
    ↓           ↓
   True        False
    ↓           ↓
[Success]   [Error]
```

## Step 8: Test Your Workflow

### Manual Execution

1. Click **Run** in the top right
2. (Optional) Add input data for the trigger:

```json theme={"dark"}
{
  "test": true
}
```

3. Click **Execute**

### Watch Execution

You'll see:

* Each node highlights as it executes
* Green checkmark when successful
* Red X if an error occurs
* Execution time per node

### View Results

Click on any node to see:

* **Input**: Data that went into the node
* **Output**: Data that came out
* **Execution Time**: How long it took

## Step 9: Enable the Workflow

1. Toggle **Enabled** to ON
2. Your workflow is now active

For webhook triggers, you'll now see the webhook URL to call.

## Adding More Nodes

### HTTP Request with POST

```yaml theme={"dark"}
Name: Create Record
Method: POST
URL: https://api.example.com/records

Headers:
  Content-Type: application/json

Body:
  name: "{{transform_data.output.fullName}}"
  email: "{{transform_data.output.email}}"
  source: "workflow"
```

Variable references use upstream node ids, e.g. `{{transform_data.output.fullName}}`. There is no `{{env.*}}` placeholder, and stored-secret injection into a node's auth is not yet available — see [Secrets](#secrets).

### Loop Over Array

```yaml theme={"dark"}
Name: Process Items
Loop Type: array
Max Iterations: 100
Array Config:
  Array Path: $.items     # JSONPath to the array in the node input
  Item Variable: item     # name used for the current item
  Batch Size: 1           # optional, items processed per batch
```

The loop reads the array at `arrayConfig.arrayPath` from the node input and exposes the current element under the variable named in `arrayConfig.itemVariable` (here, `item`). There is no `{{loop.*}}` placeholder; downstream references use the item variable name you configured.

### Delay

```yaml theme={"dark"}
Name: Wait Before Retry
Duration: 5
Unit: seconds
```

### Error Handler

1. Add an Error Handler node
2. Connect it to nodes that might fail
3. Configure what to catch:

```yaml theme={"dark"}
Name: Handle API Errors
Catch From: Fetch User Data, Create Record
```

4. Add nodes after the error handler for recovery:

```javascript theme={"dark"}
// In a Code Block after Error Handler. The Error Handler's output is the
// `input` here; the caught error message is exposed as `input.originalError`.
return {
  error_logged: true,
  original_error: input.originalError,
  timestamp: new Date().toISOString()
};
```

## Using Variables

Variable references use a single form: `{{<nodeId>.<path>}}`, where `<nodeId>` is the id of an upstream node and `<path>` is a dot-notation path (with optional `[index]` array access) into that node's output. There are no `nodes['Name']`, `env.`, or `loop.` namespaces.

### Previous Node Output

```text theme={"dark"}
// Access output from an upstream node by its node id
{{fetch_user_data.body.id}}
{{fetch_user_data.body.email}}
```

For an HTTP Request node, available fields include `status`, `statusText`, `headers`, `body`, `url`, `method`, and `success`.

### Trigger Data

```text theme={"dark"}
// The trigger node's output is available under its node id
{{trigger.data.user_id}}
```

The `trigger` alias resolves only when the trigger node's id is exactly `trigger-node`. The trigger output exposes `type`, `data`, and `timestamp` — the data you provide when running the workflow is under `data`.

### Secrets

<Warning>
  **Secret injection in workflows is coming soon.** There is no `{{env.X}}` placeholder, and stored-secret injection is not yet wired up: the engine's secret loader is not implemented, so a node's `secret_ref` auth does not yet inject a value at runtime. Until it ships, pass any required credentials through the trigger payload or an upstream node's output.
</Warning>

## Workflow Examples

### Simple API Call

```text theme={"dark"}
Trigger (Manual)
    ↓
HTTP Request (GET https://api.example.com/data)
    ↓
Code Block (Format response)
```

### Data Sync

```text theme={"dark"}
Trigger (Webhook)
    ↓
HTTP Request (Fetch from Source API)
    ↓
Loop (For each record)
    ↓
HTTP Request (Push to Destination API)
```

### Error Handling

```text theme={"dark"}
Trigger
    ↓
HTTP Request → Error Handler → Slack Notification
    ↓
Code Block
    ↓
Condition
    ↓         ↓
Success    Failure
    ↓         ↓
Continue   Log Error
```

## Execution History

View past executions:

1. Open the workflow
2. Click **Executions** tab
3. See list with:
   * Status (completed, failed, running, cancelled)
   * Started at
   * Duration
   * Trigger type

Click any execution to see:

* Full node-by-node timeline
* Input/output for each node
* Error details

## Versioning

Each time you save, a new version is created:

1. Click **Versions** tab
2. See all saved versions
3. Click a version to view it

<Note>
  Version history is viewable today (you can list every saved version and inspect it). One-click **Restore/rollback** to a previous version is coming soon — there is currently no restore action. To roll back manually, open the version you want and re-save its definition as a new version.
</Note>

## Troubleshooting

### "Variable not found"

```text theme={"dark"}
Error: Cannot read property 'data' of undefined
```

**Cause:** Referencing a node that hasn't executed yet or doesn't exist.

**Fix:** Check node names match exactly (case-sensitive).

### "HTTP Request failed"

```text theme={"dark"}
Error: ECONNREFUSED
```

**Cause:** Target API is unreachable.

**Fix:**

* Verify URL is correct
* Check if API requires authentication
* Ensure API is accessible from the internet

### "Code Block error"

```text theme={"dark"}
Error: Unexpected token
```

**Cause:** JavaScript syntax error.

**Fix:** Check your code for:

* Missing brackets
* Incorrect variable references
* Invalid JSON

### "Condition always false"

**Cause:** Comparison type mismatch, or a wrong JSONPath in the condition.

**Fix:** Check the condition's `path` resolves to the value you expect, and pick the matching condition `type`:

```yaml theme={"dark"}
# String comparison
- Type: equals
  Path: $.status
  Value: success

# Number comparison
- Type: greater_than
  Path: $.count
  Value: 0
```

## Best Practices

1. **Name nodes descriptively** - "Fetch Customer Data" not "HTTP 1"
2. **Add error handlers** for external API calls
3. **Test with sample data** before enabling
4. **Use a secret reference** (`secret_ref` auth) for credentials instead of hard-coding them
5. **Keep workflows focused** - one purpose per workflow
6. **Document complex logic** in node names/descriptions

## Next Steps

<CardGroup cols={2}>
  <Card title="Workflows Overview" icon="diagram-project" href="/workflows/workflows-overview">
    Understanding workflow concepts
  </Card>

  <Card title="Webhooks" icon="webhook" href="/webhooks/webhooks-overview">
    Trigger workflows from webhooks
  </Card>

  <Card title="API Logs" icon="list" href="/monitoring/api-logs">
    Debug workflow API calls
  </Card>

  <Card title="Route Examples" icon="lightbulb" href="/essentials/routes/route-examples">
    API patterns to use in workflows
  </Card>
</CardGroup>

***

<CardGroup cols={2}>
  <Card title="Statistics" icon="chart-line">
    * **Level**: beginner
    * **Time**: 12 minutes
  </Card>

  <Card title="Tags" icon="tags">
    `workflows`, `tutorial`, `getting-started`, `automation`
  </Card>
</CardGroup>
