Webhooks
A webhook URL is a private address that counts a finished item each time it's called. Anything that can call a URL can use it: a CI job, an automation service, a phone shortcut.
Create a webhook URL
- Sign in to the game.
- Open Account > Finished items > Webhooks and create one. You can have up to 5.
- Copy it straight away: it's shown once. It looks like this:
https://ironvaleidle.com/api/hook/3f2b8c1e-7a4d-4e09-9b6f-0c5d2e8a1f47
Treat the URL like a password: anyone who has it can send items for you. If it leaks, regenerate it (the old one stops working) or delete it.
Calling it
Use GET or POST. With no parameters at all, a call counts one finished item of type
item, with the webhook's label as its source. These optional fields describe it better. They go in the URL
as query parameters only: a request body is never read for them, so a source or id inside a JSON
or form body is ignored.
| Field | Meaning |
|---|---|
source | The tool, such as github or shortcuts. Defaults to the webhook's label |
type | What was finished, such as pr_merged or task_completed (it sets the weight). Defaults to item |
id | The tool's own id for the item, so a repeat counts once |
at | When it was finished: ISO 8601 or Unix seconds. Defaults to now |
n | How many items this call stands for, 1 to 20. Defaults to 1 |
Repeats count once. If there's no id in the URL, Ironvale uses the delivery header the sender adds
(Idempotency-Key, Webhook-Id, X-GitHub-Delivery, X-Gitlab-Event-UUID,
Linear-Delivery, X-Atlassian-Webhook-Identifier or X-Request-Id) or else a hash of the
method, the URL and the first 16 KB of the body. Identical requests within 10 minutes count once, so a retried
delivery isn't counted twice, while tools like Zapier or IFTTT that send a different body for each event are counted
separately. Anything past the first 16 KB of a body is never read.
Nothing else is kept. The body is not parsed or stored, and the headers and URL are not stored. Only the same four things as every finished item are stored: source, a hashed id, type and time.
GitHub's setup ping (the event it sends when you add a webhook) is acknowledged and ignored.
Responses
| Status | Meaning |
|---|---|
202 | Received. The body says what happened: { "accepted": 1, "duplicate": 0, "ignored": 0 }, plus a reason when items were ignored. Items already counted show as duplicate; items over a limit are acknowledged and ignored (so senders don't retry) |
400 | A URL field is malformed. The code is bad_source, bad_type, bad_id, bad_at or bad_n, or reserved_source for source=github.com (the built-in GitHub connection's) |
404 | No such webhook: it was deleted or regenerated |
413 | The request declares a body over 1 MB |
The reason on an ignored item is one of: ping, daily_request_limit,
rate_limited, daily_limit, too_many_sources, too_old or
in_future.
Limits
- 200 webhook calls a day per player, across all your webhook URLs (
daily_request_limit). - 30 calls a minute per webhook (
rate_limited). - 500 new items a day across all your sources, shared with your other sources (
daily_limit). More than that is ignored and never stored. - 20 items per call (
n). - An item finished more than 35 days ago, or more than 10 minutes in the future, is ignored.
Examples
Replace <your-hook> with your webhook's id.
curl
curl -X POST "https://ironvaleidle.com/api/hook/<your-hook>?source=cli&type=task_completed&id=release-2026-10-29"
GitHub
A plain repository webhook fires for every pull request event (opened, edited, closed), so each would count. To count only
merges, use a small Actions workflow and keep the URL in a repository secret named IRONVALE_HOOK:
# .github/workflows/ironvale.yml
on:
pull_request:
types: [closed]
jobs:
ironvale:
if: github.event.pull_request.merged
runs-on: ubuntu-latest
steps:
- run: curl -fsS -X POST "$HOOK?source=github&type=pr_merged&id=${{ github.event.pull_request.node_id }}"
env:
HOOK: ${{ secrets.IRONVALE_HOOK }}
For events that only happen when work is done, such as Releases, a repository webhook works directly: under
Settings > Webhooks, set the payload URL to
https://ironvaleidle.com/api/hook/<your-hook>?source=github&type=deploy and pick only that event.
The X-GitHub-Delivery header keeps redeliveries from counting twice.
GitLab
A CI job on your default branch counts each merge:
# .gitlab-ci.yml
ironvale:
stage: .post
rules:
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- curl -fsS -X POST "$IRONVALE_HOOK?source=gitlab&type=pr_merged&id=$CI_COMMIT_SHA"
Store the URL as a masked CI/CD variable named IRONVALE_HOOK. For a project webhook (Settings >
Webhooks), choose an event that means finished, such as Releases events; the
X-Gitlab-Event-UUID header de-duplicates retries.
Zapier
Trigger on whatever means done in your app (a task completed, a card moved to Done). Add the action Webhooks by Zapier
> POST, put the fields in the URL:
https://ironvaleidle.com/api/hook/<your-hook>?source=todoist&type=task_completed&id= followed by
the task's id from the trigger.
IFTTT
Use the Webhooks service's Make a web request action, method POST, with the fields in the URL:
https://ironvaleidle.com/api/hook/<your-hook>?source=ifttt&type=task_completed
iOS and macOS Shortcuts
Make a shortcut called "Done for today" with one action, Get Contents of URL: method POST, URL
https://ironvaleidle.com/api/hook/<your-hook>?source=shortcuts&type=task_completed&n=3. Change
n to however many things you finished, up to 20. Without an id, two identical requests in a row look
like a repeat, so add something unique such as the date and time (&id= followed by the
Current Date variable).