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

# Workflows

> Automate actions triggered by form events using the Workflows API.

A workflow defines what happens automatically when a form event occurs — such as sending a confirmation email when a submission arrives, or posting to a Slack channel when a specific answer is submitted. Each workflow has a trigger that specifies when it fires and an ordered list of actions to execute.

***

## List workflows

`GET /workflows`

Returns a paginated list of workflows in your account.

### Query parameters

<ParamField query="limit" type="integer" default="20">
  Number of workflows to return per page. Maximum `100`.
</ParamField>

<ParamField query="cursor" type="string">
  Pagination cursor from a previous response's `next_cursor`.
</ParamField>

<ParamField query="form_id" type="string">
  Filter to workflows that are attached to a specific form.
</ParamField>

### Response

<ResponseField name="data" type="object[]">
  Array of workflow objects.

  <Expandable title="Workflow object properties">
    <ResponseField name="id" type="string">
      Unique workflow identifier, prefixed with `wfl_`.
    </ResponseField>

    <ResponseField name="name" type="string">
      Display name of the workflow.
    </ResponseField>

    <ResponseField name="form_id" type="string">
      The form this workflow is attached to.
    </ResponseField>

    <ResponseField name="enabled" type="boolean">
      Whether the workflow is currently active.
    </ResponseField>

    <ResponseField name="trigger" type="object">
      The event that starts the workflow.
    </ResponseField>

    <ResponseField name="actions" type="object[]">
      Ordered list of actions to execute.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      ISO 8601 creation timestamp.
    </ResponseField>

    <ResponseField name="updated_at" type="string">
      ISO 8601 timestamp of the last update.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="next_cursor" type="string">
  Cursor for the next page. `null` on the last page.
</ResponseField>

<ResponseField name="has_more" type="boolean">
  `true` if more workflows exist beyond this page.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null}
  curl --request GET \
    --url "https://api.formflows.ai/v1/workflows?form_id=frm_8kQmP2xNvL" \
    --header "Authorization: Bearer sk_live_your_api_key"
  ```

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

  response = requests.get(
      "https://api.formflows.ai/v1/workflows",
      headers={"Authorization": "Bearer sk_live_your_api_key"},
      params={"form_id": "frm_8kQmP2xNvL"},
  )

  result = response.json()
  for wf in result["data"]:
      status = "enabled" if wf["enabled"] else "disabled"
      print(f"{wf['name']} ({status})")
  ```
</CodeGroup>

***

## Create a workflow

`POST /workflows`

Creates a new workflow attached to a form.

### Body parameters

<ParamField body="name" type="string" required>
  Display name for the workflow.
</ParamField>

<ParamField body="form_id" type="string" required>
  The ID of the form that this workflow is attached to.
</ParamField>

<ParamField body="trigger" type="object" required>
  Defines the event that starts the workflow.

  <Expandable title="Trigger properties">
    <ParamField body="type" type="string" required>
      The trigger event type. Supported values: `submission.created`, `submission.updated`.
    </ParamField>

    <ParamField body="config" type="object">
      Optional configuration for the trigger. For example, filter conditions that must match before the workflow fires.

      <Expandable title="Config properties">
        <ParamField body="conditions" type="object[]">
          Array of conditions. Each condition has a `field_id`, `operator` (`equals`, `not_equals`, `contains`, `greater_than`, `less_than`), and `value`. All conditions must match for the workflow to fire.
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="actions" type="object[]" required>
  Ordered list of actions to execute when the trigger fires.

  <Expandable title="Action properties">
    <ParamField body="type" type="string" required>
      Action type. Supported values: `send_email`, `send_webhook`, `slack_message`, `google_sheets_append`.
    </ParamField>

    <ParamField body="config" type="object" required>
      Action-specific configuration. See action type reference below.
    </ParamField>
  </Expandable>
</ParamField>

### Action type reference

