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

# Workflows Overview

> Create, manage, and execute automated data enrichment pipelines with the Workflows API.

## Use Case

Run batch data enrichment and automated processing pipelines. Workflows are ideal for processing many records through multi-step pipelines (e.g., validate emails, enrich companies, export to CSV) without manual intervention.

## Core Concepts

The Workflows API models pipelines as directed acyclic graphs (DAGs):

| Term         | Description                                                            |
| ------------ | ---------------------------------------------------------------------- |
| **Workflow** | A reusable pipeline definition containing blocks and edges             |
| **Block**    | A processing step (e.g., webhook input, enrichment, filtering, export) |
| **Edge**     | A connection between blocks that defines data flow                     |
| **Run**      | An execution instance of a workflow                                    |
| **Job ID**   | A unique identifier for tracking a workflow run                        |

## Execution Flow

```mermaid theme={null}
flowchart LR
  A[List Workflows] --> B[Run Workflow]
  B --> C[Poll Live Status]
  C --> D[Download Results]
```

1. **List workflows** — Get available workflows in your organization
2. **Run workflow** — Execute with input data; receive a `job_id`
3. **Poll live status** — Monitor progress until `completed`, `failed`, `cancelled`, or `timeout`
4. **Download results** — Get signed URLs for result files (CSV, NDJSON)

<Note>For workflows that start with a `webhook` block, see [Incoming Webhooks](/api-reference/webhooks/incoming).</Note>

## Quick Start

