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

# Event-to-call workflows

> Turn incoming events into outbound calls with a fixed workflow.

Open **Workflows** in the sidebar. This workspace turns incoming events into calls with a simple, fixed workflow. It is separate from the [agent workflow](/build/agent-workflow) canvas and from a [conversation flow](/build/conversation-flow).

The page has two tabs: **Workflows** and **Runs**.

Empty state: **Build your first workflow** / **Choose a trigger, map your data, and decide when your agent should call.**

<Columns cols={2}>
  <Card title="Workflow nodes" icon="boxes" href="/deploy/workflows/nodes">
    Start, Action, Data, Mapper, Condition, Delay, Iterator, and End.
  </Card>

  <Card title="Runs" icon="list-checks" href="/deploy/workflows/runs">
    Inspect queued, live, blocked, and failed runs after you publish.
  </Card>
</Columns>

<Card title="Run via API" icon="braces" href="/deploy/workflows/api">
  Start, inspect, cancel, or wake a published workflow with a workspace API key.
</Card>

## Create a workflow

<Steps>
  <Step title="Open Workflows">
    In the dashboard, open **Workflows** and select **Create workflow**.
  </Step>

  <Step title="Name it">
    Use a name that identifies the trigger and the call, for example `Call new qualified leads`. The empty editor starts as **Untitled workflow**.
  </Step>

  <Step title="Choose how it starts">
    On the start node, choose how this workflow runs:

    | Start | When it fires |
    | - | - |
    | **HTTP polling** | Persistence sends GET requests at the interval you set. New data in the JSON response starts a run. |
    | **CRM polling** | New or updated records from a connected CRM start a run. |
    | **Manual** | You start a run with input data when execution is available. |
    | **After workflow** | This workflow starts when a linked workflow in the series finishes. |
    | **Run via API** | After you publish, your system sends a POST to start a run. |

    Optional **Meta (optional)** is JSON available when the run starts. Use it for values that are not coming from a poll.
  </Step>

  <Step title="Add the first call">
    Add an **Action** node. Choose the published **Agent**, then **Dial from** a **Phone number** or a **Number pool**. The recipient comes from the mapped `phone_number` field.
  </Step>

  <Step title="Map input into the call">
    Add a **Mapper** if the trigger payload needs to become agent variables. Type two opening braces to insert fields.
  </Step>

  <Step title="Add branches, waits, and follow-ups">
    Use **Condition** to branch, **Delay** to wait, **Iterator** to repeat for each item in a list, and further **Action** nodes for retries or a second call.
  </Step>

  <Step title="Save draft, then publish">
    Select **Save draft**, then **Publish**. Polling and **Run via API** requests are rejected until the workflow is **Live**.
  </Step>
</Steps>

## HTTP polling

**HTTP polling** checks your endpoint with GET at the interval you set. New data in the JSON response starts a run.

<Steps>
  <Step title="Enter the endpoint">
    On the start node, choose **HTTP polling** and enter **Your endpoint URL**.
  </Step>

  <Step title="Set the interval">
    Choose how often Persistence should check. The editor summarizes it as **Every N minutes**.
  </Step>

  <Step title="Map the payload">
    Add a **Mapper** so `phone_number` and any agent variables come from the JSON.
  </Step>
</Steps>

Keep tokens in [workspace secrets](/accounts/secrets). The start node can also send a failure webhook when a run fails or is cancelled.

## CRM polling

**CRM polling** uses new or updated records from a connected CRM as the input for each call.

<Steps>
  <Step title="Connect the CRM">
    Connect the account under **Integrations** first. See [CRM](/integrations/crm).
  </Step>

  <Step title="Choose the record">
    On the start node, set **Connected account**, **Record type**, and **Trigger when**.
  </Step>

  <Step title="Choose the first poll">
    **Only changes after activation** skips existing records. **Include existing records** includes them on the first poll, then watches for the selected event.
  </Step>
</Steps>

Supported record labels include **Contact**, **Lead**, **Deal**, **Company**, **Ticket**, **Opportunity**, **Account**, and **Case**. The options you see depend on the connected CRM.

Trigger when: **New** record, record **updated**, or **New or updated**.

## Dial from a number or a pool

On each **Action** node:

| Dial from | Behavior |
| - | - |
| **Phone number** | Every call uses that caller ID. |
| **Number pool** | Calls rotate across members of one connection. |

Pool selection strategy:

| Strategy | Behavior |
| - | - |
| **Automatic** | Choose an available number from the pool. |
| **Even distribution** | Distribute calls evenly across the pool. |
| **Local area match** | Prefer a number matching the recipient's area code. |

Create the pool under **Number pools** first. A pool can only contain numbers from a single connection. See [Number pools](/deploy/number-pools).

## Call hours, skips, retries, and timeouts

Open **Action settings** on a call step to set:

* **Call hours** (for example `09:00-21:00` or a variable such as `store_hours`) and what to do **Outside these hours**: **Skip the call** (`call.skipped = true`) or **Wait and call when the hours open**.
* **Skip if called within (minutes)**, counted from **All workflows in this workspace** or **Only this workflow**. `0` turns this off.
* **Give up waiting for the call after (minutes)**. Counted from dialling, at least 10. `0` waits up to 24 hours. If the call never reports ending, the run fails and the workflow failure alert fires.
* Retries for **Busy / unanswered**, **Declined**, **Failed**, and **Voicemail / automated menu**, with exponential backoff.
* Optional **Before retrying**: refresh a GET **Data** step and only redial while rules still match. Otherwise the call continues as if no retry was set, with `call.retry_skipped = true`.

Set retries to 0 to disable a reason. Up to 3 retries per reason, excluding the initial call.

## Link another workflow

Use **Link another workflow** to run a second workflow after this one in the same series. Give the series a **Series name**. The later workflow's start mode is **After workflow**.

A common series is a first call, then a manager follow-up, then a support follow-up. Each later workflow starts when the previous one finishes, unless an **End** node stops the series.

**End** can **End this workflow** or **End this and all later workflows**.

You can **Reorder workflows** in the series. The first workflow is required.

## Run via API

After the workflow is published, open **Run via API**.

<Steps>
  <Step title="Create an API key">
    Create a workspace API key with the `workflow run` scope. Add `workflow read` if you will check status.
  </Step>

  <Step title="Send the start request">
    POST to `/v1/workflows/{workflowId}/run`. Send the key in the `X-API-Key` header and a unique `Idempotency-Key` per run.
  </Step>

  <Step title="Pass context">
    Put input in `context`. A `phone_number` field is then available to later steps as `{{phone_number}}`.
  </Step>

  <Step title="Optional concurrency">
    Add a `Concurrency-Key` (for example a store id) to allow only one active run per key. Other starts return 409. Optional `Cooldown-Minutes` refuses a new run for that key until this many minutes after the last run ended.
  </Step>
</Steps>

Publish first. The panel states that requests are rejected until the workflow is live.

See [Run via API](/deploy/workflows/api) and the [Workflows API](/api-reference/workflows/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.