From 2ade7519642c30fb9bc6d7db14fa710f53b4e74d Mon Sep 17 00:00:00 2001 From: alitariksahin Date: Fri, 6 Feb 2026 16:43:37 +0300 Subject: [PATCH 1/5] feat: add new bulk action methods to qstash and workflow --- docs.json | 1 + qstash/sdks/ts/examples/dlq.mdx | 28 ++++++++++++++ qstash/sdks/ts/examples/messages.mdx | 27 +++++++++++++ workflow/basics/client.mdx | 1 + workflow/basics/client/cancel.mdx | 55 +++++++++++++++++++++++++-- workflow/basics/client/dlq/delete.mdx | 47 +++++++++++++++++++++++ 6 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 workflow/basics/client/dlq/delete.mdx diff --git a/docs.json b/docs.json index 8020cce5..950f7d73 100644 --- a/docs.json +++ b/docs.json @@ -1190,6 +1190,7 @@ "group": "client.dlq", "pages": [ "workflow/basics/client/dlq/list", + "workflow/basics/client/dlq/delete", "workflow/basics/client/dlq/restart", "workflow/basics/client/dlq/resume", "workflow/basics/client/dlq/callback" diff --git a/qstash/sdks/ts/examples/dlq.mdx b/qstash/sdks/ts/examples/dlq.mdx index 8670392c..740a4f8b 100644 --- a/qstash/sdks/ts/examples/dlq.mdx +++ b/qstash/sdks/ts/examples/dlq.mdx @@ -33,3 +33,31 @@ const client = new Client({ token: "" }); const dlq = client.dlq; await dlq.delete("dlqId"); ``` + +#### Delete multiple messages from the DLQ + +```typescript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +const result = await client.dlq.deleteMany({ + dlqIds: ["dlqId-1", "dlqId-2", "dlqId-3"], +}); + +console.log(result.deleted); // number of deleted messages +``` + +#### Retry multiple messages from the DLQ + +```typescript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +const result = await client.dlq.retry({ + dlqIds: ["dlqId-1", "dlqId-2"], +}); + +console.log(result.responses); // [{ messageId: "..." }, ...] +``` diff --git a/qstash/sdks/ts/examples/messages.mdx b/qstash/sdks/ts/examples/messages.mdx index 4ef72e5c..b2ab462d 100644 --- a/qstash/sdks/ts/examples/messages.mdx +++ b/qstash/sdks/ts/examples/messages.mdx @@ -45,3 +45,30 @@ await client.messages.deleteMany([ // deleting all messages await client.messages.deleteAll() ``` + +#### Cancel messages with filters + +Cancel all messages matching specific filters such as `flowControlKey`, `url`, `queueName`, and more. + +```typescript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +// cancel all messages with a specific flow control key +const cancelled = await client.messages.deleteAll({ + flowControlKey: "my-flow-key", +}); + +console.log(cancelled); // number of cancelled messages + +// cancel with multiple filters +const cancelled2 = await client.messages.deleteAll({ + flowControlKey: "my-flow-key", + url: "https://example.com", + queueName: "my-queue", + label: "my-label", + fromDate: "1640995200000", + toDate: "1672531200000", +}); +``` diff --git a/workflow/basics/client.mdx b/workflow/basics/client.mdx index 2760e409..ccdf699e 100644 --- a/workflow/basics/client.mdx +++ b/workflow/basics/client.mdx @@ -33,6 +33,7 @@ The client exposes a set of functions to manage workflow runs and inspect their - [client.getWaiters](/workflow/basics/client/waiters) - client.dlq - [client.dlq.list](/workflow/basics/client/dlq/list) + - [client.dlq.delete](/workflow/basics/client/dlq/delete) - [client.dlq.restart](/workflow/basics/client/dlq/restart) - [client.dlq.resume](/workflow/basics/client/dlq/resume) - [client.dlq.retryFailureFunction](/workflow/basics/client/dlq/callback) \ No newline at end of file diff --git a/workflow/basics/client/cancel.mdx b/workflow/basics/client/cancel.mdx index 87bdcda3..5d1f715b 100644 --- a/workflow/basics/client/cancel.mdx +++ b/workflow/basics/client/cancel.mdx @@ -6,16 +6,35 @@ There are multiple ways you can cancel workflow runs: - Pass one or more workflow run ids to cancel them - Pass a workflow url to cancel all runs starting with this url -- cancel all pending or active workflow runs +- Pass filters to cancel matching workflow runs +- Cancel all pending or active workflow runs ## Arguments - - The set of workflow run IDs you want to cancel + + The set of workflow run IDs you want to cancel. - The URL address you want to filter while canceling + The URL address you want to filter while canceling. + + + + Cancel workflow runs matching the provided filters. + + + + Cancel workflows with this label. + + + + Cancel workflows created after this date (Unix ms as string). + + + + Cancel workflows created before this date (Unix ms as string). + + @@ -46,6 +65,34 @@ Note that this will cancel workflows in all endpoints under await client.cancel({ urlStartingWith: "https://your-endpoint.com" }); ``` +### Cancel workflow runs with filters + +Cancel all active workflows that match the provided filters: + +```ts +// cancel workflows with a specific label +await client.cancel({ + filters: { label: "my-workflow-label" }, +}); + +// cancel workflows within a date range +await client.cancel({ + filters: { + fromDate: "1640995200000", + toDate: "1672531200000", + }, +}); + +// combine filters +await client.cancel({ + filters: { + label: "my-workflow-label", + fromDate: "1640995200000", + toDate: "1672531200000", + }, +}); +``` + ### Cancel _all_ workflows To cancel all pending and currently running workflows, you can diff --git a/workflow/basics/client/dlq/delete.mdx b/workflow/basics/client/dlq/delete.mdx new file mode 100644 index 00000000..600a24c9 --- /dev/null +++ b/workflow/basics/client/dlq/delete.mdx @@ -0,0 +1,47 @@ +--- +title: "client.dlq.delete" +--- + +The `dlq.delete` method removes one or more messages from the **Dead Letter Queue (DLQ)**. + +## Arguments + + + A single DLQ entry ID or an array of IDs to delete. Use the `dlqId` field from + messages returned by `client.dlq.list()`. + + +## Response + + + The number of messages successfully deleted from the DLQ. + + +## Usage + + +```ts Single +import { Client } from "@upstash/workflow"; + +const client = new Client({ token: "" }); + +const result = await client.dlq.delete({ +dlqIds: "dlq-12345", +}); + +console.log(result.deleted); // 1 + +```` +```ts Multiple +import { Client } from "@upstash/workflow"; + +const client = new Client({ token: "" }); + +const result = await client.dlq.delete({ + dlqIds: ["dlq-12345", "dlq-67890", "dlq-11111"], +}); + +console.log(result.deleted); // 3 +```` + + From bca3886c30ba2034be86694123265e66e4762500 Mon Sep 17 00:00:00 2001 From: alitariksahin Date: Tue, 10 Feb 2026 10:07:28 +0300 Subject: [PATCH 2/5] fix: reviews --- docs.json | 31 +++++++-- qstash/sdks/ts/examples/dlq.mdx | 62 ++++++++++++++++-- qstash/sdks/ts/examples/messages.mdx | 24 ++++--- workflow/basics/client/dlq/delete.mdx | 87 ++++++++++++++++++-------- workflow/basics/client/dlq/restart.mdx | 54 +++++++++++++--- workflow/basics/client/dlq/resume.mdx | 54 +++++++++++++--- 6 files changed, 250 insertions(+), 62 deletions(-) diff --git a/docs.json b/docs.json index 950f7d73..521e0b2e 100644 --- a/docs.json +++ b/docs.json @@ -155,6 +155,12 @@ "redis/sdks/ts/commands/auth/ping" ] }, + { + "group": "Connection", + "pages": [ + "redis/sdks/ts/commands/connection/client_setinfo" + ] + }, { "group": "Bitmap", "pages": [ @@ -209,6 +215,8 @@ "redis/sdks/ts/commands/hash/hexpiretime", "redis/sdks/ts/commands/hash/hget", "redis/sdks/ts/commands/hash/hgetall", + "redis/sdks/ts/commands/hash/hgetdel", + "redis/sdks/ts/commands/hash/hgetex", "redis/sdks/ts/commands/hash/hincrby", "redis/sdks/ts/commands/hash/hincrbyfloat", "redis/sdks/ts/commands/hash/hkeys", @@ -217,6 +225,7 @@ "redis/sdks/ts/commands/hash/hrandfield", "redis/sdks/ts/commands/hash/hscan", "redis/sdks/ts/commands/hash/hset", + "redis/sdks/ts/commands/hash/hsetex", "redis/sdks/ts/commands/hash/hpersist", "redis/sdks/ts/commands/hash/hpexpire", "redis/sdks/ts/commands/hash/hpexpireat", @@ -353,10 +362,12 @@ "group": "Stream", "pages": [ "redis/sdks/ts/commands/stream/xack", + "redis/sdks/ts/commands/stream/xackdel", "redis/sdks/ts/commands/stream/xadd", "redis/sdks/ts/commands/stream/xautoclaim", "redis/sdks/ts/commands/stream/xclaim", "redis/sdks/ts/commands/stream/xdel", + "redis/sdks/ts/commands/stream/xdelex", "redis/sdks/ts/commands/stream/xgroup", "redis/sdks/ts/commands/stream/xinfo", "redis/sdks/ts/commands/stream/xlen", @@ -423,6 +434,12 @@ "redis/sdks/py/commands/auth/ping" ] }, + { + "group": "Connection", + "pages": [ + "redis/sdks/py/commands/connection/client_setinfo" + ] + }, { "group": "Bitmap", "pages": [ @@ -465,6 +482,8 @@ "redis/sdks/py/commands/hash/hexpiretime", "redis/sdks/py/commands/hash/hget", "redis/sdks/py/commands/hash/hgetall", + "redis/sdks/py/commands/hash/hgetdel", + "redis/sdks/py/commands/hash/hgetex", "redis/sdks/py/commands/hash/hincrby", "redis/sdks/py/commands/hash/hincrbyfloat", "redis/sdks/py/commands/hash/hkeys", @@ -479,6 +498,7 @@ "redis/sdks/py/commands/hash/hpexpiretime", "redis/sdks/py/commands/hash/hpttl", "redis/sdks/py/commands/hash/hmset", + "redis/sdks/py/commands/hash/hsetex", "redis/sdks/py/commands/hash/hsetnx", "redis/sdks/py/commands/hash/hstrlen", "redis/sdks/py/commands/hash/httl", @@ -611,10 +631,12 @@ "group": "Stream", "pages": [ "redis/sdks/py/commands/stream/xack", + "redis/sdks/py/commands/stream/xackdel", "redis/sdks/py/commands/stream/xadd", "redis/sdks/py/commands/stream/xautoclaim", "redis/sdks/py/commands/stream/xclaim", "redis/sdks/py/commands/stream/xdel", + "redis/sdks/py/commands/stream/xdelex", "redis/sdks/py/commands/stream/xgroup_create", "redis/sdks/py/commands/stream/xgroup_createconsumer", "redis/sdks/py/commands/stream/xgroup_delconsumer", @@ -1047,8 +1069,8 @@ { "group": "REST API", "openapi": { - "source": "qstash/openapi.yaml", - "directory": "qstash/api-refence" + "source": "qstash/openapi.yaml", + "directory": "qstash/api-refence" }, "pages": [ "qstash/api/authentication", @@ -1258,7 +1280,6 @@ "workflow/features/invoke/serveMany" ] } - ] }, { @@ -1591,9 +1612,7 @@ }, { "group": "Account", - "pages": [ - "GET /auditlogs" - ] + "pages": ["GET /auditlogs"] }, { "group": "DevOps", diff --git a/qstash/sdks/ts/examples/dlq.mdx b/qstash/sdks/ts/examples/dlq.mdx index 740a4f8b..dc2e9c15 100644 --- a/qstash/sdks/ts/examples/dlq.mdx +++ b/qstash/sdks/ts/examples/dlq.mdx @@ -30,8 +30,8 @@ while (true) { import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); -const dlq = client.dlq; -await dlq.delete("dlqId"); + +await client.dlq.delete("dlqId"); ``` #### Delete multiple messages from the DLQ @@ -41,13 +41,46 @@ import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); -const result = await client.dlq.deleteMany({ +// using an array of dlqIds +const result = await client.dlq.delete(["dlqId-1", "dlqId-2", "dlqId-3"]); + +// or using an object with dlqIds +const result2 = await client.dlq.delete({ dlqIds: ["dlqId-1", "dlqId-2", "dlqId-3"], }); console.log(result.deleted); // number of deleted messages ``` +#### Delete DLQ messages with filters + +```typescript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +// delete by label +const result = await client.dlq.delete({ label: "my-label" }); + +// delete with multiple filters +const result2 = await client.dlq.delete({ + url: "https://example.com", + label: "my-label", + fromDate: "1640995200000", + toDate: "1672531200000", +}); +``` + +#### Retry a single message from the DLQ + +```typescript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +const result = await client.dlq.retry("dlqId"); +``` + #### Retry multiple messages from the DLQ ```typescript @@ -55,9 +88,30 @@ import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); -const result = await client.dlq.retry({ +// using an array of dlqIds +const result = await client.dlq.retry(["dlqId-1", "dlqId-2"]); + +// or using an object with dlqIds +const result2 = await client.dlq.retry({ dlqIds: ["dlqId-1", "dlqId-2"], }); console.log(result.responses); // [{ messageId: "..." }, ...] ``` + +#### Retry DLQ messages with filters + +```typescript +import { Client } from "@upstash/qstash"; + +const client = new Client({ token: "" }); + +// retry by label +const result = await client.dlq.retry({ label: "my-label" }); + +// retry with multiple filters +const result2 = await client.dlq.retry({ + url: "https://example.com", + label: "my-label", +}); +``` diff --git a/qstash/sdks/ts/examples/messages.mdx b/qstash/sdks/ts/examples/messages.mdx index b2ab462d..44ea4d19 100644 --- a/qstash/sdks/ts/examples/messages.mdx +++ b/qstash/sdks/ts/examples/messages.mdx @@ -22,8 +22,8 @@ const msg = await messages.get("msgId"); import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); -const messages = client.messages -const msg = await messages.delete("msgId"); + +await client.messages.delete("msgId"); ``` #### Cancel messages in bulk @@ -35,15 +35,17 @@ import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); -// deleting two messages at once -await client.messages.deleteMany([ +// cancel two messages at once +const result = await client.messages.delete([ "message-id-1", "message-id-2", -]) +]); +console.log(result.cancelled); // 2 -// deleting all messages -await client.messages.deleteAll() +// cancel all messages +const result2 = await client.messages.delete({}); +console.log(result2.cancelled); // number of cancelled messages ``` #### Cancel messages with filters @@ -56,14 +58,14 @@ import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); // cancel all messages with a specific flow control key -const cancelled = await client.messages.deleteAll({ +const result = await client.messages.delete({ flowControlKey: "my-flow-key", }); -console.log(cancelled); // number of cancelled messages +console.log(result.cancelled); // number of cancelled messages // cancel with multiple filters -const cancelled2 = await client.messages.deleteAll({ +const result2 = await client.messages.delete({ flowControlKey: "my-flow-key", url: "https://example.com", queueName: "my-queue", @@ -71,4 +73,6 @@ const cancelled2 = await client.messages.deleteAll({ fromDate: "1640995200000", toDate: "1672531200000", }); + +console.log(result2.cancelled); // number of cancelled messages ``` diff --git a/workflow/basics/client/dlq/delete.mdx b/workflow/basics/client/dlq/delete.mdx index 600a24c9..d9adb0d2 100644 --- a/workflow/basics/client/dlq/delete.mdx +++ b/workflow/basics/client/dlq/delete.mdx @@ -2,46 +2,81 @@ title: "client.dlq.delete" --- -The `dlq.delete` method removes one or more messages from the **Dead Letter Queue (DLQ)**. +The `dlq.delete` method removes one or more entries from the **Dead Letter Queue (DLQ)**. ## Arguments - - A single DLQ entry ID or an array of IDs to delete. Use the `dlqId` field from - messages returned by `client.dlq.list()`. +The method accepts different argument types: + + + A single DLQ entry ID to delete. -## Response + + An array of DLQ entry IDs to delete. + - - The number of messages successfully deleted from the DLQ. - + + An object containing `dlqIds` to delete. -## Usage + + + One or more DLQ entry IDs. + + + - -```ts Single -import { Client } from "@upstash/workflow"; + + A filter object to delete matching DLQ entries. -const client = new Client({ token: "" }); + + + Delete entries with this label. + -const result = await client.dlq.delete({ -dlqIds: "dlq-12345", -}); + + Delete entries created after this date (Unix ms as string). + -console.log(result.deleted); // 1 + + Delete entries created before this date (Unix ms as string). + -```` -```ts Multiple -import { Client } from "@upstash/workflow"; + + Delete entries matching this workflow URL. + -const client = new Client({ token: "" }); + + Delete entries matching these workflow run IDs. + + + -const result = await client.dlq.delete({ - dlqIds: ["dlq-12345", "dlq-67890", "dlq-11111"], -}); +## Response -console.log(result.deleted); // 3 -```` + + The number of DLQ entries that were deleted. + +## Usage + + +```ts Single ID +await client.dlq.delete("dlq-12345"); +``` +```ts Multiple IDs +await client.dlq.delete(["dlq-12345", "dlq-67890"]); +``` +```ts Object with dlqIds +await client.dlq.delete({ + dlqIds: ["dlq-12345", "dlq-67890"], +}); +``` +```ts With Filters +await client.dlq.delete({ + label: "my-workflow-label", + fromDate: "1640995200000", + toDate: "1672531200000", +}); +``` diff --git a/workflow/basics/client/dlq/restart.mdx b/workflow/basics/client/dlq/restart.mdx index 1e8b4020..6fb0da3a 100644 --- a/workflow/basics/client/dlq/restart.mdx +++ b/workflow/basics/client/dlq/restart.mdx @@ -2,11 +2,15 @@ title: "client.dlq.restart" --- -The `dlq.restart` method restarts one or more workflow runs from **Dead Letter Queue (DLQ)**. +The `dlq.restart` method restarts one or more workflow runs from the **Dead Letter Queue (DLQ)**. This allows you to reprocess workflow runs that previously failed after exhausting retries. +Can be called with DLQ IDs (with optional `flowControl` and `retries`), or with a filter object to restart matching entries. + ## Arguments +### DLQ ID mode + The DLQ entry ID or list of IDs to restart. Use the `dlqId` field from messages returned by `client.dlq.list()`. @@ -42,18 +46,40 @@ This allows you to reprocess workflow runs that previously failed after exhausti Defaults to `3` if not provided. +### Filter mode + + + Restart DLQ entries with this label. + + + + Restart DLQ entries matching this workflow URL. + + + + Restart DLQ entries matching these workflow run IDs. + + + + Restart DLQ entries created after this date (Unix ms as string). + + + + Restart DLQ entries created before this date (Unix ms as string). + + ## Response -`client.dlq.restart()` returns one or more objects containing details of the restarted workflow run(s): +`client.dlq.restart()` returns an array of objects containing details of the restarted workflow run(s): - + The ID of the new workflow run created from the restarted DLQ message. - - The Unix timestamp (in milliseconds) when the new workflow run was created. + + The timestamp when the new workflow run was created. @@ -66,8 +92,8 @@ This allows you to reprocess workflow runs that previously failed after exhausti ```ts Single const { messages } = await client.dlq.list(); -const response = await client.dlq.restart({ - dlqId: messages[0].dlqId, // Use the dlqId from the message +const runs = await client.dlq.restart({ + dlqId: messages[0].dlqId, flowControl: { key: "my-flow-control-key", parallelism: 10, @@ -75,12 +101,24 @@ const response = await client.dlq.restart({ retries: 3, }); +console.log(runs[0].workflowRunId); // ID of the restarted workflow run ``` ```ts Multiple -const responses = await client.dlq.restart({ +const runs = await client.dlq.restart({ dlqId: ["dlq-12345", "dlq-67890"], retries: 5, }); + +console.log(runs[0].workflowRunId); // first restarted run ID +console.log(runs[1].workflowRunId); // second restarted run ID +``` +```ts With Filters +const runs = await client.dlq.restart({ + label: "my-workflow-label", + workflowUrl: "https://your-endpoint.com/workflow", +}); + +console.log(`Restarted ${runs.length} workflow runs`); ``` diff --git a/workflow/basics/client/dlq/resume.mdx b/workflow/basics/client/dlq/resume.mdx index 88b88792..a0db8842 100644 --- a/workflow/basics/client/dlq/resume.mdx +++ b/workflow/basics/client/dlq/resume.mdx @@ -5,8 +5,12 @@ title: "client.dlq.resume" The `dlq.resume` method resumes one or more workflow runs from the **Dead Letter Queue (DLQ)** at the point where they previously failed. This allows you to continue execution from the failed step instead of restarting the workflow from the beginning. +Can be called with DLQ IDs (with optional `flowControl` and `retries`), or with a filter object to resume matching entries. + ## Arguments +### DLQ ID mode + The DLQ entry ID or list of IDs to resume. Use the `dlqId` field from messages returned by `client.dlq.list()`. @@ -42,18 +46,40 @@ This allows you to continue execution from the failed step instead of restarting Defaults to `3` if not provided. +### Filter mode + + + Resume DLQ entries with this label. + + + + Resume DLQ entries matching this workflow URL. + + + + Resume DLQ entries matching these workflow run IDs. + + + + Resume DLQ entries created after this date (Unix ms as string). + + + + Resume DLQ entries created before this date (Unix ms as string). + + ## Response -`client.dlq.resume()` returns one or more objects with details of the resumed workflow run(s): +`client.dlq.resume()` returns an array of objects with details of the resumed workflow run(s): - + The ID of the workflow run resumed from the DLQ message. - - The Unix timestamp (in milliseconds) when the resumed run was created. + + The timestamp when the resumed run was created. @@ -64,20 +90,32 @@ This allows you to continue execution from the failed step instead of restarting ```ts Single const { messages } = await client.dlq.list(); -const response = await client.dlq.resume({ - dlqId: messages[0].dlqId, // Use the dlqId from the message +const runs = await client.dlq.resume({ + dlqId: messages[0].dlqId, flowControl: { key: "my-flow-control-key", - value: "my-flow-control-value", + parallelism: 10, }, retries: 3, }); +console.log(runs[0].workflowRunId); // ID of the resumed workflow run ``` ```ts Multiple -const responses = await client.dlq.resume({ +const runs = await client.dlq.resume({ dlqId: ["dlq-12345", "dlq-67890"], retries: 5, }); + +console.log(runs[0].workflowRunId); // first resumed run ID +console.log(runs[1].workflowRunId); // second resumed run ID +``` +```ts With Filters +const runs = await client.dlq.resume({ + label: "my-workflow-label", + workflowUrl: "https://your-endpoint.com/workflow", +}); + +console.log(`Resumed ${runs.length} workflow runs`); ``` From 3503571f24516ff48c916a7c0ddda4eaa65673ad Mon Sep 17 00:00:00 2001 From: alitariksahin Date: Tue, 10 Feb 2026 14:09:28 +0300 Subject: [PATCH 3/5] fix: update cancel method --- workflow/basics/client/cancel.mdx | 111 ++++++++++++++++++----------- workflow/howto/cancel.mdx | 3 +- workflow/rest/runs/bulk-cancel.mdx | 60 +++++++++++++--- workflow/rest/runs/cancel.mdx | 3 +- 4 files changed, 125 insertions(+), 52 deletions(-) diff --git a/workflow/basics/client/cancel.mdx b/workflow/basics/client/cancel.mdx index 5d1f715b..b0422ab3 100644 --- a/workflow/basics/client/cancel.mdx +++ b/workflow/basics/client/cancel.mdx @@ -5,55 +5,83 @@ title: "client.cancel" There are multiple ways you can cancel workflow runs: - Pass one or more workflow run ids to cancel them -- Pass a workflow url to cancel all runs starting with this url -- Pass filters to cancel matching workflow runs -- Cancel all pending or active workflow runs +- Pass a workflow url to cancel all runs with this exact url +- Pass a url prefix to cancel all runs starting with this url +- Pass filters (label, date range) to cancel matching workflow runs +- Cancel all pending or active workflow runs (by calling without any parameters) ## Arguments - The set of workflow run IDs you want to cancel. + The set of workflow run IDs you want to cancel. - - The URL address you want to filter while canceling. + + The exact URL of workflows you want to cancel. - - Cancel workflow runs matching the provided filters. + + The URL prefix to filter workflows while canceling. All workflows with URLs + starting with this prefix will be canceled. + - - - Cancel workflows with this label. - + + Cancel workflows with this label. + - - Cancel workflows created after this date (Unix ms as string). - + + Cancel workflows created after this date (Unix ms as string). + - - Cancel workflows created before this date (Unix ms as string). - - + + Cancel workflows created before this date (Unix ms as string). - Whether you want to cancel all workflow runs without any filter. + + Deprecated: If no parameters are provided, all workflows will be cancelled + by default. + +## Returns + +Returns an object with the number of cancelled workflows: + +```ts +{ + cancelled: number; +} +``` + ## Usage ### Cancel a set of workflow runs ```ts // cancel a single workflow -await client.cancel({ ids: "" }); +const result = await client.cancel({ ids: "" }); +console.log(`Cancelled ${result.cancelled} workflow(s)`); // cancel a set of workflow runs -await client.cancel({ ids: ["", ""] }); +const result = await client.cancel({ + ids: ["", ""], +}); +console.log(`Cancelled ${result.cancelled} workflow(s)`); ``` -### Cancel workflow runs with URL filter +### Cancel workflow runs with exact URL + +Cancel all workflow runs with a specific URL: + +```ts +const result = await client.cancel({ + workflowUrl: "https://your-endpoint.com/api/workflow", +}); +console.log(`Cancelled ${result.cancelled} workflow(s)`); +``` + +### Cancel workflow runs with URL prefix If you have an endpoint called `https://your-endpoint.com` and you want to cancel all workflow runs on it, you can use `urlStartingWith`. @@ -62,7 +90,10 @@ Note that this will cancel workflows in all endpoints under `https://your-endpoint.com`. ```ts -await client.cancel({ urlStartingWith: "https://your-endpoint.com" }); +const result = await client.cancel({ + urlStartingWith: "https://your-endpoint.com", +}); +console.log(`Cancelled ${result.cancelled} workflow(s)`); ``` ### Cancel workflow runs with filters @@ -71,33 +102,31 @@ Cancel all active workflows that match the provided filters: ```ts // cancel workflows with a specific label -await client.cancel({ - filters: { label: "my-workflow-label" }, +const result = await client.cancel({ + label: "my-workflow-label", }); // cancel workflows within a date range -await client.cancel({ - filters: { - fromDate: "1640995200000", - toDate: "1672531200000", - }, +const result = await client.cancel({ + fromDate: "1640995200000", + toDate: "1672531200000", }); -// combine filters -await client.cancel({ - filters: { - label: "my-workflow-label", - fromDate: "1640995200000", - toDate: "1672531200000", - }, +// combine multiple filters +const result = await client.cancel({ + label: "my-workflow-label", + fromDate: "1640995200000", + toDate: "1672531200000", }); + +console.log(`Cancelled ${result.cancelled} workflow(s)`); ``` ### Cancel _all_ workflows -To cancel all pending and currently running workflows, you can -do it like this: +To cancel all pending and currently running workflows, simply call cancel without any parameters: ```ts -await client.cancel({ all: true }); +const result = await client.cancel({}); +console.log(`Cancelled ${result.cancelled} workflow(s)`); ``` diff --git a/workflow/howto/cancel.mdx b/workflow/howto/cancel.mdx index ed62ec70..6d97f044 100644 --- a/workflow/howto/cancel.mdx +++ b/workflow/howto/cancel.mdx @@ -25,7 +25,8 @@ In your Upstash Workflow console, find the run you'd like to cancel and press th import { Client } from "@upstash/workflow"; const client = new Client({ token: "" }); -await client.cancel({ ids: "" }); +const result = await client.cancel({ ids: "" }); +console.log(`Cancelled ${result.cancelled} workflow(s)`); ``` And replace `` with your actual run ID. See [the documentation of `client.cancel` method for more information about other ways of canceling workflows](/workflow/basics/client/cancel). diff --git a/workflow/rest/runs/bulk-cancel.mdx b/workflow/rest/runs/bulk-cancel.mdx index b509de21..99a5e774 100644 --- a/workflow/rest/runs/bulk-cancel.mdx +++ b/workflow/rest/runs/bulk-cancel.mdx @@ -27,7 +27,16 @@ In such cases, you can run the bulk cancel operation multiple times. The list of workflow run IDs to cancel. - The prefix filter to match workflow run URLs. Workflow runs with URLs starting with this prefix will be canceled. + The workflow URL to filter. Can be used either for exact matches or as a prefix filter depending on the endpoint behavior. + + + Cancel workflows with this label. + + + Cancel workflows created after this date (Unix timestamp in milliseconds). + + + Cancel workflows created before this date (Unix timestamp in milliseconds). ## Response @@ -44,10 +53,27 @@ A cancelled object with the number of cancelled workflow runs. ```sh curl +# Cancel by workflow URL curl -XDELETE https://qstash.upstash.io/v2/workflows/runs \ - -H "Content-Type: application/json" \ + -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{"workflowUrl": "https://example.com"}' + +# Cancel with filters +curl -XDELETE https://qstash.upstash.io/v2/workflows/runs \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{ + "label": "my-workflow-label", + "fromDate": 1640995200000, + "toDate": 1672531200000 + }' + +# Cancel all workflows (empty body) +curl -XDELETE https://qstash.upstash.io/v2/workflows/runs \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{}' ``` @@ -55,16 +81,32 @@ curl -XDELETE https://qstash.upstash.io/v2/workflows/runs \ import { Client } from "@upstash/workflow"; // cancel a set of workflow runs -await client.cancel({ ids: [ - "", - "", -]}) +const result = await client.cancel({ + ids: [ + "", + "", + ] +}); +console.log(`Cancelled ${result.cancelled} workflow(s)`); // cancel workflows starting with a url -await client.cancel({ urlStartingWith: "https://your-endpoint.com" }) +const result = await client.cancel({ + urlStartingWith: "https://your-endpoint.com" +}); + +// cancel workflows with a specific label +const result = await client.cancel({ + label: "my-workflow-label" +}); + +// cancel workflows within a date range +const result = await client.cancel({ + fromDate: "1640995200000", + toDate: "1672531200000", +}); -// cancel all workflows -await client.cancel({ all: true }) +// cancel all workflows (no parameters) +const result = await client.cancel({}); ``` diff --git a/workflow/rest/runs/cancel.mdx b/workflow/rest/runs/cancel.mdx index a235444d..7251af40 100644 --- a/workflow/rest/runs/cancel.mdx +++ b/workflow/rest/runs/cancel.mdx @@ -32,7 +32,8 @@ curl -XDELETE https://qstash.upstash.io/v2/workflows/runs/wfr_TzazoUCuZmFGp2u9cd import { Client } from "@upstash/workflow"; const client = new Client({ token: "" }) -await client.cancel({ workflowRunId: "" }) +const result = await client.cancel({ ids: "" }) +console.log(`Cancelled ${result.cancelled} workflow(s)`); ``` ```js Node From fe6733766f576df63daf4d0e0fd2176f5c7874e6 Mon Sep 17 00:00:00 2001 From: alitariksahin Date: Fri, 13 Feb 2026 14:44:56 +0300 Subject: [PATCH 4/5] fix: dlq restart resume docs --- workflow/basics/client/dlq/restart.mdx | 25 +++++++++++-------------- workflow/basics/client/dlq/resume.mdx | 23 +++++++++++------------ workflow/features/dlq/restart.mdx | 4 +++- workflow/features/dlq/resume.mdx | 4 +++- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/workflow/basics/client/dlq/restart.mdx b/workflow/basics/client/dlq/restart.mdx index 6fb0da3a..ee58a001 100644 --- a/workflow/basics/client/dlq/restart.mdx +++ b/workflow/basics/client/dlq/restart.mdx @@ -70,20 +70,17 @@ Can be called with DLQ IDs (with optional `flowControl` and `retries`), or with ## Response -`client.dlq.restart()` returns an array of objects containing details of the restarted workflow run(s): +The return type depends on the input: +- **Single DLQ ID (string)**: Returns a single object +- **Array of DLQ IDs**: Returns an array of objects +- **Filter mode**: Returns an array of objects - - - - The ID of the new workflow run created from the restarted DLQ message. - - - - The timestamp when the new workflow run was created. - - - + + The ID of the new workflow run created from the restarted DLQ message. + + + The timestamp when the new workflow run was created. ## Usage @@ -92,7 +89,7 @@ Can be called with DLQ IDs (with optional `flowControl` and `retries`), or with ```ts Single const { messages } = await client.dlq.list(); -const runs = await client.dlq.restart({ +const run = await client.dlq.restart({ dlqId: messages[0].dlqId, flowControl: { key: "my-flow-control-key", @@ -101,7 +98,7 @@ const runs = await client.dlq.restart({ retries: 3, }); -console.log(runs[0].workflowRunId); // ID of the restarted workflow run +console.log(run.workflowRunId); // ID of the restarted workflow run ``` ```ts Multiple const runs = await client.dlq.restart({ diff --git a/workflow/basics/client/dlq/resume.mdx b/workflow/basics/client/dlq/resume.mdx index a0db8842..f1f3a46f 100644 --- a/workflow/basics/client/dlq/resume.mdx +++ b/workflow/basics/client/dlq/resume.mdx @@ -70,18 +70,17 @@ Can be called with DLQ IDs (with optional `flowControl` and `retries`), or with ## Response -`client.dlq.resume()` returns an array of objects with details of the resumed workflow run(s): +The return type depends on the input: +- **Single DLQ ID (string)**: Returns a single object +- **Array of DLQ IDs**: Returns an array of objects +- **Filter mode**: Returns an array of objects - - - - The ID of the workflow run resumed from the DLQ message. - + + The ID of the workflow run resumed from the DLQ message. + - - The timestamp when the resumed run was created. - - + + The timestamp when the resumed run was created. ## Usage @@ -90,7 +89,7 @@ Can be called with DLQ IDs (with optional `flowControl` and `retries`), or with ```ts Single const { messages } = await client.dlq.list(); -const runs = await client.dlq.resume({ +const run = await client.dlq.resume({ dlqId: messages[0].dlqId, flowControl: { key: "my-flow-control-key", @@ -99,7 +98,7 @@ const runs = await client.dlq.resume({ retries: 3, }); -console.log(runs[0].workflowRunId); // ID of the resumed workflow run +console.log(run.workflowRunId); // ID of the resumed workflow run ``` ```ts Multiple const runs = await client.dlq.resume({ diff --git a/workflow/features/dlq/restart.mdx b/workflow/features/dlq/restart.mdx index f9ac4441..effd0abf 100644 --- a/workflow/features/dlq/restart.mdx +++ b/workflow/features/dlq/restart.mdx @@ -23,10 +23,12 @@ You can perform this action programmatically as well: const client = new Client({ token: "" }); - await client.dlq.restart({ + const run = await client.dlq.restart({ dlqId: "dlq-12345", retries: 3, }); + + console.log(`Restarted workflow run: ${run.workflowRunId}`); ``` diff --git a/workflow/features/dlq/resume.mdx b/workflow/features/dlq/resume.mdx index 6fd669d1..e4364655 100644 --- a/workflow/features/dlq/resume.mdx +++ b/workflow/features/dlq/resume.mdx @@ -22,10 +22,12 @@ You can perform this action programmatically as well: const client = new Client({ token: "" }); - await client.dlq.resume({ + const run = await client.dlq.resume({ dlqId: "dlq-12345", retries: 3, }); + + console.log(`Resumed workflow run: ${run.workflowRunId}`); ``` From 7273631c2beaae9e2e1f3f37ab6ea3f42b7c4cbd Mon Sep 17 00:00:00 2001 From: alitariksahin Date: Fri, 20 Feb 2026 14:18:38 +0300 Subject: [PATCH 5/5] fix: update workflow and qstash bulk actions to have all: true param --- qstash/sdks/ts/examples/dlq.mdx | 6 ++++ qstash/sdks/ts/examples/messages.mdx | 9 ++---- workflow/features/dlq.mdx | 45 ++++++++++++++++++++++++++-- workflow/features/dlq/restart.mdx | 27 +++++++++++++++-- workflow/features/dlq/resume.mdx | 28 ++++++++++++++++- 5 files changed, 104 insertions(+), 11 deletions(-) diff --git a/qstash/sdks/ts/examples/dlq.mdx b/qstash/sdks/ts/examples/dlq.mdx index dc2e9c15..c64a8fde 100644 --- a/qstash/sdks/ts/examples/dlq.mdx +++ b/qstash/sdks/ts/examples/dlq.mdx @@ -69,6 +69,9 @@ const result2 = await client.dlq.delete({ fromDate: "1640995200000", toDate: "1672531200000", }); + +// delete all DLQ entries +await client.dlq.delete({ all: true }); ``` #### Retry a single message from the DLQ @@ -114,4 +117,7 @@ const result2 = await client.dlq.retry({ url: "https://example.com", label: "my-label", }); + +// retry all DLQ entries +const result3 = await client.dlq.retry({ all: true }); ``` diff --git a/qstash/sdks/ts/examples/messages.mdx b/qstash/sdks/ts/examples/messages.mdx index 44ea4d19..01e3d499 100644 --- a/qstash/sdks/ts/examples/messages.mdx +++ b/qstash/sdks/ts/examples/messages.mdx @@ -12,7 +12,7 @@ for accessing messages that are in the process of being delivered/retried. import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); -const messages = client.messages +const messages = client.messages; const msg = await messages.get("msgId"); ``` @@ -36,15 +36,12 @@ import { Client } from "@upstash/qstash"; const client = new Client({ token: "" }); // cancel two messages at once -const result = await client.messages.delete([ - "message-id-1", - "message-id-2", -]); +const result = await client.messages.delete(["message-id-1", "message-id-2"]); console.log(result.cancelled); // 2 // cancel all messages -const result2 = await client.messages.delete({}); +const result2 = await client.messages.delete({ all: true }); console.log(result2.cancelled); // number of cancelled messages ``` diff --git a/workflow/features/dlq.mdx b/workflow/features/dlq.mdx index b43eb496..83739445 100644 --- a/workflow/features/dlq.mdx +++ b/workflow/features/dlq.mdx @@ -23,7 +23,8 @@ The DLQ serves as a safety net, preserving failed workflow runs with their compl - **Pay-as-you-go**: 1 week - **Fixed pricing**: Up to 3 months - After the retention duration expires, DLQ items are automatically removed and cannot be recovered. +After the retention duration expires, DLQ items are automatically removed and cannot be recovered. + ## Recovery Actions @@ -35,4 +36,44 @@ Once a workflow run is in the DLQ, you can take the following actions: - **[Re-run Failure Function](/workflow/features/dlq/callback)** – execute the workflow's failure handling logic again. - or delete the DLQ entry if no action is required. -You can apply these actions in bulk to multiple DLQ entries. Check the individual action pages for more details. \ No newline at end of file +You can apply these actions in bulk to multiple DLQ entries. Check the individual action pages for more details. + +## Listing DLQ entries + +```typescript +import { Client } from "@upstash/workflow"; + +const client = new Client({ token: "" }); + +// list all (paginated) +const { messages, cursor } = await client.dlq.list(); + +// filter by label or workflow URL +const { messages: filtered } = await client.dlq.list({ + filter: { label: "my-label" }, +}); + +// paginate with cursor +const { messages: page2 } = await client.dlq.list({ cursor, count: 50 }); +``` + +## Deleting DLQ entries + +```typescript +import { Client } from "@upstash/workflow"; + +const client = new Client({ token: "" }); + +// delete a single entry +await client.dlq.delete("dlqId"); + +// delete multiple entries by DLQ ID +await client.dlq.delete(["dlqId-1", "dlqId-2"]); + +// delete by label or workflow URL +await client.dlq.delete({ label: "my-label" }); +await client.dlq.delete({ workflowUrl: "https://my-app.com/api/workflow" }); + +// delete all entries +await client.dlq.delete({ all: true }); +``` diff --git a/workflow/features/dlq/restart.mdx b/workflow/features/dlq/restart.mdx index effd0abf..52a6d40c 100644 --- a/workflow/features/dlq/restart.mdx +++ b/workflow/features/dlq/restart.mdx @@ -18,7 +18,7 @@ This approach is ideal when: You can perform this action programmatically as well: - ```typescript TypeScript + ```typescript Single run import { Client } from "@upstash/workflow"; const client = new Client({ token: "" }); @@ -30,6 +30,29 @@ You can perform this action programmatically as well: console.log(`Restarted workflow run: ${run.workflowRunId}`); ``` - + ```typescript Multiple runs + import { Client } from "@upstash/workflow"; + const client = new Client({ token: "" }); + + const runs = await client.dlq.restart({ + dlqId: ["dlq-12345", "dlq-67890"], + }); + + runs.forEach((run) => console.log(run.workflowRunId)); + ``` + + ```typescript Filter-based + import { Client } from "@upstash/workflow"; + + const client = new Client({ token: "" }); + + const runs = await client.dlq.restart({ + workflowUrl: "https://my-app.com/api/workflow", + }); + + runs.forEach((run) => console.log(run.workflowRunId)); + ``` + + diff --git a/workflow/features/dlq/resume.mdx b/workflow/features/dlq/resume.mdx index e4364655..44bc8284 100644 --- a/workflow/features/dlq/resume.mdx +++ b/workflow/features/dlq/resume.mdx @@ -17,7 +17,7 @@ This approach is ideal when: You can perform this action programmatically as well: - ```typescript TypeScript + ```typescript Single run import { Client } from "@upstash/workflow"; const client = new Client({ token: "" }); @@ -29,6 +29,31 @@ You can perform this action programmatically as well: console.log(`Resumed workflow run: ${run.workflowRunId}`); ``` + + ```typescript Multiple runs + import { Client } from "@upstash/workflow"; + + const client = new Client({ token: "" }); + + const runs = await client.dlq.resume({ + dlqId: ["dlq-12345", "dlq-67890"], + }); + + runs.forEach((run) => console.log(run.workflowRunId)); + ``` + + ```typescript Filter-based + import { Client } from "@upstash/workflow"; + + const client = new Client({ token: "" }); + + const runs = await client.dlq.resume({ + workflowUrl: "https://my-app.com/api/workflow", + }); + + runs.forEach((run) => console.log(run.workflowRunId)); + ``` + @@ -36,4 +61,5 @@ You can perform this action programmatically as well: Changes to steps prior to the failure are not allowed and may break the workflow. For more details, check out the [Handle workflow route code changes](/workflow/howto/changes) page. +