CI API
These endpoints let a CI pipeline that holds only an API key install es-python on its runner and then ask what the test run reported. The CI driver script calls both for you; see Run es-python in CI for complete pipeline examples.
Install on a runner
POST /api/ci/install/ returns a one-time install secret for one of your es-python machines, found by name, and creates the machine the first time. It is a write. Use it once per pipeline run: every call resets the machine for a fresh install, the same way reinstalling does, and keeps its name, configuration, rules and events.
POST /api/ci/install/
Form fields:
| Field | Meaning |
|---|---|
machine_name |
Required. One name per pipeline, such as ci-myrepo. 1 to 128 characters. |
attested |
Required, true. Your confirmation that you are authorized to monitor the computer this installs on, the same box the dashboard asks you to tick. |
distro, arch |
Required the first time, when the machine does not exist yet, such as ubuntu_24_04 and x86_64. Sent for an existing machine, they change its OS. See supported targets. |
python_version |
Optional. Used when the machine is created; the default is the usual default. For an existing machine it must match the machine's version. |
format |
Optional. text answers with key=value lines instead of JSON, for scripts with no JSON parser. |
curl -s https://eyalsec.com/api/ci/install/ \
-H "X-API-Key: $EYALSEC_API_KEY" \
-d machine_name=ci-myrepo -d distro=ubuntu_24_04 -d arch=x86_64 -d attested=true
{ "machine_name": "ci-myrepo", "public_id": "0b9d7e21-4c3a-4f5e-8a61-2d9c0e7f1b53",
"created": false, "os_type": "ubuntu_24_04_x86_64", "python_version": "3.13",
"secret": "<single-use-secret>", "script_path": "https://eyalsec.com/install.sh",
"expires_in": 600, "server_time": "2026-10-07T12:00:00.123456Z",
"events_url": "https://eyalsec.com/scanner/events?machine=ci-myrepo" }
The secret works once and expires after 10 minutes (expires_in is in seconds). Post it to script_path exactly as the install command does. server_time is the server's clock when the secret was made; you can pass it as since to the findings check.
A new machine counts against your machine limit like any other, and at the limit the answer is 409. An existing machine needs no new slot. Other errors: 409 when more than one es-python machine has that name, or when python_version differs from the existing machine's; 422 for a missing field, an unknown target, or a Python version not available for it; 403 when your account does not have es-python.
Check a run's findings
GET /api/ci/findings/ tells you what programs on one machine reported since a moment, and how many of those findings are at or above a severity. It is a read, so a read-only key may call it.
GET /api/ci/findings/
| Parameter | Meaning |
|---|---|
machine |
Required. The machine's name. |
since |
Required. Where the window starts: RFC3339 (2026-10-07T12:00:00Z), Unix seconds, or an HTTP date such as a Date response header. Prefer the server's clock (server_time above, or a Date header) to the runner's. |
min_severity |
critical, high (the default), medium, low or info. |
limit |
How many of the findings at or above min_severity to list, worst first, 0 to 100. The default is 20; the counts always cover all of them. |
format |
text for key=value lines instead of JSON. |
curl -s "https://eyalsec.com/api/ci/findings/?machine=ci-myrepo&since=2026-10-07T12:00:00Z" \
-H "X-API-Key: $EYALSEC_API_KEY"
{ "machine": "ci-myrepo", "since": "2026-10-07T12:00:00Z", "min_severity": "high",
"findings": 2, "new_findings": 1, "total": 5,
"by_severity": { "critical": 1, "high": 1, "medium": 3, "low": 0, "info": 0 },
"truncated": false, "hidden_by": "",
"url": "https://eyalsec.com/scanner/events?machine=ci-myrepo&sev=critical%2Chigh",
"events": [ { "id": 90210, "severity": "critical", "where": "os system",
"count": 3, "last_seen": "2026-10-07T12:03:10Z", "new": true } ] }
| Field | Meaning |
|---|---|
total |
Every event a run on this machine reached inside the window, at any severity. |
findings |
Those at or above min_severity. |
new_findings |
Of findings, the ones no earlier run had reached. |
by_severity |
total, split by severity. |
events |
The findings, worst first: the event id (open it on the dashboard, or with GET /api/event/{id}), its severity, its sink label (where), its all-time occurrence count, when it last happened, and whether it is new. |
url |
The dashboard's events list for this machine at that severity. |
truncated |
true when the run reached more distinct events than one check reads (10,000); the counts are then a lower bound. |
hidden_by |
The name of your Hide rule that hides every es-python event, when one does; nothing can be counted then. |
An event counts when a run on this machine reached it inside the window, even if it was found long before, so a re-run of a failing pipeline fails again until the flow is fixed or you decide to accept it. The check counts exactly what the es-python events list would show you: your event quota, your filters, your global and this machine's Hide rules, and the live bucket all apply, so a finding you hid or that was suppressed never fails a build.
The value that reached the sink is deliberately not in the answer, because CI logs are often public. Open the event on the dashboard to see it.
Errors: 404 when you have no es-python machine with that name, 409 when you have more than one, 400 for a missing or unreadable since or an unknown min_severity, 403 when your account does not have es-python.
The CI driver script
GET /ci.sh serves a small POSIX sh script that calls the two endpoints above: it installs es-python, runs your command under it, waits a few seconds for the events to arrive, then prints a short summary and exits non-zero when anything is at or above your threshold. It needs no key to download and contains no secret; it reads your key from EYALSEC_API_KEY when it runs.
curl -fsSL https://eyalsec.com/ci.sh -o es-python-ci.sh
sh es-python-ci.sh run --machine ci-myrepo --distro ubuntu_24_04 --arch x86_64 -- es-python -m pytest
Run it with sh, not source. Its options and exit codes are on Run es-python in CI.