diff --git a/docs/bulk-actions.mdx b/docs/bulk-actions.mdx
index 04d0551c93..b40807cfe5 100644
--- a/docs/bulk-actions.mdx
+++ b/docs/bulk-actions.mdx
@@ -1,9 +1,15 @@
---
title: "Bulk actions"
-description: "Perform actions like replay and cancel on multiple runs at once."
+description: "Replay or cancel multiple runs from the dashboard using filters or selected run IDs."
---
-Bulk actions allow you to perform replaying and canceling on multiple runs at once. This is especially useful when you need to retry a batch of failed runs with a new version of your code, or when you need to cancel multiple in-progress runs.
+**Bulk actions let you replay or cancel multiple runs asynchronously from the dashboard.**
+
+Use bulk actions when you need to retry failed runs after deploying a fix, or stop a group of queued or executing runs.
+
+
+ For backend code, see [Bulk actions with the SDK](/runs/bulk-actions).
+
-## How to create a new bulk action
+## Create a bulk action in the dashboard
- Open the bulk action panel from the top right of the runs page
+
+
+ Open the runs page and click **Bulk action** in the top right.
-
+ 
+
+
+ Filter the runs table to target a group of runs, or select individual runs from the table.
+
- Filter the runs table to show the runs you want to bulk action
+
+ Choose **Replay** or **Cancel**, add an optional name, then confirm the action.
- Alternatively, you can select individual runs
+ 
+
- Choose the runs you want to bulk action
+
+ Open the bulk action page to see progress, view affected runs, or replay the action.
- Name your bulk action (optional)
+ 
+
+
- Choose the action you want to perform, replay or cancel
+
+ You can only cancel runs that are still cancelable, such as queued or executing runs. Runs that have already reached a final state cannot be canceled.
+
- Click the "Replay" or "Cancel" button and confirm in the dialog
-
+## Create a bulk action from the SDK
- You'll now view the bulk action processing from the bulk action page
+Use `runs.bulk.replay()` or `runs.bulk.cancel()` when you want to create a bulk action from your backend code.
- You can replay or view the runs from this page
+```ts Your backend code
+import { runs } from "@trigger.dev/sdk";
-
+const action = await runs.bulk.replay({
+ filter: {
+ status: "FAILED",
+ taskIdentifier: "sync-customer",
+ period: "24h",
+ },
+ name: "Replay failed customer syncs",
+});
-
- You can only cancel runs that are in states that allow cancellation (like QUEUED or EXECUTING).
- Runs that are already completed, failed, or in other final states by the time the bulk action process gets to them, cannot be canceled.
-
\ No newline at end of file
+const completed = await runs.bulk.poll(action.id);
+console.log(completed.status, completed.counts);
+```
+
+See [Bulk actions with the SDK](/runs/bulk-actions) for canceling, listing, polling, and aborting bulk actions from your backend code.
diff --git a/docs/docs.json b/docs/docs.json
index 571e725df4..ad5ac311e6 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -77,7 +77,8 @@
"run-usage",
"context",
"runs/priority",
- "hidden-tasks"
+ "hidden-tasks",
+ "runs/bulk-actions"
]
},
{
@@ -339,7 +340,6 @@
"management/runs/retrieve",
"management/runs/replay",
"management/runs/cancel",
- "management/runs/bulk-actions",
"management/runs/reschedule",
"management/runs/update-metadata",
"management/runs/add-tags",
@@ -348,6 +348,15 @@
"management/runs/retrieve-result"
]
},
+ {
+ "group": "Bulk actions API",
+ "pages": [
+ "management/bulk-actions/create",
+ "management/bulk-actions/list",
+ "management/bulk-actions/retrieve",
+ "management/bulk-actions/abort"
+ ]
+ },
{
"group": "Errors API",
"pages": [
@@ -751,6 +760,10 @@
"source": "/reattempting-replaying",
"destination": "/replaying"
},
+ {
+ "source": "/management/runs/bulk-actions",
+ "destination": "/runs/bulk-actions"
+ },
{
"source": "/tasks-overview",
"destination": "/tasks/overview"
diff --git a/docs/errors-retrying.mdx b/docs/errors-retrying.mdx
index ae6c4403f4..a55359085b 100644
--- a/docs/errors-retrying.mdx
+++ b/docs/errors-retrying.mdx
@@ -377,3 +377,7 @@ export const openaiTask = task({
},
});
```
+
+## Replay failed runs in bulk
+
+After you deploy a fix, use [bulk actions](/bulk-actions) to replay multiple failed runs from the dashboard, or [bulk actions with the SDK](/runs/bulk-actions) to replay them from backend code. Bulk replay creates an asynchronous action that targets selected run IDs or a `runs.list()` filter, so you can retry a known failure set without replaying each run individually.
diff --git a/docs/management/bulk-actions/abort.mdx b/docs/management/bulk-actions/abort.mdx
new file mode 100644
index 0000000000..d68d499083
--- /dev/null
+++ b/docs/management/bulk-actions/abort.mdx
@@ -0,0 +1,4 @@
+---
+title: "Abort bulk action"
+openapi: "v3-openapi POST /api/v1/bulk-actions/{bulkActionId}/abort"
+---
diff --git a/docs/management/bulk-actions/create.mdx b/docs/management/bulk-actions/create.mdx
new file mode 100644
index 0000000000..08caf1c2cf
--- /dev/null
+++ b/docs/management/bulk-actions/create.mdx
@@ -0,0 +1,4 @@
+---
+title: "Create bulk action"
+openapi: "v3-openapi POST /api/v1/bulk-actions"
+---
diff --git a/docs/management/bulk-actions/list.mdx b/docs/management/bulk-actions/list.mdx
new file mode 100644
index 0000000000..da2bedb714
--- /dev/null
+++ b/docs/management/bulk-actions/list.mdx
@@ -0,0 +1,4 @@
+---
+title: "List bulk actions"
+openapi: "v3-openapi GET /api/v1/bulk-actions"
+---
diff --git a/docs/management/bulk-actions/retrieve.mdx b/docs/management/bulk-actions/retrieve.mdx
new file mode 100644
index 0000000000..430bf82928
--- /dev/null
+++ b/docs/management/bulk-actions/retrieve.mdx
@@ -0,0 +1,4 @@
+---
+title: "Retrieve bulk action"
+openapi: "v3-openapi GET /api/v1/bulk-actions/{bulkActionId}"
+---
diff --git a/docs/management/runs/bulk-actions.mdx b/docs/runs/bulk-actions.mdx
similarity index 79%
rename from docs/management/runs/bulk-actions.mdx
rename to docs/runs/bulk-actions.mdx
index e326d7ddba..10a7caa90d 100644
--- a/docs/management/runs/bulk-actions.mdx
+++ b/docs/runs/bulk-actions.mdx
@@ -1,9 +1,9 @@
---
title: "Bulk actions"
-description: "Cancel or replay many runs from the SDK using run IDs or the same filters as runs.list()."
+description: "Cancel or replay many runs from the SDK using run IDs or SDK run-list filters."
---
-**Bulk actions let you cancel or replay many runs asynchronously from the SDK by selecting runs with run IDs or `runs.list()` filters.**
+**Bulk actions let you cancel or replay many runs asynchronously from the SDK by selecting runs with run IDs or SDK run-list filters.**
A bulk action returns a handle immediately. Use the handle to retrieve progress, poll until completion, list previous actions, or abort pending work.
@@ -28,18 +28,14 @@ const completed = await runs.bulk.poll(action.id);
console.log(completed.status, completed.counts);
```
-`filter` accepts the same filters as [`runs.list()`](/management/runs/list), excluding pagination fields. Provide at least one filter field; use `runIds` when you want to target specific runs. Relative time filters such as `period` are resolved when the bulk action is created, so later batches process the same fixed time range.
+`filter` accepts the SDK run-list filter fields, excluding pagination fields such as `limit`, `after`, and `before`. This is the TypeScript SDK shape used by `runs.list()` (`from`, `to`, and `period` are top-level fields), not the nested `filter[createdAt]` query shape shown in the raw HTTP list-runs endpoint. Provide at least one filter field; use `runIds` when you want to target specific runs. Relative time filters such as `period` are resolved when the bulk action is created, so later batches process the same fixed time range.
Filters inherit the same time semantics as `runs.list()`: when you don't pass `from`, `to`, or `period`, the action defaults to the **last 7 days** and won't target older matching runs. To cover a wider range, pass an explicit `period` (such as `"30d"`), a `from` timestamp, or a `from`/`to` pair. This default only applies to `filter` selections; `runIds` selections are never time-bounded.
-
- Each environment can only run a limited number of bulk replays at the same time. If you start a replay while too many are still in progress, the call fails and returns an error. Wait for an in-progress replay to finish (or [abort](#abort-a-bulk-action) one) before starting another. Bulk cancels are not subject to this limit.
-
-
- Selects runs using the same filter shape as `runs.list()`, excluding `limit`, `after`, and `before`.
+ Selects runs using the SDK run-list filter shape, excluding `limit`, `after`, and `before`. In raw HTTP requests these fields are sent as JSON body properties, so time fields are top-level (`from`, `to`, `period`) instead of nested under `createdAt`.
@@ -148,6 +144,10 @@ await runs.bulk.abort("bulk_1234");
Abort is best effort. Runs already being processed in the current batch may still finish.
+
+ Each environment can only run a limited number of bulk replays at the same time. If you start a replay while too many are still in progress, the call fails and returns an error. Wait for an in-progress replay to finish or abort one before starting another. Bulk cancels are not subject to this limit.
+
+
## List bulk actions
Use `runs.bulk.list()` to page through previous bulk actions in the current environment.
@@ -171,3 +171,12 @@ for await (const action of runs.bulk.list({ limit: 25 })) {
console.log(action.id, action.status);
}
```
+
+## API reference
+
+The SDK methods use the bulk actions HTTP API:
+
+- [Create bulk action](/management/bulk-actions/create)
+- [List bulk actions](/management/bulk-actions/list)
+- [Retrieve bulk action](/management/bulk-actions/retrieve)
+- [Abort bulk action](/management/bulk-actions/abort)
diff --git a/docs/v3-openapi.yaml b/docs/v3-openapi.yaml
index 7c6fd78c1d..4bf7ad3d26 100644
--- a/docs/v3-openapi.yaml
+++ b/docs/v3-openapi.yaml
@@ -1699,6 +1699,182 @@ paths:
console.log(`Run ID: ${run.id}, Status: ${run.status}`);
}
+
+ "/api/v1/bulk-actions":
+ get:
+ operationId: list_bulk_actions_v1
+ summary: List bulk actions
+ description: List bulk actions in the current environment. Bulk actions are returned newest first and can be paginated with cursor pagination.
+ parameters:
+ - $ref: "#/components/parameters/bulkActionsCursorPagination"
+ responses:
+ "200":
+ description: Successful request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ListBulkActionsResult"
+ "400":
+ description: Invalid query parameters
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ErrorResponse"
+ "401":
+ description: Unauthorized request
+ tags:
+ - bulk-actions
+ security:
+ - secretKey: []
+ x-codeSamples:
+ - lang: typescript
+ label: List bulk actions
+ source: |-
+ import { runs } from "@trigger.dev/sdk";
+
+ let page = await runs.bulk.list({ limit: 20 });
+
+ for (const action of page.data) {
+ console.log(action.id, action.type, action.status);
+ }
+
+ post:
+ operationId: create_bulk_action_v1
+ summary: Create bulk action
+ description: Create an asynchronous bulk action to cancel or replay runs selected by run IDs or filters.
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CreateBulkActionRequestBody"
+ responses:
+ "202":
+ description: Bulk action accepted
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/CreateBulkActionResponse"
+ "400":
+ description: Invalid request parameters or body
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ErrorResponse"
+ "401":
+ description: Unauthorized request
+ "422":
+ description: Unprocessable entity
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ErrorResponse"
+ "429":
+ description: Too many concurrent bulk replays
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ErrorResponse"
+ tags:
+ - bulk-actions
+ security:
+ - secretKey: []
+ x-codeSamples:
+ - lang: typescript
+ label: Replay matching runs
+ source: |-
+ import { runs } from "@trigger.dev/sdk";
+
+ const action = await runs.bulk.replay({
+ filter: {
+ status: "FAILED",
+ taskIdentifier: "sync-customer",
+ period: "24h",
+ },
+ name: "Replay failed customer syncs",
+ });
+
+ console.log(action.id);
+ - lang: typescript
+ label: Cancel selected runs
+ source: |-
+ import { runs } from "@trigger.dev/sdk";
+
+ const action = await runs.bulk.cancel({
+ runIds: ["run_1234", "run_5678"],
+ name: "Cancel selected runs",
+ });
+
+ console.log(action.id);
+
+ "/api/v1/bulk-actions/{bulkActionId}":
+ get:
+ operationId: retrieve_bulk_action_v1
+ summary: Retrieve bulk action
+ description: Retrieve the status and aggregate processing counts for a bulk action.
+ parameters:
+ - $ref: "#/components/parameters/bulkActionId"
+ responses:
+ "200":
+ description: Successful request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/BulkActionObject"
+ "401":
+ description: Unauthorized request
+ "404":
+ description: Resource not found
+ tags:
+ - bulk-actions
+ security:
+ - secretKey: []
+ x-codeSamples:
+ - lang: typescript
+ source: |-
+ import { runs } from "@trigger.dev/sdk";
+
+ const action = await runs.bulk.retrieve("bulk_1234");
+
+ console.log(action.status, action.counts);
+
+ "/api/v1/bulk-actions/{bulkActionId}/abort":
+ post:
+ operationId: abort_bulk_action_v1
+ summary: Abort bulk action
+ description: Abort a pending bulk action so it stops processing additional runs. Runs already processed by the action are not undone.
+ parameters:
+ - $ref: "#/components/parameters/bulkActionId"
+ responses:
+ "200":
+ description: Bulk action aborted
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/AbortBulkActionResponse"
+ "400":
+ description: Invalid request
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ErrorResponse"
+ "401":
+ description: Unauthorized request
+ "404":
+ description: Resource not found
+ tags:
+ - bulk-actions
+ security:
+ - secretKey: []
+ x-codeSamples:
+ - lang: typescript
+ source: |-
+ import { runs } from "@trigger.dev/sdk";
+
+ const aborted = await runs.bulk.abort("bulk_1234");
+
+ console.log(aborted.id);
+
"/api/v1/projects/{projectRef}/runs":
parameters:
- $ref: "#/components/parameters/projectRef"
@@ -3787,6 +3963,36 @@ components:
before:
type: string
description: The ID of the session to start the page before. Sets the pagination direction to backward.
+ bulkActionId:
+ in: path
+ name: bulkActionId
+ required: true
+ schema:
+ type: string
+ description: The ID of a bulk action, starts with `bulk_`.
+ example: bulk_1234
+ bulkActionsCursorPagination:
+ in: query
+ name: page
+ style: deepObject
+ explode: true
+ description: |
+ Use this parameter to paginate bulk actions. Pass `page[after]` or `page[before]` using the cursor returned in the previous response.
+ schema:
+ type: object
+ properties:
+ size:
+ type: integer
+ maximum: 100
+ minimum: 1
+ default: 25
+ description: Number of bulk actions per page. Maximum is 100.
+ after:
+ type: string
+ description: The cursor to start the page after.
+ before:
+ type: string
+ description: The cursor to start the page before.
errorId:
in: path
name: errorId
@@ -4499,6 +4705,263 @@ components:
ignoredUntilTotalOccurrences:
type: integer
nullable: true
+ BulkActionFilter:
+ type: object
+ description: Selects runs using SDK-style run-list filter fields, excluding pagination fields. In HTTP requests these fields are sent as JSON body properties, so time fields are top-level (`from`, `to`, `period`) instead of nested under `createdAt`. Provide at least one property.
+ minProperties: 1
+ properties:
+ status:
+ oneOf:
+ - type: string
+ enum:
+ - PENDING_VERSION
+ - QUEUED
+ - EXECUTING
+ - REATTEMPTING
+ - FROZEN
+ - COMPLETED
+ - CANCELED
+ - FAILED
+ - CRASHED
+ - INTERRUPTED
+ - SYSTEM_FAILURE
+ - type: array
+ items:
+ type: string
+ enum:
+ - PENDING_VERSION
+ - QUEUED
+ - EXECUTING
+ - REATTEMPTING
+ - FROZEN
+ - COMPLETED
+ - CANCELED
+ - FAILED
+ - CRASHED
+ - INTERRUPTED
+ - SYSTEM_FAILURE
+ taskIdentifier:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ description: The identifier of the task that was run.
+ version:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ description: The worker version that executed the run.
+ from:
+ oneOf:
+ - type: string
+ format: date-time
+ - type: number
+ description: Start of the time range as an ISO date string or Unix timestamp in milliseconds.
+ to:
+ oneOf:
+ - type: string
+ format: date-time
+ - type: number
+ description: End of the time range as an ISO date string or Unix timestamp in milliseconds.
+ period:
+ type: string
+ description: Relative time period to select, such as `24h` or `30d`.
+ example: 24h
+ bulkAction:
+ type: string
+ description: Select runs that were processed by another bulk action.
+ example: bulk_1234
+ tag:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ description: Select runs with one or more tags.
+ schedule:
+ type: string
+ description: Select runs created by a schedule.
+ example: sched_1234
+ isTest:
+ type: boolean
+ description: Select test or non-test runs.
+ batch:
+ type: string
+ description: Select runs in a batch.
+ example: batch_1234
+ queue:
+ oneOf:
+ - $ref: "#/components/schemas/QueueTypeName"
+ - type: array
+ items:
+ $ref: "#/components/schemas/QueueTypeName"
+ machine:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ description: Select runs by machine preset.
+ region:
+ oneOf:
+ - type: string
+ - type: array
+ items:
+ type: string
+ description: Select runs by region.
+ QueueTypeName:
+ type: object
+ required:
+ - type
+ - name
+ properties:
+ type:
+ type: string
+ enum:
+ - task
+ - custom
+ name:
+ type: string
+ CreateBulkActionRequestBody:
+ oneOf:
+ - type: object
+ required:
+ - action
+ properties:
+ action:
+ type: string
+ enum:
+ - cancel
+ filter:
+ $ref: "#/components/schemas/BulkActionFilter"
+ runIds:
+ type: array
+ minItems: 1
+ maxItems: 500
+ items:
+ type: string
+ name:
+ type: string
+ maxLength: 255
+ oneOf:
+ - required:
+ - filter
+ - required:
+ - runIds
+ - type: object
+ required:
+ - action
+ properties:
+ action:
+ type: string
+ enum:
+ - replay
+ filter:
+ $ref: "#/components/schemas/BulkActionFilter"
+ runIds:
+ type: array
+ minItems: 1
+ maxItems: 500
+ items:
+ type: string
+ name:
+ type: string
+ maxLength: 255
+ targetRegion:
+ type: string
+ description: Region identifier to replay runs in. When omitted, each replay keeps the original run's region.
+ oneOf:
+ - required:
+ - filter
+ - required:
+ - runIds
+ BulkActionObject:
+ type: object
+ required:
+ - id
+ - type
+ - status
+ - counts
+ - createdAt
+ properties:
+ id:
+ type: string
+ description: The bulk action ID, prefixed with `bulk_`.
+ example: bulk_1234
+ name:
+ type: string
+ description: The name provided when the bulk action was created.
+ type:
+ type: string
+ enum:
+ - CANCEL
+ - REPLAY
+ status:
+ type: string
+ enum:
+ - PENDING
+ - COMPLETED
+ - ABORTED
+ counts:
+ type: object
+ required:
+ - total
+ - success
+ - failure
+ properties:
+ total:
+ type: integer
+ description: The number of runs selected when the bulk action was created.
+ success:
+ type: integer
+ description: The number of runs processed successfully.
+ failure:
+ type: integer
+ description: The number of runs that could not be processed.
+ createdAt:
+ type: string
+ format: date-time
+ completedAt:
+ type: string
+ format: date-time
+ CreateBulkActionResponse:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ example: bulk_1234
+ AbortBulkActionResponse:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: string
+ example: bulk_1234
+ ListBulkActionsResult:
+ type: object
+ required:
+ - data
+ - pagination
+ properties:
+ data:
+ type: array
+ items:
+ $ref: "#/components/schemas/BulkActionObject"
+ pagination:
+ type: object
+ properties:
+ next:
+ type: string
+ description: Pass this cursor as `page[after]` to retrieve the next page.
+ previous:
+ type: string
+ description: Pass this cursor as `page[before]` to retrieve the previous page.
ListRunsResult:
type: object
properties: