Callbacks
Webhooks, Slack notifications, and HTTP push for worker wake
Callbacks
Jobs can optionally specify a callbackUrl when created. When the job reaches a terminal state — completed, dead, or killed — the system sends a POST request to that URL.
When callbacks fire
| Terminal state | Trigger |
|---|---|
completed | Worker calls the complete endpoint |
dead | Job fails and has no retries remaining |
killed | Job is force-stopped via the kill endpoint |
Callbacks do not fire on intermediate failures that will be retried.
Callback payload
The POST body is JSON:
{
"jobId": "01JLQX...",
"spaceId": "01JLQ...",
"name": "process-video",
"status": "completed",
"attemptNumber": 0,
"result": { "output_url": "s3://bucket/output.mp4" },
"timestamp": "2025-02-24T10:35:00.000Z"
}For dead jobs, result contains the final error:
{
"jobId": "01JLQX...",
"spaceId": "01JLQ...",
"name": "process-video",
"status": "dead",
"attemptNumber": 3,
"result": { "error": "FFmpeg exited with code 1: out of memory" },
"timestamp": "2025-02-24T10:42:00.000Z"
}For killed jobs, result includes the kill metadata:
{
"jobId": "01JLQX...",
"spaceId": "01JLQ...",
"name": "process-video",
"status": "killed",
"attemptNumber": 1,
"result": { "reason": "manual stop" },
"timestamp": "2025-02-24T10:36:00.000Z"
}Request headers
Every callback request includes:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | emit.run/1.0 |
Any headers you set in callbackHeaders at job creation time are merged in:
{
"callbackUrl": "https://emit.run/webhooks/jobs",
"callbackHeaders": {
"Authorization": "Bearer whsec_abc123"
}
}Delivery tracking
Callback delivery is recorded as a job event:
| Event type | Meaning |
|---|---|
callback_sent | Request succeeded with 200 OK. Event data includes url, statusCode, and retryAttempt (0 on first try). |
callback_failed | Request send failed, or callback returned non-200. Event data includes url, error, statusCode, optional responseBody, retryAttempt, willRetry, and optional nextRetryAt. |
Inspect these events via GET /api/v1/jobs/:jobId in the events array.
Retry behavior
Callback delivery retries are automatic for failed delivery.
- Retries use exponential backoff (capped) for up to 50 retry attempts in a 24-hour window.
- Retries happen when the request cannot be sent (for example DNS/TCP/TLS/network errors) or when the callback responds with any non-
200status. - Only
200 OKis treated as delivered (callback_sent). callback_failedevents include retry metadata (retryAttempt,willRetry,nextRetryAt) so you can observe retry progress.
Slack Notifications
Each space can configure a Slack Incoming Webhook in dashboard settings.
Setup: Dashboard → Space → Settings → Slack Webhook
| Event | When it fires |
|---|---|
created | Job is accepted |
started | Worker acknowledges the job |
completed | Job completes successfully |
failed | Job failure is recorded (including retriable failures) |
dead | Final failure after retries are exhausted |
killed | Job is force-stopped |
Slack delivery is best-effort and asynchronous — job processing does not fail if Slack is unavailable.
HTTP Push (Worker Wake)
Spaces can configure an HTTP push endpoint for serverless worker wake signals.
Setup: Dashboard → Space → Settings → HTTP Push (Worker Wake)
When enabled, emit.run sends a lightweight webhook whenever a job becomes pending due to creation, schedule trigger, retry, or delivery timeout requeue.
{
"event": "job.pending",
"source": "emit.run",
"reason": "created",
"timestamp": "2025-03-02T01:23:45.000Z",
"spaceId": "01SPACE...",
"jobId": "01JOB...",
"jobName": "send-email",
"status": "pending",
"attemptNumber": 0,
"claim": {
"method": "POST",
"path": "/api/v1/jobs/01JOB.../claim",
"requiredScope": "jobs:poll"
}
}Push is best-effort and asynchronous. You can limit pushes to specific job names and add custom headers in space settings.