How to Mass Terminate Workflows with Date Filtering
Scope:
bizprocWho can execute methods: administrator
Choose a tool for developing with an AI agent:
- use Alaio Vibecode to build an app for Bitrix24 from a task description without knowing any programming language. The agent writes the code and deploys the app to a server, with no manual hosting setup
- use the MCP server to develop a REST API integration in your own project. The agent refers to the official REST documentation
Bitrix24 can retain running workflows that are no longer needed: they are stuck, waiting for outdated data, or were launched before a process change. You can find them by launch date and remove them using the bizproc.workflow.kill method.
The bizproc.workflow.kill method deletes the workflow together with its data. If you need to stop execution but keep the launch record, use bizproc.workflow.terminate. Both methods accept the same workflow identifier.
The scenario consists of two steps.
- Retrieve the list of workflows using the bizproc.workflow.instances method
- Remove the selected workflows using the bizproc.workflow.kill method
What You Need Before You Start
- an administrator inbound webhook or an application with the
bizprocscope - a date before which running workflows should be found. The examples use
2025-01-01T00:00:00Z - a decision on what to do with the found workflows: delete them using
bizproc.workflow.killor stop them usingbizproc.workflow.terminate
Before deleting, make sure to verify the found ID values. Re-running the same filter may find different workflows if new launches with matching dates appear in the meantime.
How to Use Examples in Documentation
1. Retrieve the List of Processes
Use the bizproc.workflow.instances method with the following parameters:
filter[<STARTED]— launch date. The<prefix selects workflows started before the specified timeselect— the fields required by the scenario. It is enough to getIDandSTARTED
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
// B24_HOOK = 'https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/'
const response = await $b24.actions.v2.call.make({
method: 'bizproc.workflow.instances',
params: {
select: ['ID', 'STARTED'],
filter: { '<STARTED': '2025-01-01T00:00:00Z' },
},
requestId: 'workflow-instances',
})
const instances = response.getData().result
const workflowIds = instances.map((instance) => instance.ID)
from b24pysdk import BitrixWebhook, Client
token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
client = Client(token)
instances = client.bizproc.workflow.instances(
select=["ID", "STARTED"],
filter={"<STARTED": "2025-01-01T00:00:00Z"},
).response.result
workflow_ids = [instance["ID"] for instance in instances]
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Psr\Log\NullLogger;
$b24 = (new ServiceBuilderFactory(new EventDispatcher(), new NullLogger()))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
$response = $b24->core->call('bizproc.workflow.instances', [
'select' => ['ID', 'STARTED'],
'filter' => ['<STARTED' => '2025-01-01T00:00:00Z'],
]);
$instances = $response->getResponseData()->getResult();
$workflowIds = array_column($instances, 'ID');
import (
"context"
"encoding/json"
"log"
"os"
b24 "github.com/bitrix24/b24gosdk"
)
ctx := context.Background()
core := b24.NewClient(os.Getenv("B24_WEBHOOK_URL")).Core()
res, err := core.Call(ctx, "bizproc.workflow.instances", b24.Params{
"select": []string{"ID", "STARTED"},
"filter": b24.Params{"<STARTED": "2025-01-01T00:00:00Z"},
}, b24.WithIdempotent())
if err != nil {
log.Fatal(err)
}
var instances []struct {
ID string `json:"ID"`
Started string `json:"STARTED"`
}
if err := json.Unmarshal(res.Result, &instances); err != nil {
log.Fatal(err)
}
workflowIDs := make([]string, 0, len(instances))
for _, instance := range instances {
workflowIDs = append(workflowIDs, instance.ID)
}
Store the ID values of the workflows that need to be deleted or stopped.
{
"result": [
{
"ID": "660e559f34af10.95144732",
"STARTED": "2024-12-04T10:04:24+03:00"
},
{
"ID": "6639c7b59e9eb5.40607056",
"STARTED": "2024-12-04T09:52:40+03:00"
}
],
"total": 2
}
If result contains an empty array, there are no matching workflows. There is no need to proceed to the second step.
2. Terminate Workflows
Use the bizproc.workflow.kill method with the following parameter:
ID— the workflow identifier from the response of step 1. Pass a string such as660e559f34af10.95144732
const response = await $b24.actions.v2.call.make({
method: 'bizproc.workflow.kill',
params: { ID: workflowIds[0] },
requestId: 'workflow-kill',
})
const isKilled = response.getData().result
token.call_method(
"bizproc.workflow.kill",
{"ID": workflow_ids[0]},
)
$isKilled = $b24->getBizProcScope()->workflow()
->kill($workflowIds[0])
->isSuccess();
res, err := core.Call(ctx, "bizproc.workflow.kill", b24.Params{
"ID": workflowIDs[0],
})
if err != nil {
log.Fatal(err)
}
var isKilled bool
if err := json.Unmarshal(res.Result, &isKilled); err != nil {
log.Fatal(err)
}
log.Println(isKilled)
The successful response contains true.
{
"result": true
}
Code Example
The example first prints all found workflows for review. To delete the workflows, run the example with the --confirm argument. For large volumes of data, keep REST limits and the Performance recommendations in mind.
import { B24Hook } from '@bitrix24/b24jssdk'
const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
const [day, month, year] = (process.argv[2] || '').split('.')
if (!day || !month || !year) {
throw new Error('Pass the date in dd.mm.yyyy format')
}
const isoDate = `${year}-${month}-${day}T00:00:00Z`
const confirmed = process.argv.includes('--confirm')
const listResponse = await $b24.actions.v2.callList.make({
method: 'bizproc.workflow.instances',
params: {
select: ['ID', 'STARTED'],
filter: { '<STARTED': isoDate },
},
requestId: 'workflow-instances',
})
const instances = listResponse.getData()
const workflowIds = instances.map((instance) => instance.ID)
if (!workflowIds.length) {
console.log('No workflows found')
$b24.destroy()
process.exit(0)
}
console.log(`Found workflows: ${workflowIds.length}`)
for (const workflowId of workflowIds) {
console.log(`Workflow to delete: ${workflowId}`)
}
if (!confirmed) {
console.log('Review the list and rerun the example with the --confirm argument to delete the workflows')
$b24.destroy()
process.exit(0)
}
for (const workflowId of workflowIds) {
const killResponse = await $b24.actions.v2.call.make({
method: 'bizproc.workflow.kill',
params: { ID: workflowId },
requestId: `workflow-kill-${workflowId}`,
})
console.log(killResponse.isSuccess
? `Workflow ${workflowId} deleted`
: `Error deleting workflow ${workflowId}: ${killResponse.getErrorMessages().join('; ')}`)
}
$b24.destroy()
import re
import sys
from b24pysdk import BitrixWebhook, Client
from b24pysdk.errors import BitrixAPIError
user_date_input = input("Enter the date in dd.mm.yyyy format: ")
if not re.match(r"^\d{2}\.\d{2}\.\d{4}$", user_date_input):
raise ValueError("Enter the date in dd.mm.yyyy format")
day, month, year = user_date_input.split(".")
iso_date = f"{year}-{month}-{day}T00:00:00Z"
confirmed = "--confirm" in sys.argv
token = BitrixWebhook(
domain="your-domain.bitrix24.com",
webhook_token="user_id/webhook_key",
)
client = Client(token)
start = None
workflow_ids = []
while True:
params = {
"select": ["ID", "STARTED"],
"filter": {"<STARTED": iso_date},
}
if start is not None:
params["start"] = start
response = client.bizproc.workflow.instances(**params).response
workflow_ids.extend(instance["ID"] for instance in response.result or [])
if response.next is None:
break
start = response.next
if not workflow_ids:
print("No workflows found")
sys.exit(0)
print(f"Found workflows: {len(workflow_ids)}")
for workflow_id in workflow_ids:
print(f"Workflow to delete: {workflow_id}")
if not confirmed:
print("Review the list and rerun the example with the --confirm argument to delete the workflows")
sys.exit(0)
for workflow_id in workflow_ids:
try:
token.call_method("bizproc.workflow.kill", {"ID": workflow_id})
except BitrixAPIError as error:
print(f"Error deleting workflow {workflow_id}: {error}")
else:
print(f"Workflow {workflow_id} deleted")
// composer require bitrix24/b24phpsdk:"^3.0"
require_once 'vendor/autoload.php';
use Bitrix24\SDK\Services\ServiceBuilderFactory;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Psr\Log\NullLogger;
$b24 = (new ServiceBuilderFactory(new EventDispatcher(), new NullLogger()))
->initFromWebhook('https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/');
$userDateInput = readline('Enter the date in dd.mm.yyyy format: ');
if (!preg_match('/^\d{2}\.\d{2}\.\d{4}$/', $userDateInput)) {
throw new InvalidArgumentException('Enter the date in dd.mm.yyyy format');
}
[$day, $month, $year] = explode('.', $userDateInput);
$isoDate = "{$year}-{$month}-{$day}T00:00:00Z";
$confirmed = in_array('--confirm', $argv, true);
$workflowIds = [];
$start = 0;
do {
$response = $b24->core->call('bizproc.workflow.instances', [
'select' => ['ID', 'STARTED'],
'filter' => ['<STARTED' => $isoDate],
'start' => $start,
]);
foreach ($response->getResponseData()->getResult() as $instance) {
$workflowIds[] = $instance['ID'];
}
$start = $response->getResponseData()->getPagination()->getNextItem();
} while ($start !== null);
if ($workflowIds === []) {
echo "No workflows found\n";
exit;
}
echo "Found workflows: " . count($workflowIds) . "\n";
foreach ($workflowIds as $workflowId) {
echo "Workflow to delete: {$workflowId}\n";
}
if (!$confirmed) {
echo "Review the list and rerun the example with the --confirm argument to delete the workflows\n";
exit;
}
foreach ($workflowIds as $workflowId) {
$isKilled = $b24->getBizProcScope()->workflow()->kill($workflowId)->isSuccess();
echo $isKilled
? "Workflow {$workflowId} deleted\n"
: "Error deleting workflow {$workflowId}\n";
}
// Setup in an empty directory:
// go mod init example && go get github.com/bitrix24/b24gosdk
//
// Run:
// B24_WEBHOOK_URL="https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/" go run main.go 01.01.2025
// B24_WEBHOOK_URL="https://your-domain.bitrix24.com/rest/USER_ID/TOKEN/" go run main.go 01.01.2025 --confirm
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
b24 "github.com/bitrix24/b24gosdk"
)
type workflowInstance struct {
ID string `json:"ID"`
Started string `json:"STARTED"`
}
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
func run() error {
if len(os.Args) < 2 {
return fmt.Errorf("pass the date in dd.mm.yyyy format")
}
parts := strings.Split(os.Args[1], ".")
if len(parts) != 3 || len(parts[0]) != 2 || len(parts[1]) != 2 || len(parts[2]) != 4 {
return fmt.Errorf("pass the date in dd.mm.yyyy format")
}
isoDate := fmt.Sprintf("%s-%s-%sT00:00:00Z", parts[2], parts[1], parts[0])
confirmed := false
for _, arg := range os.Args[2:] {
if arg == "--confirm" {
confirmed = true
}
}
ctx := context.Background()
core := b24.NewClient(os.Getenv("B24_WEBHOOK_URL")).Core()
workflowIDs, err := getWorkflowIDs(ctx, core, isoDate)
if err != nil {
return err
}
if len(workflowIDs) == 0 {
fmt.Println("No workflows found")
return nil
}
fmt.Printf("Found workflows: %d\n", len(workflowIDs))
for _, workflowID := range workflowIDs {
fmt.Printf("Workflow to delete: %s\n", workflowID)
}
if !confirmed {
fmt.Println("Review the list and rerun the example with the --confirm argument to delete the workflows")
return nil
}
for _, workflowID := range workflowIDs {
if err := killWorkflow(ctx, core, workflowID); err != nil {
fmt.Printf("Error deleting workflow %s: %v\n", workflowID, err)
continue
}
fmt.Printf("Workflow %s deleted\n", workflowID)
}
return nil
}
func getWorkflowIDs(ctx context.Context, core *b24.Core, isoDate string) ([]string, error) {
workflowIDs := []string{}
for start := 0; ; start += 50 {
res, err := core.Call(ctx, "bizproc.workflow.instances", b24.Params{
"select": []string{"ID", "STARTED"},
"filter": b24.Params{"<STARTED": isoDate},
"start": start,
}, b24.WithIdempotent())
if err != nil {
return nil, fmt.Errorf("bizproc.workflow.instances: %w", err)
}
var instances []workflowInstance
if err := json.Unmarshal(res.Result, &instances); err != nil {
return nil, fmt.Errorf("decode bizproc.workflow.instances: %w", err)
}
for _, instance := range instances {
workflowIDs = append(workflowIDs, instance.ID)
}
if len(instances) < 50 {
break
}
}
return workflowIDs, nil
}
func killWorkflow(ctx context.Context, core *b24.Core, workflowID string) error {
_, err := core.Call(ctx, "bizproc.workflow.kill", b24.Params{
"ID": workflowID,
})
if err != nil {
return fmt.Errorf("bizproc.workflow.kill: %w", err)
}
return nil
}
Check the Result
In the interface, open the list of running business processes and verify that the deleted processes are no longer present.
Through REST, repeat the bizproc.workflow.instances request with the same filter and verify that the deleted ID values are not returned.
const checkResponse = await $b24.actions.v2.call.make({
method: 'bizproc.workflow.instances',
params: {
select: ['ID'],
filter: { '<STARTED': '2025-01-01T00:00:00Z' },
},
requestId: 'workflow-instances-check',
})
console.log(checkResponse.getData().result.map((instance) => instance.ID))
check_result = client.bizproc.workflow.instances(
select=["ID"],
filter={"<STARTED": "2025-01-01T00:00:00Z"},
).response.result
print([instance["ID"] for instance in check_result])
$checkResponse = $b24->core->call('bizproc.workflow.instances', [
'select' => ['ID'],
'filter' => ['<STARTED' => '2025-01-01T00:00:00Z'],
]);
foreach ($checkResponse->getResponseData()->getResult() as $instance) {
echo $instance['ID'] . PHP_EOL;
}
res, err := core.Call(ctx, "bizproc.workflow.instances", b24.Params{
"select": []string{"ID"},
"filter": b24.Params{"<STARTED": "2025-01-01T00:00:00Z"},
}, b24.WithIdempotent())
if err != nil {
log.Fatal(err)
}
var instances []struct {
ID string `json:"ID"`
}
if err := json.Unmarshal(res.Result, &instances); err != nil {
log.Fatal(err)
}
for _, instance := range instances {
log.Println(instance.ID)
}
The scenario is complete if the response no longer contains the ID values that were passed to bizproc.workflow.kill.
Errors and Diagnostics
If a method returns an error, verify the request data.
|
Error code or text |
Cause and action |
|
|
The method was not called by an administrator or the webhook does not have the |
|
|
|
|
Empty |
There are no running processes that match the |
|
Errors with large volumes of data |
Check the paging logic and the Performance recommendations |
Repeat the scenario from the step where the error occurred. If the error happens while deleting one process, verify its ID and continue processing the remaining processes.
What to Consider
bizproc.workflow.killdeletes the process together with its process databizproc.workflow.terminatestops the process and keeps the record of its launch- the business process
IDis a string like660e559f34af10.95144732; do not convert it to a number bizproc.workflow.instancesreturns 50 records per request, so mass processing requires pagination