API reference
Workers
Claim jobs and report on them over HTTP, for a worker in any language.
A worker does two things: it claims jobs, and it reports on each job it holds, until the job is done. The SDKs' workers do both for you, renewing leases and retrying reports. Use these endpoints to write a worker in a language without an SDK.
Both need a token with the work scope.
Claim jobs
POST /v1/spaces/{space}/jobs:claim · scope work
Hands out up to count jobs, oldest first. With waitMs, the call waits for work to arrive instead of answering empty at once.
countintegerrequiredHow many jobs you can take, from 1 to 1,000. The answer also stops at 8 MiB of payloads.
namesstring[]Only jobs of these names, at most 32. Leave it out for any job in the space.
waitMsintegerHow long to wait for work when there's none, up to 60 seconds.
leaseMsintegerdefault: each job's timeoutHow long you hold each job before it goes to someone else, unless you renew it.
curl -s "$EMIT_URL/v1/spaces/emails/jobs:claim" \
-H "Authorization: Bearer $EMIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"names": ["send-welcome-email"], "count": 10, "waitMs": 30000}'{
"jobs": [
{
"id": "01M3QSMMXQ00ZSGYA6VWM3NGQA",
"attempt": 1,
"name": "send-welcome-email",
"payload": { "userId": "usr_42", "email": "ada@example.com" },
"leaseUntilMs": "1790726572493"
}
]
}Keep each job's id and attempt: every report names both. An empty jobs means nothing arrived in time; ask again. Give your HTTP client a timeout longer than waitMs.
A claim whose answer is lost
If the connection drops after emit handed out jobs, nobody knows which ones. Those jobs wait until their leases run out, and each loses an attempt. Keep count to what you'll work on, and leases no longer than you need.
Report on jobs
POST /v1/spaces/{space}/jobs:report · scope work
Send one report, an array of them, or {"reports": [...]}. A report can renew the lease, carry progress, an event and log lines, and end the attempt, all at once.
idstringrequiredThe job's id.
attemptintegerrequiredThe attempt you were handed. A report for an attempt that isn't the current one is refused, so a worker that lost its lease can't speak for the one that holds the job now.
touchobjectStill working: renew the lease. {"leaseMs": 60000} for a minute from now, or {} for the job's timeout. A lease is never shortened this way.
completeobjectDone: {"result": <any JSON>}. The result is at most 4 MiB.
failobjectThis attempt failed: error, a message; unrecoverable: true to fail the job for good; or retryAfterMs to try again after that wait (at most a day) instead of the space's policy.
releaseobject{}: give the job back unfinished. It's handed out again at once, and the attempt isn't counted.
progressobjectpercent (0 to 100) and message. Only the latest is kept.
eventJSONAn event to keep with the job, at most 4 KiB.
linesobject[]Log lines, up to 1,000 a report: level (TRACE, DEBUG, INFO, WARN, ERROR or FATAL), message (at most 4 KiB), and optional source and metadata (any small JSON).
Give at most one of touch, complete, fail and release.
curl -s "$EMIT_URL/v1/spaces/emails/jobs:report" \
-H "Authorization: Bearer $EMIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "01M3QSMMXQ00ZSGYA6VWM3NGQA", "attempt": 1,
"touch": {},
"progress": {"percent": 50, "message": "rendering"},
"lines": [{"level": "INFO", "message": "template rendered"}]
}'curl -s "$EMIT_URL/v1/spaces/emails/jobs:report" \
-H "Authorization: Bearer $EMIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "01M3QSMMXQ00ZSGYA6VWM3NGQA", "attempt": 1, "complete": {"result": {"messageId": "msg_5521"}}}'curl -s "$EMIT_URL/v1/spaces/emails/jobs:report" \
-H "Authorization: Bearer $EMIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id": "01M3QSMMXQ00ZSGYA6VWM3NGQA", "attempt": 1, "fail": {"error": "SMTP server timed out", "retryAfterMs": 30000}}'{ "outcomes": [{ "status": "RUNNING" }] }The request answers 200 with one outcome per report: the job's status after it, or a rejection. Only when every report in the request breaks a rule is the whole request refused, with 400.
reason |
What it means |
|---|---|
STALE |
Another attempt holds the job now. Stop working on it. |
NOT_RUNNING |
The job isn't held by anyone. |
ALREADY_FINISHED |
It finished already, for example because it was cancelled. Stop working on it. |
NOT_FOUND |
No such job. |
TOO_MANY_EVENTS |
The job has all the events its space allows. The rest of the report was applied. |
INVALID |
The report breaks a rule, which message names. The other reports in the request were applied. |
Keeping the lease
Renew the lease well before it runs out: the SDKs send a touch every half-lease. If a lease runs out, the attempt counts as failed and the job goes to another worker, even if the first one is still working. A later report from the first worker is answered STALE.
A job may be worked on twice this way, so make the work safe to repeat. See Leases and timeouts.