<Tip>Don't want to write code? Use [Sixtyfour's Workflow Builder](https://app.sixtyfour.ai/templates) to create and manage your own workflows.</Tip>

<CodeGroup>
  ```bash cURL theme={null}
  # 1. List available workflows
  curl -X GET "https://api.sixtyfour.ai/workflows" \
    -H "x-api-key: YOUR_API_KEY"

  # 2. Run a workflow with data
  curl -X POST "https://api.sixtyfour.ai/workflows/run?workflow_id=WORKFLOW_ID" \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "webhook_payload": [
        {"company_name": "Acme Corp", "website": "acme.com"}
      ]
    }'

  # 3. Check status (use job_id from step 2)
  curl -X GET "https://api.sixtyfour.ai/workflows/runs/JOB_ID/live_status" \
    -H "x-api-key: YOUR_API_KEY"

  # 4. Download results
  curl -X GET "https://api.sixtyfour.ai/workflows/runs/JOB_ID/results/download-links" \
    -H "x-api-key: YOUR_API_KEY"
  ```

  ```python Python theme={null}
  import requests
  import time

  API_KEY = "YOUR_API_KEY"
  BASE_URL = "https://api.sixtyfour.ai"
  headers = {
      "x-api-key": API_KEY,
      "Content-Type": "application/json"
  }

  # 1. Run workflow
  run_response = requests.post(
      f"{BASE_URL}/workflows/run?workflow_id=wf_abc123",
      headers=headers,
      json={
          "webhook_payload": [
              {"company_name": "Acme Corp", "website": "acme.com"},
              {"company_name": "TechStart", "website": "techstart.io"}
          ]
      }
  )
  run_response.raise_for_status()
  job_id = run_response.json()["job_id"]
  print(f"Workflow started: {job_id}")

  # 2. Poll for completion
  while True:
      status_response = requests.get(
          f"{BASE_URL}/workflows/runs/{job_id}/live_status",
          headers=headers
      )
      status = status_response.json()

      print(f"Status: {status['overall_status']} - {status['overall_progress_percentage']:.1f}%")

      if status["overall_status"] in ["completed", "failed", "cancelled", "timeout"]:
          break

      time.sleep(5)

  # 3. Download results
  if status["overall_status"] == "completed":
      results_response = requests.get(
          f"{BASE_URL}/workflows/runs/{job_id}/results/download-links",
          headers=headers
      )
      for result in results_response.json():
          print(f"Download: {result['filename']} ({result['row_count']} rows)")
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = 'YOUR_API_KEY';
  const BASE_URL = 'https://api.sixtyfour.ai';
  const headers = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };

  // 1. Run workflow
  const runResponse = await fetch(
    `${BASE_URL}/workflows/run?workflow_id=wf_abc123`,
    {
      method: 'POST',
      headers,
      body: JSON.stringify({
        webhook_payload: [
          { company_name: 'Acme Corp', website: 'acme.com' },
          { company_name: 'TechStart', website: 'techstart.io' }
        ]
      })
    }
  );
  if (!runResponse.ok) throw new Error(`Failed to start: ${await runResponse.text()}`);
  const { job_id } = await runResponse.json();
  console.log(`Workflow started: ${job_id}`);

  // 2. Poll for completion
  let status;
  while (true) {
    const statusResponse = await fetch(
      `${BASE_URL}/workflows/runs/${job_id}/live_status`,
      { headers }
    );
    status = await statusResponse.json();
    console.log(`Status: ${status.overall_status} - ${status.overall_progress_percentage}%`);
    if (['completed', 'failed', 'cancelled', 'timeout'].includes(status.overall_status)) break;
    await new Promise((r) => setTimeout(r, 5000));
  }

  // 3. Download results
  if (status.overall_status === 'completed') {
    const resultsResponse = await fetch(
      `${BASE_URL}/workflows/runs/${job_id}/results/download-links`,
      { headers }
    );
    const results = await resultsResponse.json();
    results.forEach((r) => console.log(`Download: ${r.filename} (${r.row_count} rows)`));
  }
  ```
</CodeGroup>

## Uploading an input file

Workflows that start with a `read_csv` block read their rows from a file you upload first.

<Tip>Enriching a list of people or companies? Use [Bulk Intelligence](/api-reference/endpoint/bulk-intelligence) — one call with file and config, no workflow or handle needed.</Tip>

```http theme={null}
POST https://api.sixtyfour.ai/storage/csv/upload
```

Send the file as `multipart/form-data` with a single file part. Accepts `.csv`, `.json`, `.jsonl`, and `.ndjson`.

```bash cURL theme={null}
curl -X POST "https://api.sixtyfour.ai/storage/csv/upload" \
  -H "x-api-key: YOUR_API_KEY" \
  -F "file=@leads.csv"
```

The response returns a `handle_id`:

```json theme={null}
{ "handle_id": "9b1f9d2e-..." }
```

Pass `handle_id` as `specs_override.resource_handle_id` when you call `/workflows/run`:

```bash cURL theme={null}
curl -X POST "https://api.sixtyfour.ai/workflows/run?workflow_id=WORKFLOW_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "specs_override": { "resource_handle_id": "9b1f9d2e-..." } }'
```

## NDJSON input & output

Inputs can be NDJSON or JSONL as well as CSV — upload them the same way (see [Uploading an input file](#uploading-an-input-file)).

For NDJSON output, add `result_formats=ndjson` when running. CSV is always produced regardless; NDJSON is added alongside it. Use NDJSON when you need typed values — numbers, booleans, and nested objects survive instead of being flattened to strings.

```bash cURL theme={null}
# Request NDJSON output (CSV is always included automatically)
curl -X POST "https://api.sixtyfour.ai/workflows/run?workflow_id=WORKFLOW_ID&result_formats=ndjson" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "specs_override": { "resource_handle_id": "9b1f9d2e-..." } }'

# Get the NDJSON download link — returns a JSON array of result objects, each with a download_url
curl -X GET "https://api.sixtyfour.ai/workflows/runs/JOB_ID/results/download-links?format=ndjson" \
  -H "x-api-key: YOUR_API_KEY"

# Fetch the file from the returned download_url
curl -X GET "DOWNLOAD_URL" \
  -H "x-api-key: YOUR_API_KEY" \
  -o results.ndjson
```

The `format` query parameter selects which output to link — `csv` (default), `json`, or `ndjson`. It defaults to `csv`, so pass `format=ndjson` to get the NDJSON links.
