Skip to content

Management API

@monque/management provides HTTP endpoints for job listings, queue counts, scheduler health, and job actions. Use the same Monque instance that processes your application’s jobs so worker activity and health describe that scheduler.

For Express, start with Express API setup. To add a browser interface, see Dashboard setup.

bun add @monque/management @monque/core mongodb

Create a Management handler from an initialized instance:

import { createManagementSurface } from "@monque/management";

const management = createManagementSurface<{ canManageJobs: boolean }>({
  monque,
  authorize: ({ action, context }) => action === "read" || context.canManageJobs,
});

const result = await management.openApiHandler.handle(request, {
  context: {
    managementContext: { canManageJobs: false },
  },
});

if (result.matched) {
  return result.response;
}

request is a standard Request supplied by your HTTP server. Return the response through that server and pass unmatched requests to its other routes. Authenticate requests before calling the handler and derive managementContext from the authenticated session.

The Fetch handler returned by createManagementSurface and the router returned by createManagementExpressRouter enforce the origin checks and body limits below. If you mount createManagementRouter with a custom oRPC HTTP handler, configure both protections on that handler.

The HTTP handler rejects mutations with an untrusted Origin using 403, before reading the body or invoking authorization and job operations. Same-origin browser requests work by default. Other origins, including sibling subdomains, must be explicitly trusted:

const management = createManagementSurface({
  monque,
  trustedOrigins: ["https://dashboard.example.com"],
});

Each entry must be an exact HTTP or HTTPS origin, including a port when required, without a trailing slash, path, credentials, query, or fragment. Wildcards are not accepted. Malformed and null request origins are always rejected. The default same-origin check uses the supplied Request URL, so adapters must preserve its correct scheme and host.

Server clients without Origin remain supported. When Origin is absent, browser Sec-Fetch-Site values of same-site, cross-site, or an invalid value are rejected; same-origin and none are accepted. Read endpoints are unaffected. Authentication and operation authorization are still required, and CORS remains host-owned. For a trusted cross-origin Dashboard, allow only that origin in credentialed CORS and configure cookies for the intended browser access.

Management accepts request bodies up to 64 KiB by default. Larger bodies return HTTP 413 before operation authorization, job lookups, or mutations. The limit counts bytes as the body arrives, including requests without a Content-Length header.

Set maxBodySize in bytes when creating a Management Surface or Express router:

const management = createManagementSurface({
  monque,
  maxBodySize: 32 * 1024,
});

Use a nonnegative safe integer. Responses, including job payloads, are not limited by this setting. Configure your proxy and host body parser limits too: if middleware such as express.json() parses a body before Management, its original allocation has already happened, and Management checks the adapter’s serialized body instead.

Paths are relative to the Management router’s mount URL:

MethodPathOperation
GET/api/v1/healthScheduler health
GET/api/v1/capabilitiesRead-only mode and available actions
GET/api/v1/processingLocal pause state and scheduler identity; optional name
POST/api/v1/processing/actions/pausePause all local workers or one job name
POST/api/v1/processing/actions/resumeResume all local workers or one job name
GET/api/v1/queue-viewsJob counts grouped by name and local worker activity
GET/api/v1/jobsCursor-paginated jobs
GET/api/v1/jobs/statsJob counts, optionally filtered by name
GET/api/v1/jobs/{id}Job detail and payload
POST/api/v1/jobs/{id}/actions/cancelCancel a pending job
POST/api/v1/jobs/{id}/actions/retryRetry a failed or cancelled job
POST/api/v1/jobs/{id}/actions/rescheduleChange a pending job’s run time
POST/api/v1/jobs/{id}/actions/priorityChange a pending Job’s priority
DELETE/api/v1/jobs/{id}Delete a job
POST/api/v1/jobs/actions/cancelCancel jobs matching a selector
POST/api/v1/jobs/actions/retryRetry jobs matching a selector
POST/api/v1/jobs/actions/deleteDelete jobs matching a selector
POST/api/v1/jobs/actions/selectedAct on specific job IDs

Job IDs are MongoDB ObjectId strings. Dates in requests and responses use ISO 8601 UTC strings, such as 2026-09-19T12:00:00.000Z. Job responses use payload for the job’s data and failureReason for its latest failure. Absent lock and heartbeat timestamps are null; absent heartbeatInterval, repeatInterval, timezone, and uniqueKey fields are omitted.

Jobs using renewable leases also include leaseExpiresAt, an ISO UTC deadline measured by MongoDB. Heartbeats extend it while the claim remains valid. Jobs using absolute lockTimeout have no lease deadline field. Details, full and summary listings, and job action responses use the same representation.

