Skip to content
emitDocs

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.

countintegerrequired

How 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.

waitMsinteger

How long to wait for work when there's none, up to 60 seconds.

leaseMsintegerdefault: each job's timeout

How long you hold each job before it goes to someone else, unless you renew it.

Shell
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}'
JSON
{
  "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.

idstringrequired

The job's id.

attemptintegerrequired

The 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.

touchobject

Still working: renew the lease. {"leaseMs": 60000} for a minute from now, or {} for the job's timeout. A lease is never shortened this way.

completeobject

Done: {"result": <any JSON>}. The result is at most 4 MiB.

failobject

This 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.

progressobject

percent (0 to 100) and message. Only the latest is kept.

eventJSON

An 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}}'
JSON
{ "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.