<AccordionGroup>
  <Accordion title="send_email">
    Sends an email when the workflow fires.

    | Config key  | Type   | Required | Description                                                    |
    | ----------- | ------ | -------- | -------------------------------------------------------------- |
    | `to`        | string | Yes      | Recipient email. Supports `{{field_id}}` template variables.   |
    | `subject`   | string | Yes      | Email subject line. Supports template variables.               |
    | `body`      | string | Yes      | Email body in plain text or HTML. Supports template variables. |
    | `from_name` | string | No       | Sender display name. Defaults to your account name.            |
  </Accordion>

  <Accordion title="send_webhook">
    Sends an HTTP POST request to a URL with the submission data as the body.

    | Config key | Type   | Required | Description                         |
    | ---------- | ------ | -------- | ----------------------------------- |
    | `url`      | string | Yes      | Destination URL. Must use HTTPS.    |
    | `headers`  | object | No       | Additional HTTP headers to include. |
  </Accordion>

  <Accordion title="slack_message">
    Posts a message to a Slack channel. Requires a connected Slack integration.

    | Config key | Type   | Required | Description                                      |
    | ---------- | ------ | -------- | ------------------------------------------------ |
    | `channel`  | string | Yes      | Slack channel ID or name (e.g., `#sales-leads`). |
    | `message`  | string | Yes      | Message text. Supports template variables.       |
  </Accordion>

  <Accordion title="google_sheets_append">
    Appends a new row to a Google Sheet. Requires a connected Google integration.

    | Config key       | Type   | Required | Description                                          |
    | ---------------- | ------ | -------- | ---------------------------------------------------- |
    | `spreadsheet_id` | string | Yes      | Google Sheets spreadsheet ID.                        |
    | `sheet_name`     | string | Yes      | Tab name within the spreadsheet.                     |
    | `row_mapping`    | object | Yes      | Map of column headers to field IDs or static values. |
  </Accordion>
</AccordionGroup>

### Response

Returns the created workflow object with a `201 Created` status.

<CodeGroup>
  ```bash curl theme={null}
  curl --request POST \
    --url "https://api.formflows.ai/v1/workflows" \
    --header "Authorization: Bearer sk_live_your_api_key" \
    --header "Content-Type: application/json" \
    --data '{
      "name": "New registration — email confirmation",
      "form_id": "frm_8kQmP2xNvL",
      "trigger": {
        "type": "submission.created"
      },
      "actions": [
        {
          "type": "send_email",
          "config": {
            "to": "{{fld_e5F6g7H8}}",
            "subject": "You are registered for the 2025 Annual Conference",
            "body": "Hi {{fld_a1B2c3D4}},\n\nThank you for registering. We look forward to seeing you!\n\nThe Events Team"
          }
        },
        {
          "type": "slack_message",
          "config": {
            "channel": "#event-registrations",
            "message": "New registration from {{fld_a1B2c3D4}} ({{fld_e5F6g7H8}})"
          }
        }
      ]
    }'
  ```

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

  payload = {
      "name": "New registration — email confirmation",
      "form_id": "frm_8kQmP2xNvL",
      "trigger": {
          "type": "submission.created",
      },
      "actions": [
          {
              "type": "send_email",
              "config": {
                  "to": "{{fld_e5F6g7H8}}",
                  "subject": "You are registered for the 2025 Annual Conference",
                  "body": (
                      "Hi {{fld_a1B2c3D4}},\n\n"
                      "Thank you for registering. We look forward to seeing you!\n\n"
                      "The Events Team"
                  ),
              },
          },
          {
              "type": "slack_message",
              "config": {
                  "channel": "#event-registrations",
                  "message": "New registration from {{fld_a1B2c3D4}} ({{fld_e5F6g7H8}})",
              },
          },
      ],
  }

  response = requests.post(
      "https://api.formflows.ai/v1/workflows",
      headers={"Authorization": "Bearer sk_live_your_api_key"},
      json=payload,
  )

  workflow = response.json()
  print("Created workflow:", workflow["id"])
  ```
</CodeGroup>

**Example response:**

```json theme={null}
{
  "id": "wfl_4nRsT6uVwX",
  "name": "New registration — email confirmation",
  "form_id": "frm_8kQmP2xNvL",
  "enabled": true,
  "trigger": {
    "type": "submission.created",
    "config": {}
  },
  "actions": [
    {
      "id": "act_yZ0aB1cD2e",
      "type": "send_email",
      "config": {
        "to": "{{fld_e5F6g7H8}}",
        "subject": "You are registered for the 2025 Annual Conference",
        "body": "Hi {{fld_a1B2c3D4}},\n\nThank you for registering. We look forward to seeing you!\n\nThe Events Team"
      }
    },
    {
      "id": "act_fG3hI4jK5l",
      "type": "slack_message",
      "config": {
        "channel": "#event-registrations",
        "message": "New registration from {{fld_a1B2c3D4}} ({{fld_e5F6g7H8}})"
      }
    }
  ],
  "created_at": "2025-01-15T11:30:00Z",
  "updated_at": "2025-01-15T11:30:00Z"
}
```

***

## Get a workflow

`GET /workflows/{id}`

Returns a single workflow by ID, including its full trigger and actions configuration.

### Path parameters

<ParamField path="id" type="string" required>
  The workflow ID, e.g. `wfl_4nRsT6uVwX`.
</ParamField>

### Response

Returns the full workflow object.

<CodeGroup>
  ```bash curl theme={null}
  curl --request GET \
    --url "https://api.formflows.ai/v1/workflows/wfl_4nRsT6uVwX" \
    --header "Authorization: Bearer sk_live_your_api_key"
  ```

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

  response = requests.get(
      "https://api.formflows.ai/v1/workflows/wfl_4nRsT6uVwX",
      headers={"Authorization": "Bearer sk_live_your_api_key"},
  )

  workflow = response.json()
  print(f"Workflow '{workflow['name']}' has {len(workflow['actions'])} action(s).")
  ```