Queue views include the local worker’s effective paused state, hasSchema flag, and retry settings (maxRetries, baseRetryInterval, maxBackoffDelay). Delays are in milliseconds. These settings describe new executions on the attached scheduler; already-running jobs retain the policies captured when they started. A global pause also makes each local worker appear paused. Other scheduler instances may still run jobs. Custom scheduler facades can omit these fields when the information is unavailable. Validator functions and schemas are never serialized.

Recurring jobs expose their configured IANA timezone in detail, full and summary listings, and job action responses. When omitted, the schedule uses the worker server’s local timezone. This metadata does not change UTC timestamp serialization. Rescheduling changes only the next run time; it preserves the recurring cron expression and timezone.

Jobs stopped by a worker’s NonRetryableError use the existing failed status and expose failureReason and the actual failCount, which may be only one. Existing single and bulk retry endpoints remain available. No Management or Express adapter upgrade is required for this behavior; the worker must use core 1.14.0 or later.

Read /api/v1/processing first, then send its instanceId to either control endpoint:

{ "instanceId": "scheduler-id-from-the-state-response", "name": "send-email" }

Omit name to control the whole attached scheduler. These controls are process-local: other instances continue processing. Running handlers finish and their leases keep renewing. Resuming the instance preserves individual worker pauses; resuming a worker cannot override an instance-wide pause. Responses include the effective paused state and globallyPaused. Pauses do not survive process restarts.

GET /api/v1/capabilities?name=send-email checks processing permissions for that worker using the attached scheduler’s identity. Without name, it checks instance-wide controls. The other job-action capability checks remain unscoped.

The pause and resume authorization actions receive name and instanceId. Read-only mode denies both. Capabilities report them unavailable when the scheduler facade omits the corresponding methods or getProcessingState(). State reads require read access.

A different scheduler identity returns 409 without changing processing. Behind a load balancer, use instance-specific or sticky routing. Refresh state after a conflict and confirm the intended instance before retrying. Give each scheduler a unique schedulerInstanceId if overriding the generated identifier.

GET /api/v1/jobs accepts:

ParameterMeaning
nameExact job name; substring and payload search are not supported.
statuspending, processing, completed, failed, or cancelled. Repeat the parameter for multiple statuses.
limitPositive page size; defaults to 50 and values above 100 are capped at 100.
cursorOpaque cursor returned by the previous response.
sortByidentifier, createdAt (default), updatedAt, or nextRunAt.
sortDirectionasc or desc; defaults to desc.
createdAtFrom, createdAtToInclusive creation-time range.
updatedAtFrom, updatedAtToInclusive update-time range.
nextRunAtFrom, nextRunAtToInclusive scheduled-time range.
viewfull (default) or summary.
/ops/api/v1/jobs?name=send-email&status=failed&limit=20&sortBy=createdAt&sortDirection=desc&view=summary

Responses include jobs, cursor, hasNextPage, and hasPreviousPage. Keep the same filters and sort when following a cursor. Start a new page sequence when changing them.

view=summary returns metadata with payload: null and skips payload serialization. With a scheduler that supports summary queries, it also avoids loading payloads from MongoDB. Use the job detail endpoint when you need the payload. Full listings retain payload serialization and redaction.

Send POST /api/v1/jobs/{id}/actions/priority with a signed safe integer:

{ "priority": -3 }

Success returns the updated Job. The operation applies only to pending Jobs and leaves their scheduled time unchanged. For a recurring Job, the new priority also applies to future runs. Invalid input returns 400, a missing Job returns 404, and a Job claimed before the update returns 409. Processing and terminal Jobs also return 409.

The setJobPriority authorization action receives the target job and request context. The matching capability, actions.setJobPriority, reflects method support, read-only mode, and authorization for the request. A scheduler without setJobPriority, a denied action, or a read-only surface returns 403. Capability checks have no target Job; the mutation checks authorization again with the current Job.

The Dashboard shows Change priority with its other Job actions. Enter a signed safe integer and confirm before applying it. Only pending Jobs with an available capability can be changed. After success, the table and detail refresh; unrelated valid selections remain. A stale state or authorization change shows the server error instead of a success message.

Post a JSON body to /api/v1/jobs/actions/selected:

{
  "action": "retry",
  "ids": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"]
}

Supported actions are cancel, retry, delete, reschedule, and priority. Rescheduling requires nextRunAt; a priority change requires priority. Requests accept 1 to 100 IDs before deduplication. Duplicate IDs are processed and counted once, including IDs that differ only in letter case. Only the supplied IDs are targets; filters and cursor pages never widen the selection.

