Reference
HTTP API
Every endpoint Skyline adds to the Horizon API, with parameters and responses.
The Skyline dashboard is a single-page app talking to a JSON API. Every operation it performs is available to you — for a deploy script, an incident runbook, or a bot that drains a queue when a downstream service goes down.
This page documents the endpoints Skyline adds, and the extra parameters it adds to inherited ones.
The upstream Horizon endpoints (/stats, /workload, /masters,
/monitoring, /metrics/*, /batches/*) are unchanged.
Base path and authentication
Endpoints are mounted under the dashboard path, which is horizon.path in your config and defaults to
horizon. So the full base URL is /horizon/api.
There is no separate API authentication. Every endpoint sits behind the same
horizon.middleware (['web'] by default) and the same viewHorizon gate as the
dashboard itself. In practice that means a session cookie: whoever can view the dashboard can call the API, and
nobody else can. Calls that change state are ordinary POST and DELETE requests through the
web middleware group, so they need a CSRF token.
The curl snippets below show the shape of each request. They are not copy-pasteable as-is: without a
session cookie you get a redirect, and without a CSRF token a state-changing call gets a 419.
DELETE /api/queues/… discards every job in a queue. If you expose the dashboard beyond your
operators, review your viewHorizon gate before you rely on the UI's confirmation modals as the
safety net — the API has no modal.
Skyline endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /api/trends | Workload, wait and failure time-series. |
POST | /api/queues/{connection}/{queue}/pause | Pause one queue. |
DELETE | /api/queues/{connection}/{queue}/pause | Resume one queue. |
GET | /api/jobs/queues/{queue} | Jobs waiting inside a queue. |
GET | /api/jobs/delayed | Scheduled and retrying jobs. |
GET | /api/jobs/reserved | Jobs currently executing. |
POST | /api/jobs/perform/{id} | Run a delayed job immediately. |
DELETE | /api/jobs/{id} | Delete a pending or delayed job. |
DELETE | /api/queues/{connection}/{queue} | Empty a queue. |
Job list responses
Every job-listing endpoint returns the same envelope, with the job payload already decoded:
{
"jobs": [ { "id": "8813", "name": "App\\Jobs\\ProcessPodcast", "queue": "default", "payload": { }, "status": "pending" } ],
"total": 412
}
They paginate with a cursor, not a page number. Pass the index after which to read as starting_at; it
defaults to -1, meaning the beginning.
When a listing is scoped with ?queue=, total comes back as null. Counting
the matches for one queue means scanning the whole state, so Skyline skips it and paginates by cursor alone.
Treat null as "unknown", not zero.
GET /api/trends
Returns the time-series behind the dashboard's trend charts — workload, wait and failures, bucketed and retained
according to your trends config. Takes no
parameters.
POST / DELETE /api/queues/{connection}/{queue}/pause
Pause and resume a single queue. Both return 204 No Content on success.
curl -X POST https://example.com/horizon/api/queues/redis/exports/pause
curl -X DELETE https://example.com/horizon/api/queues/redis/exports/pause
Both respond 409 Conflict with {"message": "Queue pausing is not supported in this
environment."} when the Laravel version or the cache store cannot support per-queue pausing. See
Pausing & resuming queues.
GET /api/jobs/queues/{queue}
The jobs waiting inside a single queue, in order.
| Parameter | Default | Meaning |
|---|---|---|
starting_at | -1 | Cursor index to read after. |
name | — | Case-insensitive substring match on the job class name. |
GET /api/jobs/delayed
Delayed jobs: those scheduled for the future and those waiting out a retry backoff.
| Parameter | Default | Meaning |
|---|---|---|
starting_at | -1 | Cursor index to read after. |
name | — | Case-insensitive substring match on the job class name. |
filter | — | scheduled (never attempted) or retry (released after a failure). Any other value is ignored and returns both. |
queue | — | Restrict to one queue. Forces total to null. |
Each job carries an absolute available_at timestamp, so you can show a real next-run time rather than a backoff duration.
GET /api/jobs/reserved
Jobs a worker has reserved and is executing right now — what the dashboard's In Progress tab shows.
| Parameter | Default | Meaning |
|---|---|---|
starting_at | -1 | Cursor index to read after. |
name | — | Case-insensitive substring match on the job class name. |
queue | — | Restrict to one queue. Forces total to null. |
POST /api/jobs/perform/{id}
Runs a delayed or retrying job immediately instead of waiting for its available-at time. This is the Perform Now button. The request returns as soon as the trigger is queued; the job itself runs on a worker.
curl -X POST https://example.com/horizon/api/jobs/perform/8813
The job id is not validated at the HTTP layer — an unknown id is accepted here and resolved when the trigger runs.
DELETE /api/jobs/{id}
Removes a single job from its queue. Returns 204 No Content on success.
| Status | When |
|---|---|
204 | The job was removed and any ShouldBeUnique lock released. |
404 | No such job. |
422 | The job is not pending or delayed — a reserved, completed or failed job cannot be deleted. |
422 | The job's queue connection is unknown, or is not a Redis connection. |
409 | A worker reserved the job between the lookup and the delete, so it is no longer removable. |
DELETE /api/queues/{connection}/{queue}
Empties a queue: discards every pending and delayed job in it. Returns 200 with the number removed.
curl -X DELETE https://example.com/horizon/api/queues/redis/exports
{"deleted": 1284}
Responds 422 when the connection is unknown, or when its driver does not support clearing. The unique
lock of every removed job is released — see Unique job
locks.
Extended Horizon endpoints
These endpoints exist in Horizon; Skyline adds parameters to them. All accept starting_at as upstream does.
| Endpoint | Added parameters |
|---|---|
GET /api/jobs/pending | name, queue |
GET /api/jobs/completed | name, queue |
GET /api/jobs/silenced | name |
GET /api/jobs/failed | name |
On /api/jobs/failed, the upstream tag parameter still works and takes precedence over
name; the two are mutually exclusive.
From PHP
If you are scripting against Skyline in-process rather than over HTTP, the same data is on the job repository, which you can resolve from the container:
use Laravel\Horizon\Contracts\JobRepository;
$jobs = app(JobRepository::class);
$jobs->getReserved(); // executing now
$jobs->getDelayed(null, null, 'retry'); // backing off after a failure
$jobs->getPendingForQueue('exports'); // waiting in one queue
$jobs->countPending('ProcessPodcast'); // by class-name substring
And to prepend a raw payload onto a queue, bypassing the job class entirely:
Queue::connection('redis')->pushRawOnFront($payload, 'media');
GET /api/jobs/reserved, the filter parameter on /api/jobs/delayed, and the
queue parameter on the job listings arrived with the job-state tabs. If your installed Skyline
version predates them, the reserved endpoint 404s and the unknown parameters are ignored. Upgrade with
composer update boring-o11y/laravel-skyline.