Headless mode (--no-gui)
Skip the desktop window entirely and run Seraph as a pure HTTP API host. Every backend service stays alive — license heartbeat, bot manager, slot sweeper, REST + SSE bridge — but no Tauri webview opens.
seraph.exe --no-gui
# or, equivalent
seraph.exe --headless
The startup banner switches:
[seraph] api on http://0.0.0.0:8090 (PIN=798465 at C:\Users\<you>\AppData\Roaming\Seraph\pin.txt)
[seraph] headless mode (--no-gui) — UI window suppressed; HTTP API on http://0.0.0.0:8090
Logging into your Seraph account
Every bot-control command runs through require_active_license,
which means the operator has to be logged in to a Seraph account
with an active license before any bot will spawn or warp.
Three ways to handle the login:
1. Pass credentials at launch (preferred)
seraph.exe --no-gui \
--seraph-user yourname \
--seraph-pass yourpassword
The bin auto-logs in during startup, fetches a signed lease, and prints:
[seraph] headless auto-login as yourname…
[seraph] license active: plan=premium max_slots=100
After that, every /api/bots/... / /invoke call passes the
license gate.
2. Use environment variables
Same result, with the credentials kept out of the process arg list:
SERAPH_USER=yourname SERAPH_PASS=yourpassword seraph.exe --no-gui
Useful for systemd units, Set-Item Env:SERAPH_USER in PowerShell,
or .env files loaded by your launcher.
3. Login via the HTTP API after launch
For interactive operators or one-off scripts:
# 1. Boot headless.
seraph.exe --no-gui
# 2. Exchange the PIN (printed at startup) for a bearer token.
PIN=$(grep -oE '[0-9]{6}' "$APPDATA/Seraph/pin.txt")
TOKEN=$(curl -s -X POST -H 'content-type: application/json' \
-d "{\"pin\":\"$PIN\"}" http://localhost:8090/auth/pin | jq -r .token)
# 3. Login to Seraph (talks to licensing server, drops a session
# token at %LOCALAPPDATA%\seraph\auth\session_token).
curl -s -X POST http://localhost:8090/invoke \
-H "authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"cmd":"seraph_login","args":{"username":"yourname","password":"yourpassword"}}'
# 4. Now spawn / warp / disconnect bots — every command passes the
# license gate while this session stays valid.
curl -s -X POST http://localhost:8090/invoke \
-H "authorization: Bearer $TOKEN" \
-H "content-type: application/json" \
-d '{"cmd":"spawn_bot_email","args":{
"email":"[email protected]",
"password":"botpass",
"aid":null
}}'
The next launch reuses the saved session token (no re-login needed)
until it expires or you seraph_logout.
When to use each path
| Path | Best for |
|---|---|
--seraph-user | unattended servers / docker / systemd / cron — fire-and-forget |
SERAPH_USER | same as above, but credentials don't show in ps/Task Manager |
/invoke seraph_login | interactive ops, scripted login from existing tooling |
Auth still applies
Every endpoint behind the bearer token still gates the same way as
the GUI build — pull the PIN from pin.txt after launch, exchange
it for a token via POST /auth/pin, then use that bearer for
everything else. See Authentication.
Discovery
Hit GET /api/commands for the full list of
dispatch commands the running binary exposes — it's the canonical
way to learn what's reachable in headless mode without a code-walk.
Limits
- No login UI, no PIN entry dialog. The PIN file path is printed at startup; copy it from there.
- The licensing dashboard tab (subscription, redeem code) doesn't
exist in this mode. Use
POST /invokewithcmd: "seraph_login"/seraph_redeem_keyetc. directly, or pass--seraph-user/SERAPH_USERat launch. - The slot sweeper still runs — bots over the plan cap get reaped.
- Without a successful Seraph login, every bot command returns
{"error":"not_logged_in: sign in to your Seraph account first"}. Login first, then drive bots.