Reference
Bulk Operations
Asynchronous batch jobs that act on many contacts at once (tag, untag, delete,
etc.). Differs from POST /api/contacts/bulk (synchronous delete-only) in two
ways: this runs in the background on the bulk queue, and supports more
actions with per-row failure tracking.
Start a bulk operation
Enqueues the job and returns 202 with the operation record. Poll
GET /api/bulk-operations/{id} until status=completed or failed.
POST
/api/bulk-operations
Body parameters
-
actionstring requiredAction to run (
tag,untag,delete, etc. — seeBulkOperation::ACTIONS).Example:
tag -
payloadobject requiredAction-specific payload. Must include
ids.-
idsinteger[] requiredTarget contact IDs (min 1).
Example:
[42,43,44] -
tag_idintegerRequired for
tag/untag.Example:
3
-
curl --request POST \
"https://klozzo.com/api/bulk-operations" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"action\": \"tag\",
\"payload\": {
\"ids\": [
42,
43,
44
],
\"tag_id\": 3
}
}"
const url = new URL(
"https://klozzo.com/api/bulk-operations"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"action": "tag",
"payload": {
"ids": [
42,
43,
44
],
"tag_id": 3
}
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/bulk-operations';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'action' => 'tag',
'payload' => [
'ids' => [42, 43, 44],
'tag_id' => 3,
],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/bulk-operations'
payload = {
"action": "tag",
"payload": {
"ids": [
42,
43,
44
],
"tag_id": 3
}
}
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()
{
"id": 31,
"action": "tag",
"status": "pending",
"total": 3,
"processed": 0,
"failed": 0,
"progress": 0,
"errors": null,
"started_at": null,
"finished_at": null,
"created_at": "2026-06-05T13:42:00+00:00"
}
-
idinteger -
actionstring -
statusstring -
totalinteger -
processedinteger -
failedinteger -
progressinteger -
errorsstring -
started_atstring -
finished_atstring -
created_atstring
{
"message": "Action no es una opción válida.",
"errors": {
"action": [
"Action no es una opción válida."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
{
"message": "Falta payload.ids.",
"errors": {
"payload.ids": [
"Falta payload.ids."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
Bulk operation status
Returns the current state of a bulk operation. progress is (processed / total) * 100 rounded. errors is populated per-row when individual rows
fail (the job continues; only status=failed indicates a global failure).
GET
/api/bulk-operations/{bulkOperation_id}
Path parameters
-
bulkOperation_idinteger requiredBulk operation ID.
Example:
31
curl --request GET \
--get "https://klozzo.com/api/bulk-operations/31" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/bulk-operations/31"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/bulk-operations/31';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/bulk-operations/31'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()
{
"id": 31,
"action": "tag",
"status": "processing",
"total": 3,
"processed": 2,
"failed": 0,
"progress": 66,
"errors": null,
"started_at": "2026-06-05T13:42:01+00:00",
"finished_at": null,
"created_at": "2026-06-05T13:42:00+00:00"
}
-
idinteger -
actionstring -
statusstring -
totalinteger -
processedinteger -
failedinteger -
progressinteger -
errorsstring -
started_atstring -
finished_atstring -
created_atstring
{
"message": "No query results for model."
}
Every error shares the same shape — message and errors.
See Errors.