Wiring
Island Pulse uses a single JSON field, job_id, to decide whether an incoming webhook should start a new Live Activity or update an existing one. Getting this right is the key to a clean integration.
The job key
Every POST to /notify should include job_id when you need more than one concurrent job, or when you want a stable label for that run.
- Format:
1–64characters. Allowed characters: letters (A–Z,a–z), digits (0–9), underscore (_), and hyphen (-) only. No spaces or other symbols. - Omit
job_idto use the implicit keydefault, which is fine when you only ever have one activity at a time.
Two POSTs with the same job_id target the same Live Activity. Two POSTs with different job_id values produce independent activities. Invalid job_id values return 400 INVALID_JOB_ID with an error message (no silent fallback to alternate field names).
Start → update → end lifecycle
You don’t need to manage state yourself. Island Pulse tracks whether a Live Activity is already running for a given job key:
- No activity running → Island Pulse starts a new Live Activity on your phone.
- Activity already running → Island Pulse updates it in place.
action: "end"→ Island Pulse dismisses the activity.
In practice this means you can send the exact same payload structure throughout a job’s lifetime, Island Pulse handles the routing automatically.
# Step 1, first POST: no activity running, Island Pulse starts onecurl -X POST "YOUR_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{"job_id": "nightly-build", "message": "Build started", "status": "running"}'
# Step 2, subsequent POSTs: activity is running, Island Pulse updates it in placecurl -X POST "YOUR_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{"job_id": "nightly-build", "message": "Running tests…", "status": "running", "progress": 0.4}'
# Step 3, finish the jobcurl -X POST "YOUR_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{"job_id": "nightly-build", "message": "All 142 tests passed", "status": "success"}'Naming conventions
Use short, stable IDs such as github-deploy, api-health, or daily-report. The same string is shown as the task label on the Live Activity card.
| Pattern | Example job_id |
|---|---|
| CI pipeline | github-deploy, gitlab-staging |
| Uptime monitor | api-health, db-health |
| Cron job | daily-report, invoice-sync |
| Metric pulse | mrr-today, signups-today |
Multiple concurrent jobs (Pro)
Free tier is limited to one concurrent Live Activity. If you POST a start for a second job while the first is still running, the request is rejected with a 403 TIER_LIMIT_EXCEEDED error.
Pro tier removes this limit (up to the iOS system cap of 5 simultaneous Live Activities per app). Run as many independent job_id values as you need, each one is tracked and updated separately.
# Pro: three independent jobs running at the same timecurl -X POST "YOUR_WEBHOOK_URL" -d '{"job_id": "deploy-prod", ...}'curl -X POST "YOUR_WEBHOOK_URL" -d '{"job_id": "db-backup", ...}'curl -X POST "YOUR_WEBHOOK_URL" -d '{"job_id": "report-gen", ...}'Activity session length
Live Activities are capped by iOS at 8 hours. Pro users get automatic session renewal, if a session expires, the next update seamlessly starts a fresh one. Free users’ activities expire after 8 hours without renewal.
On the Free tier, if a session expires and a new activity runs into the concurrent limit, you’ll get a TIER_LIMIT_EXCEEDED error until the first activity is dismissed.
Custom notification title
When a new activity starts, iOS briefly shows a notification banner. By default, the banner title is your job_id string. You can override it with alertTitle:
{ "job_id": "deploy-prod", "alertTitle": "Vercel Deploy", "message": "Build queued", "status": "running"}alertTitle only applies on the initial start push. Update pushes are silent by design.