Slug Pings
Send job heartbeats by slug with an API key in the Authorization header, auto-create jobs on first ping, and migrate off the deprecated key-in-URL slug route.
A slug ping reports a job heartbeat by the job's slug instead of its ping token. You authenticate with an API key, so one key can drive many jobs, and a job can be created automatically on its first ping.
Use slug pings when your jobs are defined in code and you would rather not copy a ping token per job. If you already have a job's ping URL, the plain token ping on signal.ionhour.com keeps working and needs no key.
Send a slug ping
Slug pings live on the API host. Send the key as a Bearer token:
curl -X POST https://api.ionhour.com/api/signals/s/nightly-backup \
-H "Authorization: Bearer ionh_your_api_key"{ "message": "OK" }| Method | Path | Signal |
|---|---|---|
GET or POST | /api/signals/s/:slug | Success |
GET or POST | /api/signals/s/:slug/fail | Failure |
GET or POST | /api/signals/s/:slug/start | Run started |
The slug must match ^[a-z0-9_-]{1,64}$. A POST may carry a JSON payload of up to 10 KB, stored with the signal like a token ping's payload. These optional query parameters add run telemetry:
| Parameter | Meaning |
|---|---|
rid | Run id, up to 64 characters. |
host | Host name, up to 253 characters. |
env | Environment label, up to 32 characters. |
exit_code | Process exit code, 0 to 255. |
duration | Run duration in milliseconds. |
curl -X POST "https://api.ionhour.com/api/signals/s/nightly-backup/fail?exit_code=2&duration=48120" \
-H "Authorization: Bearer ionh_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "error": "disk full" }'signal.ionhour.com serves only token pings, /api/signals/ping/:token and its /fail and /start variants.
Slug pings sent to the signal host fail. Always use api.ionhour.com for slug pings.
Create the job on first ping
Add ?create=1 to create the job when no job in the workspace has that slug yet:
curl -X POST "https://api.ionhour.com/api/signals/s/nightly-backup?create=1" \
-H "Authorization: Bearer ionh_your_api_key"When the job is created, the response is 201:
{ "message": "OK", "created": true }- The new job is named after its slug and placed in a project called Auto-provisioned. Ionhour creates that project if your plan has room for another project; otherwise the job goes into the workspace's oldest project.
- The job starts with a one-hour interval and a five-minute grace period. Adjust its interval and grace period afterwards in the dashboard or with
PATCH /v1/jobs/slug:<slug>. - If the slug already exists,
create=1is ignored and the request is a normal ping that returns200. SDKs can therefore sendcreate=1on every ping. - Without
create=1, an unknown slug returns404 NOT_FOUND.
Auto-creating jobs is controlled by a server-side feature flag. When it is off for your workspace, create=1 on an
unknown slug returns 403 FEATURE_DISABLED. Pings to jobs that already exist are not affected. To control job settings
from the start, create the job with the Integration API and ping it by slug.
Key requirements
Every slug ping changes job state, so the key must be able to write:
- The key must be read/write. A read-only key gets
403 READ_ONLY_KEY. - The key must project to at least the member role.
- If the key is restricted to specific resources, it needs
mcp:signalsto ping. Creating a job withcreate=1also needsmcp:jobs. - Personal keys, service keys, and OAuth app tokens are accepted. An OAuth token needs write access to the same resources.
Errors use the Integration API envelope, { statusCode, error, code, message }:
| HTTP | code | Meaning |
|---|---|---|
| 400 | VALIDATION_FAILED | The slug does not match the allowed pattern. |
| 401 | MISSING_BEARER | No Authorization: Bearer header. |
| 401 | INVALID_KEY | The key is unknown, malformed, or disabled. |
| 401 | KEY_EXPIRED | The key has expired. |
| 401 | SERVICE_KEY_INACTIVE | The service key cannot be used. |
| 403 | READ_ONLY_KEY | The key is read-only. |
| 403 | KEY_SCOPE_DENIED | The key's resource list does not include mcp:signals, or mcp:jobs for a create. |
| 403 | OAUTH_SCOPE_DENIED | The OAuth app was not granted write access to the resource. |
| 403 | KEY_OWNER_NO_ACCESS | The key's owner is no longer an active member of the workspace. |
| 403 | INSUFFICIENT_ROLE | The key projects below the member role. |
| 403 | FEATURE_DISABLED | create=1 was sent, but auto-creating jobs is off for the workspace. |
| 403 | PLAN_LIMIT_EXCEEDED | Creating the job would exceed the plan's job limit. |
| 404 | NOT_FOUND | No job has this slug and create=1 was not sent. |
Slug pings are rate-limited per key and slug, so many jobs behind one shared IP address do not starve each other. A generous per-IP limit applies on top.
Deprecated: key in the URL
The earlier form put the API key in the path:
https://api.ionhour.com/api/signals/p/:pingKey/:slug
https://api.ionhour.com/api/signals/p/:pingKey/:slug/fail
https://api.ionhour.com/api/signals/p/:pingKey/:slug/startThis form is deprecated. URLs end up in shell history, CI logs, and proxy logs, which leaks the key. Every response from these routes carries deprecation headers:
Deprecation: @1791676800
Sunset: Sun, 11 Apr 2027 00:00:00 GMT
Link: <https://docs.ionhour.com/api-reference/signals>; rel="deprecation", <https://docs.ionhour.com/api-reference/signals>; rel="sunset"The route keeps the same behavior and errors as the header form until the sunset date, with one difference: service keys are never accepted in the URL and return 403 KEY_KIND_NOT_ALLOWED.
To migrate, move the key from the path into the Authorization header and change /p/<key>/ to /s/:
# Before (deprecated)
curl "https://api.ionhour.com/api/signals/p/ionh_your_api_key/nightly-backup?create=1"
# After
curl "https://api.ionhour.com/api/signals/s/nightly-backup?create=1" \
-H "Authorization: Bearer ionh_your_api_key"If a key has ever appeared in a URL, rotate it after you migrate.
Checks & Jobs
Manage projects, HTTP checks, and heartbeat jobs as code with the Integration API: create, read, patch, pause, and delete with slugs, idempotency, and errors.
Check Lifecycle
The complete state machine behind check status transitions — inbound and outbound flows, signal types, validation timing, and multi-region consensus rules.