</CodeGroup>

***

## Update a workflow

`PATCH /workflows/{id}`

Updates a workflow. Use this to enable or disable a workflow, rename it, or modify its trigger and actions. Only the fields you include are changed.

### Path parameters

<ParamField path="id" type="string" required>
  The workflow ID to update.
</ParamField>

### Body parameters

<ParamField body="name" type="string">
  New display name.
</ParamField>

<ParamField body="enabled" type="boolean">
  Set to `false` to pause the workflow without deleting it. Set to `true` to re-enable it.
</ParamField>

<ParamField body="trigger" type="object">
  Replaces the workflow trigger configuration.
</ParamField>

<ParamField body="actions" type="object[]">
  Replaces the entire actions array. Include existing action IDs to preserve them.
</ParamField>

### Response

Returns the updated workflow object.

<CodeGroup>
  ```bash curl theme={null}
  curl --request PATCH \
    --url "https://api.formflows.ai/v1/workflows/wfl_4nRsT6uVwX" \
    --header "Authorization: Bearer sk_live_your_api_key" \
    --header "Content-Type: application/json" \
    --data '{"enabled": false}'
  ```

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

  response = requests.patch(
      "https://api.formflows.ai/v1/workflows/wfl_4nRsT6uVwX",
      headers={"Authorization": "Bearer sk_live_your_api_key"},
      json={"enabled": False},
  )

  workflow = response.json()
  print("Enabled:", workflow["enabled"])
  ```
</CodeGroup>

***

## Delete a workflow

`DELETE /workflows/{id}`

Permanently deletes a workflow. In-progress executions are allowed to finish before the workflow is removed.

### Path parameters

<ParamField path="id" type="string" required>
  The workflow ID to delete.
</ParamField>

### Response

Returns a `204 No Content` response on success.

<CodeGroup>
  ```bash curl theme={null}
  curl --request DELETE \
    --url "https://api.formflows.ai/v1/workflows/wfl_4nRsT6uVwX" \
    --header "Authorization: Bearer sk_live_your_api_key"
  ```

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

  response = requests.delete(
      "https://api.formflows.ai/v1/workflows/wfl_4nRsT6uVwX",
      headers={"Authorization": "Bearer sk_live_your_api_key"},
  )

  if response.status_code == 204:
      print("Workflow deleted.")
  ```
</CodeGroup>