The response reports successful changes and individual failures:

{
  "count": 1,
  "errors": [
    {
      "jobId": "507f1f77bcf86cd799439012",
      "status": 409,
      "error": "Cannot retry job in status 'processing'"
    }
  ]
}

A response can contain both successes and failures. Successful changes are not rolled back when another job fails. Check errors even when the request returns HTTP 200.

Send POST /api/v1/jobs/actions/selected with one priority for the selected Jobs:

{
  "action": "priority",
  "ids": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"],
  "priority": 10
}

The priority must be a signed JavaScript safe integer. Invalid input returns 400 before any Job changes. Each target must still be pending when the API applies the change; future Jobs are eligible too. The operation keeps each Job’s scheduled time and uses the same setJobPriority permission and capability as a single-Job change. Read-only mode and schedulers without that method reject the action with 403.

Authorization first checks setJobPriority with the selected ids, then checks the same action with each current job and request context. A missing Job returns a per-Job 404, a denied Job returns 403, and a processing or terminal Job returns 409. A Job claimed before its update also returns 409. Other selected Jobs can succeed; failed and unselected Jobs keep their values. There is no selector-wide priority action.

In the Dashboard, select pending Jobs and choose Change priority. The bulk control is available only when every selected Job is pending and the action is enabled. Enter one priority, then review the selected count and value before confirming. The server rechecks state and permissions, so changes after selection can produce partial results.

After the response, the Dashboard refreshes priorities, clears successful IDs from the selection, and reports success and failure counts with the first error. Failed IDs and unrelated selections remain selected while their rows are still in the current view. Polling preserves those selections and removes IDs whose rows disappear from the view.

The /actions/cancel, /actions/retry, and /actions/delete endpoints accept a selector:

{
  "name": "send-email",
  "status": ["failed", "cancelled"],
  "olderThan": "2026-09-01T00:00:00.000Z"
}

Selectors support name, status, olderThan, and newerThan. Date bounds refer to job creation time. These operations affect matching jobs across all pages. An empty selector matches all jobs eligible for the action. Use selected job actions when you want to target only explicit IDs or require authorization for each job.

Authentication establishes who is making a request. Your HTTP server must handle it before Management. authorize then decides which operations that user may perform:

const management = createManagementSurface<{ canManageJobs: boolean }>({
  monque,
  authorize: ({ action, context }) => action === "read" || context.canManageJobs,
});

Available action names are read, cancel, cancelBulk, retry, retryBulk, reschedule, setJobPriority, delete, and deleteBulk. The callback receives:

InputAvailable when
contextEvery authorization check
jobA single job is targeted
selectorA selector-based bulk operation is requested
idsThe initial check for a selected-job request

Capabilities checks do not include a job, selector, or IDs. Handle those inputs as optional. Selected cancel, retry, and delete requests first check the corresponding bulk permission, then the individual permission for each job. Selected rescheduling checks reschedule at both stages; selected priority changes check setJobPriority at both stages. Selector-based bulk actions check the selector once, without per-job checks.

Set readOnly: true to reject all mutations with HTTP 403. It does not bypass read authorization. Without authorize, supported operations are allowed unless read-only mode blocks them. Unsupported actions also return 403.

Read authorization allows or denies the read operation as a whole; it does not filter jobs by tenant or hide individual Queue Views. Health reports the scheduler’s running state and is available without a read authorization check, so protect the entire HTTP mount with authentication middleware when needed.

parallelCapabilityChecks: true runs capability authorization checks concurrently. Enable it only if your callback can run independent checks without relying on their order. The default is sequential.

Choose which payload fields API clients can see:

Install Zod with bun add zod if your application does not already use it.

import { z } from "zod";

const EmailPreview = z.object({ subject: z.string() });
const management = createManagementSurface({
  monque,
  serializePayload: async () => null,
  serializePayloadByJobName: {
    "send-email": async ({ job }) => EmailPreview.parse(job.data),
  },
});

This returns only the email subject and hides all other payloads. A serializer registered for a job name takes precedence over the default serializer. Both receive job and request context, so you can decide which fields a user may see. Redaction affects API responses and the dashboard, not the payload stored in MongoDB. Without a serializer, Management returns the job data.

Use the OpenAPI document served by the Express adapter with an OpenAPI client generator. To generate a document yourself, call generateManagementOpenApiDocument().

TypeScript clients can import managementContract and DTO schemas from @monque/management/contract. See the Management API reference for exported types and functions.

To enqueue jobs, register workers, or start and stop the scheduler, use the Core API operations.