Skip to content
Get API Key

Guides

Webhooks

Add a webhook in Settings, check each notice's signature, answer in time, and catch up after one was missed.

Updated
View as Markdown

A webhook tells your server when a task starts working or stops, so your program does not have to keep asking. You add your server's address in Settings. When a task changes, Little Worker posts a small signed notice to that address. The notice names the task and carries no task data, so your server reads the task with its API key to learn what happened.

This guide builds a small server that checks each notice, answers it, reads the task, and catches up if it missed any.

Before you start

  • A key in LITTLE_WORKER_API_KEY. API keys shows how to make one.
  • A public https:// address that reaches your server. Little Worker refuses an address that leads inside a private network, so to try the server on your own computer, use a tool that gives it a public https:// address.
  • For curl: curl and jq. A shell script is not a good place to receive notices, so the curl tabs show the checks and reads that go with the server.
  • For TypeScript: Node 22.18 or later, which runs a .ts file as it is. Save the program as webhooks.ts and run node webhooks.ts.
  • For Python: Python 3.9 or later and the requests library (pip install requests). Save the program as webhooks.py and run python3 webhooks.py.

Step 1: Add the webhook

Open Settings, then Webhooks, and choose Add Webhook.

  1. In Address, enter the https:// address of your server, such as https://example.com/little-worker. It may be up to 2,048 bytes. An address that cannot be reached from the internet is refused when you add it.
  2. In Description, add a note for yourself if you like, up to 200 characters.
  3. Under Send a Notice When, choose A task starts working, A task stops working, or both. A task stops when it is done, needs you, or reaches its limit.
  4. Choose Add. Copy Your Signing Key shows a key that starts with whsec_. Copy it.

The signing key is shown once. Your server uses it to check that each notice came from Little Worker. Keep it in your server's secrets, in LITTLE_WORKER_SIGNING_KEY:

Shell
export LITTLE_WORKER_SIGNING_KEY="whsec_..."

A workspace holds up to 10 webhooks. In the list, each webhook has a Send Notices switch, and Details shows its recent notices and has Send Test Notice, which posts a notice that is not about a task.

Step 2: Receive a notice and check its signature

Every notice is a POST with a JSON body and these headers:

Header What it holds
webhook-id The notice's id. It is the same on every try of the same notice.
webhook-timestamp When this try was signed, in seconds since 1970. Each try has a new one.
webhook-signature v1, and a base64 signature. More than one may come, separated by spaces.

The body looks like this. data.type says what happened, and data.task_id names the task:

JSON
{
  "type": "event",
  "id": "event_9d5c1f7e0b3a4c2d8e6f1a2b3c4d5e6f",
  "created_at": "2026-10-25T22:07:46.123Z",
  "data": {
    "type": "task.status_idle",
    "id": "task_01M5TTRJKWYAK3XHFX432K5MBV",
    "workspace_id": "wrk_01M5TTRH6V3N8QZ4YD7C2BKXPA",
    "task_id": "task_01M5TTRJKWYAK3XHFX432K5MBV",
    "sequence": "11"
  }
}
data.type When it is sent
task.status_running A task begins running: its work was accepted, or it went on after it had stopped.
task.status_idle The worker stops: it is done, it failed, you stopped it, it is out of budget, it reached a run limit, or it waits for you.
webhook.test You chose Send Test Notice. It names no task.

sequence is the number of the event that caused the notice, the same one the task's events carry.

Notices are signed by the Standard Webhooks scheme, so any library for it checks them. By hand, the signature is an HMAC-SHA256, in base64, of the three parts webhook-id, webhook-timestamp and the raw body, joined by dots. The key is what follows whsec_ in your signing key, decoded from base64. Check the raw body exactly as it came, before you parse it, and refuse a notice signed more than 5 minutes ago.

# A post with no signature must be refused: this answers 401.
curl -i -X POST http://127.0.0.1:8080 \
  -H "content-type: application/json" \
  -d '{"type":"event"}'

Start the program. In Settings, open the webhook's Details and choose Send Test Notice. The program prints webhook.test, and Recent Notices shows the notice as delivered. Your own post with no signature, as in the curl tab, is refused with 401.

Step 3: Read what changed

A notice says that a task changed, not how. Read the task, and its events (List events) if you need more. Replace the handle function, in the same place in the file, with these two functions. They read the task when it stops:

# The task named by the notice:
curl -s https://core.thelittleworker.com/api/v1/tasks/$TASK_ID \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  | jq '{id, state, stop_reason, result_summary}'

# The event that caused the notice: send its sequence, minus one, as after.
curl -s "https://core.thelittleworker.com/api/v1/tasks/$TASK_ID/events?after=10&limit=1" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  | jq '.data[0]'

The task's state.status says whether the work is done, or needs_you and why in state.needs. Follow a task as it works says what each stop_reason means.

Step 4: Answer in time

Answer a notice with any 2xx status within 10 seconds. A slower answer, or any other status, counts as a failure, and Little Worker tries the notice again. Reply first and do the work after, as the server above does, so a slow task read never makes an answer late.

Try When it is made
1 When the notice is ready.
2 to 5 After about 10 seconds, 1 minute, 10 minutes and 1 hour. Each wait may be up to 20 percent longer or shorter.

After the fifth failure Little Worker stops trying that notice. Every try carries the same webhook-id, so keep the ids you handled and drop a repeat, as the server does with handled. A notice that reaches you twice is normal. A set in memory is enough for a small program, and a database keeps ids across restarts.

Do not rely on the order. Notices are sent side by side and a retry arrives late, so a task.status_running can arrive after the task.status_idle that followed it. Read the task to learn where it stands now.

Step 5: Catch up after a missed notice

A notice can be missed: your server was down, an answer was late for every try, or the webhook was off. Nothing is queued for a webhook that is off, and turning it on again does not send what it missed. So have your program look, now and then, and after every restart.

List the idle tasks made since a time you choose, and run each one through the same code as a notice. Pick a time earlier than your longest task, because the list goes by when a task was made:

curl -s -g "https://core.thelittleworker.com/api/v1/tasks?statuses[]=idle&created_at[gte]=2026-10-24T00:00:00Z&limit=100" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  | jq '.data[] | {id, state, stop_reason}'

Run it once when the program starts. In Python, put the call above the line that starts the server, since that line does not return. One page holds up to 100 tasks. next_page is the page to send for more. For a single task, list its events with after set to the last sequence you handled, and you get everything since.

When Little Worker turns a webhook off

A webhook that cannot be reached is turned off, so your server is not hammered. Settings shows Off, and for the first three causes below it says why. Fix the cause, then turn the Send Notices switch back on.

Cause What to do
Your server answered with a redirect. Little Worker never follows one. Use the address that answers directly.
The address leads inside a private network. Use a public https:// address.
Every notice failed for 24 hours. One 2xx answer resets that clock. Fix your server.
The person who added the webhook left the workspace. They saw its signing key, so it cannot be turned on again. Add a new webhook.

Next steps

Type a few words, like “give a task” or “errors”.