Skip to content
Get started
Get started
TCP Control Commands: Jobs, Queues, Limits
View Markdown
api reference · tcp · control

Steer jobs, queues and limits.

Change individual jobs, pause, drain, clean and obliterate whole queues, and set rate limits, concurrency caps, job group controls and per-queue stall and DLQ configuration.

Part of the TCP protocol reference, which describes the framing, authentication, pipelining and response format that every command on this page uses.

Cancel a waiting or delayed job.

Request:

{ cmd: 'Cancel', id: string }

Response:

{
ok: true;
}

Update the progress of an active job.

Request:

{
cmd: 'Progress',
id: string,
progress: number, // 0-100
message?: string // Optional progress message
}

progress is stored as in 2.9.10 and never refused: a number is clamped to 0-100 (NaN is 0); a numeric string, a boolean or null is its Number(...) ("50" is 50, true is 1); other text is 0 with the text as the message when none is given. Clients send object progress as 0 with its JSON as the message.

Response:

{
ok: true;
}

Update the data payload of an existing job.

Request:

{
cmd: 'Update',
id: string,
data: any // New job data
}

data must be JSON serializable (Job data must be JSON serializable). Unlike PUSH, an update has no size limit, as in 2.9.10.

Response:

{
ok: true;
}

Change the priority of a queued job.

Request:

{
cmd: 'ChangePriority',
id: string,
priority: number,
lifo?: boolean // Tie-break ordering among same-priority jobs
}

priority can be any finite number, for grouped jobs too (as in 2.9.10; a numeric string counts as its number); a missing priority is 0 (BullMQ’s changePriority({ lifo: true })). lifo, when given, is made a boolean exactly as on PUSH (1 is true, 0 is false). NaN, an infinity or a non-number fails (priority must be a finite number, priority must be a number) and leaves the job unchanged; a job that is not queued fails with Job not found or not in queue, which the client SDKs treat as “not changed”, like embedded mode.

Response:

{
ok: true;
}

Move a delayed job to the waiting state immediately.

Request:

{ cmd: 'Promote', id: string }

Response:

{
ok: true;
}

Move an active job back to the delayed state.

Request:

{
cmd: 'MoveToDelayed',
id: string,
delay: number, // Delay in ms from now (required, finite; negative = past run time)
token?: string // Required when the active job has a lock
}

A missing or non-finite delay fails the command (delay is required, delay must be a finite number, …) and leaves the job unchanged. As in 2.9.10, a negative delay makes the job ready at once with a past run time (ahead of later ready jobs; a PostgreSQL broker uses “now”, as on 2.9.10) and a very large one is applied (clamped at about 136,900 years).

Response:

{
ok: true;
}

Discard a job by moving it to the dead-letter queue.

Request:

{ cmd: 'Discard', id: string, token?: string }

When the job has an active lease, token must match the current delivery token. For waiting or otherwise unlocked jobs, the field may be omitted for an administrative discard.

Response:

{
ok: true;
}

Wait for a job to complete. This is event-driven (no polling). Returns immediately if the job is already completed.

Request:

{
cmd: 'WaitJob',
id: string,
timeout?: number // Max wait time in ms (default: 30000, max: 600000)
}

Response:

{ ok: true, completed: boolean, result?: any }

Pause a queue. Workers will stop pulling new jobs.

Request:

{ cmd: 'Pause', queue: string }

Response:

{
ok: true;
}

Resume a paused queue.

Request:

{ cmd: 'Resume', queue: string }

Response:

{
ok: true;
}

Check whether a queue is currently paused.

Request:

{ cmd: 'IsPaused', queue: string }

Response:

{ ok: true, paused: boolean }

Remove all waiting and delayed jobs from a queue. Active jobs are not affected.

Request:

{ cmd: 'Drain', queue: string }

Response:

{ ok: true, count: number } // Number of jobs removed

Remove all data for a queue (all jobs in all states).

Request:

{ cmd: 'Obliterate', queue: string }

Response:

{
ok: true;
}

Remove jobs older than a grace period, optionally filtered by state.

Request:

{
cmd: 'Clean',
queue: string,
grace: number, // Grace period in ms - jobs older than this are removed
state?: string, // 'waiting'/'delayed'/'prioritized'/'paused' (queued jobs, the default), 'completed', or 'failed'
limit?: number // Max jobs to remove (default: 1000)
}

Response:

{ ok: true, count: number, ids: string[] } // IDs of the removed jobs

List the names of all known queues.

Request:

{
cmd: 'ListQueues';
}

Response:

{ ok: true, queues: string[] } // Queue names

For per-queue counts use GetJobCounts per queue, or the HTTP GET /queues/summary endpoint.


Set a rate limit on a queue: limit jobs per duration ms (default 1000, so jobs per second).

Request:

