# Trigger workflows programmatically Run a workflow on demand from the Dashboard or via the API. By default, workflows trigger on Stripe events: when a payment succeeds, a subscription renews, or a dispute is created. Programmatic triggers let you start a workflow on demand, either from the Dashboard or through an API call, with custom input data. Use programmatic triggers when: - A human needs to initiate a process (support agent processing a refund, ops team running a bulk update) - An external system needs to invoke Stripe logic (your backend triggering a workflow after an internal event) - You want to test a workflow with specific input data ## When to use programmatic vs. event-based triggers A workflow uses one trigger type — either event-based or programmatic, not both. If you need the same workflow logic to run both automatically (on events) and on demand, create separate workflows. |   | **Event-based triggers** | **Programmatic triggers** | | --------------- | -------------------------------------- | -------------------------------------------- | | **Starts when** | A Stripe event fires | A user or API call initiates it | | **Input data** | The event payload | Custom JSON you provide | | **Use case** | Automated reactions to Stripe activity | On-demand processes, human-initiated actions | | **Volume** | One run per matching event | One run per invocation | ## Set up an on-demand trigger To configure a workflow with a programmatic trigger: 1. [Create a workflow](https://docs.stripe.com/workflows/set-up.md) or open an existing one in the Dashboard. 1. Click **Add a trigger**, then select **Tools**. 1. Choose **On-demand trigger**. 1. Define your input fields (optional). See [Input fields](https://docs.stripe.com/workflows/programmatic-triggers.md#input-fields) for supported types. 1. Publish the workflow. ## Trigger from the Dashboard You can trigger a workflow from the Dashboard without writing code. 1. Open the workflow in the Dashboard. 1. Click **Run workflow**. 1. Enter the input data. The input must conform to the workflow’s defined input fields. 1. Click **Run workflow**. The workflow starts immediately. The run appears in **Recent runs** and run history shows the input data you provided. ## Invoke a trigger through the API Trigger a workflow programmatically by calling the invoke endpoint with the workflow ID and input fields (if configured). > Only workflows with a manual trigger and an `active` status can be invoked via API. Attempting to invoke a draft, inactive, or archived workflow returns an error. ### Create a workflow run ```bash curl https://api.stripe.com/v2/extend/workflows/{id}/invoke \ -X POST \ -H "Authorization: Bearer {{API_SECRET_KEY}}" \ -H "Stripe-Version: {{API_VERSION}}" \ -H "Content-Type: application/json" \ -d '{ "input_parameters": { "charge_id": "ch_1234567890", "reason": "customer_request" } }' ``` The API returns immediately after accepting the request — it doesn’t wait for the workflow to complete. The run may be queued briefly before execution begins. To track progress, [retrieve the run](https://docs.stripe.com/workflows/programmatic-triggers.md#retrieve-a-workflow-run) or listen for [webhook events](https://docs.stripe.com/workflows/programmatic-triggers.md#webhook-events). **Response:** ```json { "id": "wfrun_abc123...", "object": "v2.extend.workflow_run", "livemode": false, "workflow": "wf_XYZ789...", "status": "started", "trigger": { "type": "manual", "manual": { "input_parameters": { "charge_id": "ch_1234567890", "reason": "customer_request" } } }, "created": "2026-03-19T14:05:00.000+0000", "status_details": { "started": {} }, "status_transitions": { "started_at": "2026-03-19T14:05:00.000+0000" } } ``` ### Retrieve a workflow run Use the run ID from the invoke response to check the status of a run. ```bash curl https://api.stripe.com/v2/extend/workflow_runs/{id} \ -H "Authorization: Bearer {{API_SECRET_KEY}}" \ -H "Stripe-Version: {{API_VERSION}}" ``` The response includes the current status (`started`, `succeeded`, or `failed`) and details about the outcome. ```json { "id": "wfrun_abc123...", "object": "v2.extend.workflow_run", "livemode": false, "workflow": "wf_XYZ789...", "status": "succeeded", "trigger": { "type": "manual", "manual": { "input_parameters": { "charge_id": "ch_1234567890", "reason": "customer_request" } } }, "created": "2026-03-19T14:05:00.000+0000", "status_details": { "succeeded": {} }, "status_transitions": { "started_at": "2026-03-19T14:05:00.000+0000", "succeeded_at": "2026-03-19T14:15:00.000+0000" } } ``` If a run fails, `status_details.failed` includes the error message: ```json { "status": "failed", "status_details": { "failed": { "error_message": "During API call to stripe.api_call_post_v1_customers: Field \"name\" is required." } }, "status_transitions": { "started_at": "2026-03-19T14:05:00.000+0000", "failed_at": "2026-03-19T14:10:00.000+0000" } } ``` ### List workflow runs List runs across all workflows, or filter by workflow ID or status. ```bash curl "https://api.stripe.com/v2/extend/workflow_runs?workflow=wf_XYZ789&status=failed" \ -H "Authorization: Bearer {{API_SECRET_KEY}}" \ -H "Stripe-Version: {{API_VERSION}}" ``` The list endpoint returns runs in descending creation order. You can filter by: - `workflow` — one or more workflow IDs - `status` — one or more of `started`, `succeeded`, `failed` > The list endpoint omits `trigger.manual.input_parameters` and `status_details` for performance. Use the [retrieve endpoint](https://docs.stripe.com/workflows/programmatic-triggers.md#retrieve-a-workflow-run) to get full run details. ### Idempotency keys Include an `Idempotency-Key` header to prevent duplicate runs. If a request with the same idempotency key has already created a run, the API returns the original response. If the key matches but the input differs, the API returns an error. ```bash curl https://api.stripe.com/v2/extend/workflows/{id}/invoke \ -X POST \ -H "Authorization: Bearer {{API_SECRET_KEY}}" \ -H "Stripe-Version: {{API_VERSION}}" \ -H "Idempotency-Key: refund-ch_1234567890-20260308" \ -H "Content-Type: application/json" \ -d '{ "input_parameters": { "charge_id": "ch_1234567890" } }' ``` ### Error responses | **Status** | **Code** | **Meaning** | | ---------- | ----------------------------------- | ------------------------------------------------------------------------ | | 400 | `invalid_workflow_input_parameters` | One or more input field values failed validation. | | 400 | `workflow_not_invokable` | The workflow doesn’t have a manual trigger, or isn’t in an active state. | | 401 | — | Invalid or expired API key. | | 404 | `not_found` | Workflow ID not found. | ## Webhook events Stripe emits webhook events when workflow runs change status. You can listen for these events to trigger downstream actions or monitor run outcomes without polling. These events fire for both API-invoked and event-triggered workflow runs. | **Event** | **Fires when** | **Notes** | | ---------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `v2.extend.workflow_run.started` | A workflow run begins executing | Fires for both API-invoked and event-triggered runs. | | `v2.extend.workflow_run.succeeded` | A workflow run completes successfully | Output values are not included (they can contain PII). | | `v2.extend.workflow_run.failed` | A workflow run fails | Pull model includes failure details (`error_message`, `dashboard_url`). Push model omits error details (PII risk). | ## Input fields Define the input your workflow expects by configuring input fields on the on-demand trigger in the Dashboard. Input fields specify which fields are required, their types, and descriptions. ### Supported input types | **Input type** | **Description** | | ---------------------- | ----------------------------------------- | | Text (`string`) | Text values (IDs, names, email addresses) | | Number (`integer`) | Numeric values (amounts, counts) | | Decimal (`decimal`) | Decimal numbers (prices, percentages) | | True/false (`boolean`) | True/false flags | | Enum (`enum`) | A predefined set of allowed values | | Array (`array`) | Lists of values | | Empty payload | The workflow accepts no input fields | > In public preview, input field schemas are not exposed in the API response when retrieving a workflow configuration. You can view and configure input fields in the Dashboard. ### Using Stripe object IDs in input fields If your input includes Stripe object IDs (for example, `charge_id: "ch_123"` or `customer_id: "cus_456"`), you can add a retrieve action in your workflow to fetch the full object. For example, pass a `charge_id` as an input field, then add a **Retrieve a charge** action that uses that ID. The full object’s fields become available in subsequent workflow steps, the same way event payload fields are available in event-triggered workflows. ## Limitations - **One trigger type per workflow.** A workflow uses either event-based triggers or programmatic triggers, not both. - **No scheduled or recurring triggers.** Programmatic triggers are on-demand only. There’s no cron, interval, or calendar-based scheduling. - **No webhook ingestion.** Workflows can’t accept arbitrary HTTP POST requests as a generic webhook endpoint. Use the invoke API with the workflow ID. - **Asynchronous only.** The API returns immediately after starting the run. There’s no synchronous mode that waits for workflow completion. Use the [retrieve endpoint](https://docs.stripe.com/workflows/programmatic-triggers.md#retrieve-a-workflow-run) or [webhook events](https://docs.stripe.com/workflows/programmatic-triggers.md#webhook-events) to track outcomes. - **No third-party event triggers.** You can’t trigger workflows directly from non-Stripe events. Use the invoke API from your backend to bridge external events into Workflows. - **Event-triggered workflows can’t be invoked via API.** Only workflows configured with a manual trigger support API invocation. ## Permissions Programmatic invocation requires the workflow invoke permission, which is included in the default Workflows role. You can scope API keys to include or exclude this permission. ## See also - [Workflows](https://docs.stripe.com/workflows/define-workflows.md) - [Set up workflows](https://docs.stripe.com/workflows/set-up.md) - [Use cases](https://docs.stripe.com/workflows/use-cases.md) - [Loop over collections](https://docs.stripe.com/workflows/loops.md) - [Create custom actions](https://docs.stripe.com/workflows/custom-actions.md)