Machine-readable findings
If you already run a dashboard or a monitoring tool, SixPreflight can hand it the result of your last scan as plain JSON — no scraping the page, and no scan triggered just to check in.
Why it never starts a scan itself
The endpoint always returns the last saved check. Nothing about calling it starts a new one.
A monitor that triggered a full scan on every dashboard refresh — one step of which fetches several URLs off your own live site — would be a load generator wearing a monitor's clothes, rather than something you would actually want polling every minute.
The response carries the age of that saved run in seconds, so your dashboard can say "this reading is two weeks old" instead of quietly presenting stale news as current.
Authentication
The same password you sign in with, sent over HTTP Basic auth. Any username works — there is no separate credential to create or to leak.
curl -s -u "sixpreflight:YOUR-PASSWORD" \
"https://your-domain/preflight/?api=findings"This shares the same password hash and the same lockout counter as the sign-in page, so there is nothing extra to configure. No session is opened for this call, so polling it every minute leaves nothing behind on disk. A Basic-auth sign-in can read this one endpoint and nothing else on the tool. If you have set an IP allow-list in the config file, it still applies here too.
The response shape
A version number identifies the shape of the response itself. Fields may be added at any time; a field is never renamed or removed without that number going up — so it is safe to key your integration off the presence of the result list and that version, rather than trying to detect shape changes some other way.
{
"contract": 1,
"tool": { "name": "SixPreflight" },
"generated_at": 1786726822,
"embedded": true,
"page": "/preflight/?page=fix",
"run": {
"id": "20260814-101500-a1b2c3",
"finished_at": 1786438160,
"age_seconds": 288662,
"deep": false,
"score": 34,
"grade": "F"
},
"counts": { "critical": 10, "warning": 11, "ok": 54, "unknown": 8 },
"checks": [
{
"id": "env_app_debug",
"severity": "critical",
"scope": "app",
"title": "Debug mode",
"setting": "APP_DEBUG",
"why": "Debug mode is on, so any error page shows your database password to whoever triggered it.",
"impact": "Serious risk",
"now": "true",
"want": "APP_DEBUG=false",
"fix_target": ".env",
"fix_owner": "operator",
"url": "/preflight/?page=fix"
}
]
}| Field | Meaning |
|---|---|
contract | Integer. The shape of this document. |
tool.name | The product name, for display only — key your integration off contract and the presence of the result list, not this string. |
run | null when no check has ever been run; a note field explains that case. |
run.age_seconds | How old the answer is. Treat anything over a few days as stale. |
Each result's id | A stable key for the check — safe to store and to deduplicate on across runs. |
Each result's severity | critical or warning only. Rows that could not be measured show up in the unknown count, not in this list — a monitor that raised a ticket for every unreadable value on a locked-down host is a monitor people switch off. |
Each result's scope | app — the shop, its settings, its data. stack — the server underneath it. |
Each result's impact | What ignoring it costs, in plain words: Stops orders, Serious risk, Slows the site, Worth fixing, Tidying. |
Each result's fix_target | Where it is put right — a file, or a place named in words. |
Each result's fix_owner | sixpanel when SixPanel manages that file on this server, operator when it is yours to change. Always operator on a standalone install. |
Each result's url | A deep link to the page that explains this specific check. |
Errors come back as an error object with a status code: 401 for a bad or missing password, 403 if Basic auth is used against a different endpoint, or 419 if the session has expired.
Where to go next
- Reading the report — what each severity and impact label means when you are reading it as a person, not as JSON
- Security and privacy — how the password and lockout counter behave