Skip to Content
HTTP API

HTTP API

Base URL: http://localhost:<PORT> (example uses 9000).

All /api/jobs/* routes need:

  • Content-Type: application/json
  • Signature header (see Request signing) over the exact request body

GET /api/health needs no signature.

Which scheduling path

NeedUseRecurs?
Fixed recurring work (daily digest, nightly cleanup)YAML cron_jobs in ConfigurationYes, until you change config and restart
Run once, now or at a known timestampPOST /api/jobs/one-off (optional runAt)No
Run once at the next match of a cron expressionPOST /api/jobs/scheduleNo. Call again if you need another occurrence

Examples: publish a post at 15:00 tomorrow → one-off + runAt. Send a digest every day at 08:00 → YAML cron_jobs. “Next Monday 08:00 only” from app logic → /schedule.

Health

curl -s http://127.0.0.1:9000/api/health
{ "ok": true }

503 with { "ok": false } if pg-boss is not ready.

One-off jobs

POST /api/jobs/one-off202

Body: non-empty array.

FieldRequiredNotes
idempotencyKeyYesstring
queueYesmust exist in YAML
payloadNoobject, default {}
runAtNoISO-8601 with offset. Future → delayed; past/omitted → now

Run now

[ { "idempotencyKey": "invoice-42-send", "queue": "WEBHOOK_DELIVERY", "payload": { "invoiceId": 42, "action": "send" } } ]

Response:

{ "jobs": [{ "jobId": "pg-boss-job-id" }] }

Delayed

[ { "idempotencyKey": "invoice-42-reminder", "queue": "WEBHOOK_DELIVERY", "payload": { "invoiceId": 42, "action": "remind" }, "runAt": "2026-03-23T14:30:00Z" } ]

Batch

You can send multiple items in one array. They are processed sequentially. If item 2 fails validation or enqueue, earlier items in that request may already be stored.

[ { "idempotencyKey": "order-1-fulfill", "queue": "WEBHOOK_DELIVERY", "payload": { "orderId": 1 } }, { "idempotencyKey": "order-2-fulfill", "queue": "WEBHOOK_DELIVERY", "payload": { "orderId": 2 } } ]

Idempotency

Submitting the same idempotencyKey again cancels the previous job (best effort) and enqueues a new one. It does not return the old job unchanged.

One-off jobs keep their idempotency row after success until you delete or replace the key.

Schedule next cron occurrence

POST /api/jobs/schedule201

Enqueues a single delayed job for the next time the cron expression fires. It does not create a recurring schedule. For recurring work, use YAML cron_jobs.

FieldRequiredNotes
idempotencyKeyYes
queueYes
scheduleYescron string
timezoneNodefault UTC
payloadNoobject
[ { "idempotencyKey": "user-9-digest", "queue": "REPORT_DIGEST", "schedule": "0 8 * * *", "timezone": "Africa/Lagos", "payload": { "userId": 9 } } ]

Response:

{ "jobs": [{ "id": "user-9-digest" }] }

id is the idempotency key, not the pg-boss job id.

After a successful delivery, the idempotency row for schedule jobs is removed. To fire again later, call schedule again (or use static cron).

Signed Node call:

import crypto from 'node:crypto'; const secret = process.env.REQUEST_SIGNING_SECRET; const body = JSON.stringify([ { idempotencyKey: 'user-9-digest', queue: 'REPORT_DIGEST', schedule: '0 8 * * *', timezone: 'Africa/Lagos', payload: { userId: 9 }, }, ]); const t = Math.floor(Date.now() / 1000); const v1 = crypto .createHmac('sha512', secret) .update(`${t}.`) .update(body) .digest('hex'); const res = await fetch('http://127.0.0.1:9000/api/jobs/schedule', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-bq-queue-request-signature': `t=${t},v1=${v1}`, }, body, }); // 201 { jobs: [ { id: 'user-9-digest' } ] }

Your handler receives data with the payload plus idempotencyKey (for example { userId: 9, idempotencyKey: 'user-9-digest' }).

Delete / cancel

POST /api/jobs/delete200

Body is a single object (not an array):

{ "idempotencyKey": "user-9-digest" }
{ "deleted": true }

Missing key → 404 { "error": "Job not found", "idempotencyKey": "..." }.

import crypto from 'node:crypto'; const secret = process.env.REQUEST_SIGNING_SECRET; const body = JSON.stringify({ idempotencyKey: 'user-9-digest' }); const t = Math.floor(Date.now() / 1000); const v1 = crypto .createHmac('sha512', secret) .update(`${t}.`) .update(body) .digest('hex'); await fetch('http://127.0.0.1:9000/api/jobs/delete', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-bq-queue-request-signature': `t=${t},v1=${v1}`, }, body, });

Use this when a user cancels a scheduled publish or you need the key free without waiting for delivery.

Errors

StatusWhen
400Bad body shape, unknown queue, invalid cron, invalid runAt
401Missing/invalid signature
404Delete target not found
503Boss not ready
500Enqueue failure

Unknown queue responses include an index for the failing array item.

Curl

Signing with shell tools is easy to get wrong. Prefer the Node createHmac example above or Request signing. Once you have BODY and HEADER_VALUE:

curl -s -X POST http://127.0.0.1:9000/api/jobs/one-off \ -H 'Content-Type: application/json' \ -H "x-bq-queue-request-signature: ${HEADER_VALUE}" \ -d "${BODY}"
Last updated on