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

# Build your first flow

> Browse the catalog, connect Five9, and build a multi-step flow that chains step outputs with expressions

This guide builds a real, multi-step flow against a live integration: it creates a contact list in Five9, then adds a contact to that list — with the second step reading the first step's output through an [expression](/concepts/expressions).

You'll go end to end: find an action in the [catalog](/concepts/catalog), store credentials as a [connection](/concepts/connections), author and activate a definition, trigger it, and inspect the results.

<Info>
  You'll need an API key (see [Authentication](/authentication)) and Five9 admin credentials. Export both:

  ```bash theme={null}
  export BASE_URL="https://api.getdialed.ai/flows"
  export GETDIALED_API_KEY="<your key>"
  ```
</Info>

<Steps>
  <Step title="Find your action in the catalog">
    The catalog is the public registry of platforms, services, and actions you can use in flows. No authentication required. Start from the platform and drill down:

    ```bash theme={null}
    curl "$BASE_URL/catalog/platforms"
    ```

    ```bash theme={null}
    curl "$BASE_URL/catalog/platforms/five9/services"
    ```

    ```bash theme={null}
    curl "$BASE_URL/catalog/services/five9__configuration_service/actions"
    ```

    The last call lists actions like:

    ```json theme={null}
    {
      "id": "five9__configuration_service__add_to_list",
      "platform_id": "five9",
      "service_id": "five9__configuration_service",
      "name": "Add to List",
      "description": "Add records to a Five9 contact list",
      "parameters": {
        "list_name": {
          "type": "string",
          "required": true,
          "description": "Name of the target contact list"
        }
      },
      "status": "active"
    }
    ```

    Each action's `parameters` describe what a task must pass, and `output_schema` describes what it returns. This flow uses two actions: `five9__configuration_service__create_list` and `five9__configuration_service__add_to_list`.
  </Step>

  <Step title="Create a connection">
    A [connection](/concepts/connections) stores the credentials a task uses to reach an external platform. Create one for the Five9 Configuration Service:

    ```bash theme={null}
    curl -X POST "$BASE_URL/connections" \
      -H "X-API-Key: $GETDIALED_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Five9 Production",
        "platform_id": "five9",
        "service_id": "five9__configuration_service",
        "auth_method": "basic_auth",
        "credentials_ref": "vault://five9/prod"
      }'
    ```

    The response is the created connection — note its `id`:

    ```json theme={null}
    {
      "id": "conn_e5f6g7h8",
      "name": "Five9 Production",
      "platform_id": "five9",
      "service_id": "five9__configuration_service",
      "auth_method": "basic_auth",
      "status": "active"
    }
    ```

    Verify the stored credentials actually work before building on them:

    ```bash theme={null}
    curl -X POST "$BASE_URL/connections/conn_e5f6g7h8/test" \
      -H "X-API-Key: $GETDIALED_API_KEY"
    ```

    A passing test returns `{"success": true, ...}` with the measured latency. A failing one returns `success: false` and an `error` code such as `AUTH_FAILED` — fix the credentials before continuing.
  </Step>

  <Step title="Author the definition">
    A [flow definition](/concepts/flow-definitions) declares **inputs**, then **steps** that run in order. Each step contains one or more **tasks**; every task names the catalog action it runs (`platform_id`, `service_id`, `action_id`) and the connection it runs with.

    This definition has two steps. Step 1 creates a Five9 list named after the trigger input. Step 2 adds a contact to it — and gets the list name from step 1's output, not from the input:

    ```bash theme={null}
    curl -X POST "$BASE_URL/definitions" \
      -H "X-API-Key: $GETDIALED_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Create list and add contact",
        "status": "active",
        "inputs": [
          {"name": "list_name", "type": "string", "required": true},
          {"name": "first_name", "type": "string", "required": true},
          {"name": "phone", "type": "string", "required": true}
        ],
        "steps": [
          {
            "step_id": "create_list",
            "name": "Create the contact list",
            "tasks": [
              {
                "task_id": "create_list_task",
                "task_name": "Create Five9 list",
                "platform_id": "five9",
                "service_id": "five9__configuration_service",
                "action_id": "five9__configuration_service__create_list",
                "connection_id": "conn_e5f6g7h8",
                "parameters": {
                  "list_name": "{{input.list_name}}"
                }
              }
            ]
          },
          {
            "step_id": "add_contact",
            "name": "Add the contact to the new list",
            "tasks": [
              {
                "task_id": "add_contact_task",
                "task_name": "Add contact to Five9 list",
                "platform_id": "five9",
                "service_id": "five9__configuration_service",
                "action_id": "five9__configuration_service__add_to_list",
                "connection_id": "conn_e5f6g7h8",
                "parameters": {
                  "list_name": "{{create_list.output.list_name}}",
                  "first_name": "{{input.first_name}}",
                  "number1": "{{input.phone}}"
                }
              }
            ]
          }
        ]
      }'
    ```

    Two kinds of expression are doing the work here:

    | Expression                         | Resolves to                                              |
    | ---------------------------------- | -------------------------------------------------------- |
    | `{{input.list_name}}`              | The `list_name` field of the trigger's `input_data`      |
    | `{{create_list.output.list_name}}` | The `list_name` field of the `create_list` step's output |

    Chaining follows the pattern `{{<step_id>.<task_id>.output.<field>}}`. When a step has exactly one task, the shorthand `{{<step_id>.output.<field>}}` also works — that's what's used above. See [Expressions](/concepts/expressions) for the full context reference.

    Expressions are validated when you create or update a definition — a malformed one is rejected immediately with a `422` and an `expression_errors` list, so you never discover a typo at runtime.

    The response includes the new definition's `id` (e.g. `def_a1b2c3d4`).
  </Step>

  <Step title="Activate it">
    Only definitions with `"status": "active"` can be triggered. The example above sets that at creation, so you're done.

    If you'd rather iterate on a draft first, omit `status` (new definitions default to `draft`) and activate later with `PUT /definitions/{id}` — a full replacement, so resend the whole body with `"status": "active"`.
  </Step>

  <Step title="Trigger it">
    ```bash theme={null}
    curl -X POST "$BASE_URL/definitions/def_a1b2c3d4/trigger" \
      -H "X-API-Key: $GETDIALED_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "input_data": {
          "list_name": "July leads",
          "first_name": "Alice",
          "phone": "5551234567"
        }
      }'
    ```

    ```json theme={null}
    {
      "job_id": "job_11223344",
      "definition_id": "def_a1b2c3d4",
      "status": "pending"
    }
    ```

    Every trigger creates a **job**; this one carries a single `input_data` object, so it produces one **execution**. To fan the same flow out over many records at once, see [Trigger with records](/guides/trigger-with-records).

    <Warning>
      Triggering a `draft` or `archived` definition returns `400` with `"Cannot trigger definition with status 'draft'. Only active definitions can be triggered."`
    </Warning>
  </Step>

  <Step title="Inspect the results">
    Follow the job until its status reaches `completed`:

    ```bash theme={null}
    curl "$BASE_URL/jobs/job_11223344" \
      -H "X-API-Key: $GETDIALED_API_KEY"
    ```

    Then list its executions and fetch the one execution's full detail:

    ```bash theme={null}
    curl "$BASE_URL/jobs/job_11223344/executions" \
      -H "X-API-Key: $GETDIALED_API_KEY"
    ```

    ```bash theme={null}
    curl "$BASE_URL/executions/exec_aabbccdd" \
      -H "X-API-Key: $GETDIALED_API_KEY"
    ```

    The execution's `result` is keyed by `step_id`, so you can see exactly what each step produced — including the `list_name` that step 2 consumed:

    ```json theme={null}
    {
      "id": "exec_aabbccdd",
      "job_id": "job_11223344",
      "status": "completed",
      "result": {
        "create_list": {
          "output": {"success": true, "list_name": "July leads"}
        },
        "add_contact": {
          "output": {"records_added": 1}
        }
      },
      "error": null
    }
    ```

    On failure, `status` is `failed` and `error` carries the message. See [Jobs and executions](/concepts/jobs-and-executions) for the full lifecycle.
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Trigger with records" icon="table" href="/guides/trigger-with-records">
    Run this flow once per row of a contact file.
  </Card>

  <Card title="Expressions" icon="code" href="/concepts/expressions">
    The full expression context: inputs, variables, step outputs, defaults.
  </Card>

  <Card title="Execution models" icon="clock" href="/concepts/execution-models">
    Run tasks immediately, on a schedule, in a time window, or on a signal.
  </Card>

  <Card title="Templates" icon="copy" href="/guides/templates">
    Start from a pre-built definition instead of a blank page.
  </Card>
</CardGroup>