{
cmd: 'RateLimit',
queue: string,
limit: number, // Max jobs per window
duration?: number, // Window in ms (default 1000)
ttl?: number // Auto-expiry in ms: the server clears the limit itself
}

Invalid duration or ttl values (non-finite or not positive) fall back to the defaults (1 second window, permanent limit) instead of failing. Servers older than 2.8.35 ignore both optional fields.

Response:

{
ok: true;
}

Remove the rate limit from a queue.

Request:

{ cmd: 'RateLimitClear', queue: string }

Response:

{
ok: true;
}

Set a concurrency limit on a queue (max concurrent active jobs).

Request:

{
cmd: 'SetConcurrency',
queue: string,
limit: number
}

Response:

{
ok: true;
}

Remove the concurrency limit from a queue.

Request:

{ cmd: 'ClearConcurrency', queue: string }

Response:

{
ok: true;
}

Read the live rate/concurrency configuration and saturation state.

{ cmd: 'GetQueueLimits', queue: string, maxJobs?: number }
{
ok: true,
data: {
limits: {
rateLimit: { max: number, duration: number } | null,
rateLimitTtl: number, // -2 when no rate limit exists
concurrencyLimit: number | null,
maxed: boolean
}
}
}

Group depth excludes active jobs and includes waiting, prioritized and delayed jobs. Every response below is wrapped in data:

{ cmd: 'GetGroupJobsCount', queue, groupId }
// -> { ok: true, data: { count: number } }
{ cmd: 'GetGroupsJobsCount', queue, maxCount? }
// -> { ok: true, data: { count: number } }
{ cmd: 'GetGroupActiveCount', queue, groupId }
// -> { ok: true, data: { count: number } }
{ cmd: 'SetGroupRateLimit', queue, groupId, max, duration }
{ cmd: 'GetGroupRateLimit', queue, groupId }
// -> { ok: true, data: { limit: { max, duration } | null } }
{ cmd: 'RemoveGroupRateLimit', queue, groupId }
// -> { ok: true, data: { removed: 0 | 1 } }
{ cmd: 'GetGroupRateLimitTtl', queue, groupId, maxJobs? }
// -> { ok: true, data: { ttl: number } }
{ cmd: 'SetGroupConcurrency', queue, groupId, concurrency }
{ cmd: 'GetGroupConcurrency', queue, groupId }
// -> { ok: true, data: { concurrency: number | null } }
{ cmd: 'RemoveGroupConcurrency', queue, groupId }
// -> { ok: true, data: { removed: 0 | 1 } }
{ cmd: 'PauseGroup', queue, groupId }
// -> { ok: true, data: { changed: boolean } }
{ cmd: 'ResumeGroup', queue, groupId }
// -> { ok: true, data: { changed: boolean } }
{ cmd: 'IsGroupPaused', queue, groupId }
// -> { ok: true, data: { paused: boolean } }
{ cmd: 'RateLimitGroup', queue, groupId, duration }
// -> { ok: true }

Group IDs are non-empty strings of at most 256 characters. max, duration, and concurrency must be positive safe integers. Stored overrides affect a claim only when PULL/PULLB carries the corresponding group default. Pause blocks only new claims from that group. RateLimitGroup installs an immediately effective manual deadline even when the Worker has no group-rate default.


{ cmd: 'GetDeduplicationJobId', queue: string, deduplicationId: string }
// -> { ok: true, data: { jobId: string | null } }
{ cmd: 'RemoveDeduplicationKey', queue: string, deduplicationId: string }
// -> { ok: true, data: { count: number } }
{ cmd: 'RemoveJobDeduplicationKey', id: string }
// -> { ok: true, data: { removed: boolean } }

The job-owned form removes a key only when the requested job is still its registered owner.


{ cmd: 'MoveToWaitingChildren', id: string, token?: string }
// -> { ok: true, data: { moved: true } }

The job must be active. The transition releases its active resources and persists the parked state. If the job has a lock, token must match it.


Per-queue stall detection configuration. Numeric fields: stallInterval, maxStalls, gracePeriod (numeric strings are coerced, non-numeric values are dropped).

Request:

{ cmd: 'SetStallConfig', queue: string, config: { stallInterval?: number, maxStalls?: number, gracePeriod?: number } }
{ cmd: 'GetStallConfig', queue: string }

Response: { ok: true } for set, { ok: true, config: {...} } for get.

Per-queue DLQ configuration. Numeric fields: autoRetryInterval, maxAutoRetries, maxAge, maxEntries.

Request:

{ cmd: 'SetDlqConfig', queue: string, config: { autoRetry?: boolean, autoRetryInterval?: number, maxAutoRetries?: number, maxAge?: number | null, maxEntries?: number } }
{ cmd: 'GetDlqConfig', queue: string }

Response: { ok: true } for set, { ok: true, config: {...} } for get.