Producers
A producer is any app that has work it wants done. It POSTs a task to a queue's endpoint,
gets back an id, and then either polls that id for status or subscribes to it on a socket and watches it
move. The examples below assume a resize queue on task.example.com.
Post a task
POST /<queue> with the item as a JSON body. The server validates it against the
queue's item schema; on success you get the new id and its
starting status. This needs post access: the
Bearer token of a listed user, unless the queue's taskhoster-post is public.
curl -fsS https://task.example.com/resize \
-H 'authorization: Bearer USER_TOKEN' \
-H 'content-type: application/json' \
-d '{"url":"https://ex.com/a.png","format":"jpg","width":800}'
{ "ok": true, "id": "9f2c1a0b7d4e6f83", "status": "pending" }
If validation fails you get 400 with the reasons, and no item is created:
{ "ok": false, "errors": ["url is required", "format: must be one of: png, jpg"] }
The id is a 16-hex-character string. Keep it: it is how you read the task's status and how you subscribe to it.
Read a task's status
GET /<queue>/items/<id> returns the full status view. This needs
fetch access: the Bearer token of a listed user.
curl -fsS https://task.example.com/resize/items/9f2c1a0b7d4e6f83 \
-H 'authorization: Bearer USER_TOKEN'
{
"ok": true,
"id": "9f2c1a0b7d4e6f83",
"status": "processing",
"postedBy": "alice",
"postedAt": 1751500000000,
"updatedAt": 1751500012000,
"finishedAt": null,
"attempts": 1,
"progress": { "percent": 40 },
"result": null,
"error": null
}
| Field | Meaning |
|---|---|
status | One of pending, processing,
finished or failed. See the lifecycle. |
postedBy | The name of the user who posted the
item, or null if it was posted publicly (anonymously). |
postedAt / updatedAt / finishedAt | Millisecond
timestamps. finishedAt is null until the item is terminal. |
attempts | How many times the item has been claimed; it rises each time a re-queued item is claimed again. |
progress | The last progress record a worker posted, or null. |
result | The result record once status is
finished, else null. |
error | Set when status is failed: e.g.
"timeout" from a queue timeout, or an in-band error your workers put in the result
schema. |
fetched clock. When the queue's retainFrom is "fetched", that is
when its retention countdown begins, so a result is kept until you have actually collected it.Watch a task live
Polling is fine, but for anything interactive open the monitor WebSocket
at GET /<queue>/monitor. Subscribe to one or more ids and the server pushes a
status message immediately, then again whenever a subscribed item changes. It needs fetch
access; because browsers can't set headers on a WebSocket, pass a user's token as
?token=.
const ws = new WebSocket("wss://task.example.com/resize/monitor?token=USER_TOKEN");
ws.onopen = () => {
ws.send(JSON.stringify({ subscribe: ["9f2c1a0b7d4e6f83"] }));
};
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
// { type: "status", id, status, postedBy, progress, result, error, ... }
if (msg.type === "status") {
console.log(msg.id, msg.status, msg.progress);
if (msg.status === "finished") {
console.log("done:", msg.result);
ws.send(JSON.stringify({ unsubscribe: [msg.id] }));
}
}
};
You can subscribe and unsubscribe ids at any time on the open socket; each
subscribe replays the current status once, so you never miss the state an item is already in. One socket
watches one queue: open one per queue you care about.
From application code
The HTTP surface is plain JSON, so any client works. In the browser or Node:
async function submit(task) {
const res = await fetch("https://task.example.com/resize", {
method: "POST",
headers: {
"authorization": "Bearer USER_TOKEN",
"content-type": "application/json",
},
body: JSON.stringify(task),
});
const { ok, id, errors } = await res.json();
if (!ok) throw new Error(errors.join("; "));
return id;
}
const id = await submit({ url: "https://ex.com/a.png", format: "jpg", width: 800 });
Sending binary as a blob
If a task carries binary (an image to process, a file to transform), declare a
blob field and send it as base64 text. It rides inside the
ordinary JSON body; the item file on the server stays plain text.
const bytes = new Uint8Array(await file.arrayBuffer());
const b64 = btoa(String.fromCharCode(...bytes));
await submit({ url: "https://ex.com/ignored", format: "png", blob: b64 });
max is a length limit on the base64 text (default 8 MiB).
Base64 is about a third larger than the raw bytes, so size the max accordingly.