31 API and Configuration
Everything the Polaris web UI does goes through the HTTP API described here, so anything you can click is also scriptable: a cron job can start a sequence, a home-automation system can watch the guider, an observatory dashboard can subscribe to the status stream. This chapter maps the integration surface: the REST endpoints under /api, the three WebSocket streams, and the configuration system (appsettings.json and environment variables).
This is a reference chapter, and deliberately not an exhaustive one. The endpoint catalog in docs/api-reference.md in the repository is the living, complete reference and is updated with the code; the Endpoints/ folder in src/NINA.Polaris is the ground truth (a few newer endpoint groups, such as the ONNX save and editor upload routes, appear there before the catalog catches up). Here the endpoints are grouped by area with representative examples so you can see the shape of the API and know where to look.
31.1 Ports and transports
The server configures its listeners explicitly (in Program.cs) rather than through the generic ASP.NET Core URL settings:
| Listener | Port | Bind | Purpose |
|---|---|---|---|
| HTTPS | 5000 | all interfaces | The main entry point. Self-signed certificate generated on first run. |
| HTTP | 5080 | loopback only | Plaintext service port for the relay tunnel and curl-from-the-host scripts. |
Port 5000 carrying HTTPS (not plain HTTP) is deliberate: browsers only unlock WebGPU and multi-threaded WebAssembly on secure origins, so the URL a user naturally types must be the secure one. When the HTTP-to-HTTPS redirect is enabled (the default), the HTTP listener is also exposed on the LAN, but it only ever answers with a redirect to the HTTPS endpoint; no content is served over plaintext. Older documentation that mentions plain HTTP on 5000, port 5001, or ASPNETCORE_URLS as the way to change the listen address predates this arrangement; the Server:* keys below are what the code reads.
| Key | Default | Meaning |
|---|---|---|
Server:Https:Enabled |
true |
Serve HTTPS |
Server:Https:Port |
5000 |
HTTPS port |
Server:Http:Enabled |
true |
Serve HTTP |
Server:Http:Port |
5080 |
HTTP port |
Server:Http:Bind |
loopback |
any exposes plaintext HTTP to the LAN |
Server:Http:RedirectToHttps |
true |
Answer LAN HTTP with a redirect to HTTPS |
All REST endpoints live under /api/* and speak JSON. Long-running operations return 202 with a { jobId }; the caller either polls the matching .../status endpoint or watches the job’s sub-object on /ws/status. Errors that map naturally to HTTP arrive as 400, 404, or 409 with a message; unexpected failures arrive as 500 with { error: "..." }.
31.2 REST endpoints by area
Equipment and rigs
Device discovery, connection, and the multi-rig profile model (Chapter 5).
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/equipment/devices |
List all discovered INDI devices |
| POST | /api/equipment/connect |
Connect all selected devices |
| GET | /api/equipment/status |
Aggregated status of every selected device |
| GET | /api/equipment/rigs |
All rigs plus the active rig id |
| POST | /api/equipment/rigs |
Create an empty rig { name } |
| PUT | /api/equipment/rigs/{id} |
Update a rig (device selections, optics, PHD2 endpoint) |
| POST | /api/equipment/rigs/{id}/activate |
Switch to this rig |
A companion group under /api/indi/properties exposes the raw INDI property browser: the full device-to-property tree, property set operations, cache refresh, the driver’s save/load/default configuration actions, and the operator’s per-property notes.
Devices
Each device kind has its own group with the same select, connect, act, and status shape. Representative examples:
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/camera/capture |
Capture { exposure, gain, binning, filter } |
| POST | /api/camera/cooler |
Set cooler { enabled, targetTemperature } |
| POST | /api/telescope/slew |
Slew to { ra, dec } |
| POST | /api/telescope/move/{direction} |
Manual move (north, south, east, west, stop) |
| POST | /api/telescope/abort |
Emergency stop |
| POST | /api/focuser/move/absolute |
Move to { position } |
| POST | /api/filterwheel/position/{slot} |
Move to a filter slot |
Alpaca devices get a parallel group (/api/alpaca/*) covering UDP discovery on port 32227, direct device listing, camera and telescope probes, and connect and disconnect; a small /api/stellarium/* group pulls the selected object and view direction from a running Stellarium’s Remote Control plugin.
Imaging and the image stream
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/image/latest/preview |
Latest image as JPEG |
| GET | /api/image/latest/stats?withStars |
Dimensions plus mean, median, min, max, standard deviation, MAD, and optional star statistics |
| GET | /api/image/latest/histogram?bins=256 |
Pixel-value histogram |
| GET | /api/image/latest/stars?maxStars&sigma |
Detected stars with position, HFR, flux, and peak |
| GET | /api/image/stream/clients |
Per-client WebSocket diagnostics (mode, latency, streaks) |
| POST | /api/image/stream/adaptive |
Toggle adaptive bandwidth { enabled } |
Guiding
The largest group, /api/guider/*, mirrors the depth of the PHD2 integration (Chapter 12): connection and state (/connect, /status, /steps?limit=N), guiding actions (/guide, /dither, /stop, /loop, /pause, /resume), PHD2 profile management and per-rig profile sync (/profiles, /profile/sync), exposure and declination-mode control, PHD2 process lifecycle (/process/launch, /process/shutdown, /install-info), the smart calibration job (/calibrate/smart, returning a jobId), guiding algorithm presets and live parameters (/algo-presets, /algo-params), and the embedded PHD2 GUI session (/gui-session/*, Linux only, with /phd2-gui/{**} reverse-proxied to the xpra HTML5 client).
Capture automation
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/autofocus/start |
Start a V-curve run { steps, stepSize, exposureSeconds, minStars, backlashSteps } |
| GET | /api/autofocus/result |
Last completed run with fitted curve coefficients |
| GET | /api/meridianflip/status |
State, sidereal time, hour angle, minutes to meridian |
| POST | /api/meridianflip/trigger |
Manual flip { ra, dec } |
| POST | /api/flatwizard/start |
Automated flats { filters, framesPerFilter, targetAdu, tolerance, ... } |
| GET | /api/flatwizard/trained |
Persisted filter-and-binning to exposure dictionary |
Sequencing
Three engines, three groups (Chapter 14, Chapter 15, Chapter 16):
/api/sequence/*: the simple flat list. Load a JSON array of items, thenstart,pause,resume,stop, and readstatus. A/api/sequence/ditherpair reads and updates the dither settings./api/sequencer/*: the advanced tree-based sequencer. Get and set theSequenceDocument(as an object or as raw JSON for save-to-file and load-from-file),start,stop,validate, list the palette of available instruction types, and manage named templates./api/mosaic/*: the mosaic planner; compute panels and time estimates from aMosaicRequest, or lower the plan directly into aSequenceDocument, optionally loading it into the engine.
Live stacking and planetary
/api/livestack/* offers start, stop, reset, and status (Chapter 17). Planetary video recording and stacking are driven through their own endpoints and report through the videoRecording and videoStack sub-objects of the status stream (Chapter 18).
Sky, planning, and plate solving
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/sky/catalog/search?query=M31 |
Search the embedded deep-sky catalog |
| GET | /api/sky/altitude?ra&dec&stepMinutes |
Tonight’s altitude track with twilight transitions |
| GET | /api/sky/tonights-best?lat=&lon=&limit= |
Ranked observable targets for tonight |
| GET | /api/weather/forecast?lat=&lon= |
3-day astronomy forecast in 3-hour slots, server-cached 15 minutes |
| GET | /api/sky/solver/list |
The plate-solver backends with availability flags |
| POST | /api/sky/slew-and-center |
Start a slew-and-center job { ra, dec, toleranceArcsec } |
STUDIO
/api/studio/* powers the post-processing library and its batch jobs (Chapter 20): rescan walks the image output directory and upserts the SQLite index; frames queries the paginated library with filter, target, and date criteria; per-frame routes serve thumbnails, stretched previews, statistics, and exports; and the job routes (masters, calibrate, integrate) start master-frame integration, light calibration, and batch stacking, each returning a jobId with a matching status route. Single-frame operations (debayer, bgextract, nr, sharpen) run synchronously.
System
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/system/status |
CPU, RAM, uptime (also the Docker health check) |
| GET | /api/system/relay |
Relay tunnel status (state, hostname, lastError) |
| GET | /api/system/profiles |
List profiles |
| PUT | /api/system/profile |
Update settings |
| POST | /api/system/factory-reset |
Wipe profiles, rigs, auth, and settings (keeps captured images) |
| GET | /api/plugins |
Loaded plugins with name, version, and contributed types |
31.3 WebSocket streams
| Endpoint | Type | Description |
|---|---|---|
/ws/status |
JSON | Full application state at 1 Hz |
/ws/image-stream |
Binary | Live frames, JPEG or raw with LZ4 compression |
/ws/terminal |
Mixed | SSH bridge for the in-browser terminal |
The status stream
Every connected client receives the same once-per-second payload; each service contributes its own sub-object, and new services add new keys. A trimmed example:
{
"type": "status",
"equipment": {
"indi": { "connected": true },
"camera": { "name": "ZWO ASI2600MC", "temperature": -10.0 },
"telescope": { "ra": 0.713, "dec": 41.27, "tracking": true,
"slewing": false },
"focuser": { "position": 12500, "temperature": 15.2 },
"filterWheel": { "position": 3, "currentFilter": "Ha",
"filters": ["L","R","G","B","Ha","OIII","SII"] }
},
"liveStack": { "isRunning": true, "frameCount": 42 },
"sequence": { "state": "running", "currentItemIndex": 1,
"totalFrames": 100, "totalFramesCompleted": 37 }
}Further sub-objects cover the guider (with its profile-sync, calibration-job, and GUI-session children), autofocus, meridian flip, camera stream, video recording and stacking, slew preview, host metrics, and external-tool jobs. The full composition is described in Chapter 30.
The image stream
After connecting, the client selects a format by sending {"mode":"jpeg"} or {"mode":"raw"}. JPEG mode delivers ready-to-display frames; raw mode delivers LZ4-compressed 16-bit sensor data, bayer pattern intact, for the browser’s own WebGL2 debayer and stretch. If a client cannot keep up, frames are dropped rather than queued, and the adaptive bandwidth service can fall a slow client back from raw to JPEG automatically (/api/image/stream/clients shows the per-client state).
The terminal stream
One WebSocket is one SSH session. The first frame must be {"type":"auth", "host", "port", "user", "password", "cols", "rows"}; credentials are held only in memory for the socket’s lifetime. Subsequent text frames carry keystrokes in and terminal output back, and {"type":"resize", "cols", "rows"} resizes the remote pseudo-terminal. The endpoint returns 403 unless enabled via the Terminal:Enabled key or the Settings toggle, and idle sessions are closed after ten minutes (Chapter 28).
31.4 Configuration
appsettings.json
The file ships nearly empty; every section below has code-side defaults and only needs to appear when you override something. A representative override:
{
"Indi": {
"Host": "localhost",
"Port": 7624
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}The recognized sections are Server (listeners, above), Indi, PHD2, PlateSolve, Sequencer, Plugins, Mdns, Relay, Terminal, Kestrel, and Logging. appsettings.Development.json overlays the base file during development.
Environment variables
Every configuration key can also be set as an environment variable, using __ (double underscore) in place of :; this is the natural mechanism for Docker and systemd deployments (Chapter 2). The commonly used ones:
| Variable | Default | Description |
|---|---|---|
DOTNET_gcServer |
0 |
Workstation garbage collector; saves RAM on a Raspberry Pi |
Indi__Host |
localhost |
INDI server hostname |
Indi__Port |
7624 |
INDI server port |
PHD2__Host / PHD2__Port |
localhost / 4400 |
PHD2 event server endpoint |
PHD2__ExecutablePath |
auto-detected | Override the PHD2 binary path; by default the standard install paths per OS are searched |
PHD2__InstanceNumber |
1 |
PHD2 -i N instance number |
PHD2__AutoStart |
false |
Fallback for the per-profile auto-start flag; the GUIDE tab checkbox is the normal way |
Sequencer__TemplateDir |
sequencer-templates |
Folder holding advanced-sequencer templates, one JSON file each |
Plugins__Enabled |
true |
Set false to skip the plugin scan |
Plugins__Directory |
plugins |
Folder scanned at startup for plugin .dll files |
PlateSolve__PrimarySolver |
astap |
One of astap, platesolve3, astrometry-net-online, astrometry-net-local |
PlateSolve__BlindSolver |
astrometry-net-online |
Fallback when the primary fails |
PlateSolve__UseBlindFallback |
true |
Set false to lock to the primary only |
PlateSolve__AstapPath |
auto | ASTAP executable path |
PlateSolve__PlateSolve3Path |
none | PlateSolve3 executable path |
PlateSolve__SolveFieldPath |
/usr/bin/solve-field |
Local Astrometry.net binary |
PlateSolve__AstrometryApiKey |
none | nova.astrometry.net API key |
Mdns__Enabled |
true |
mDNS announcer |
Mdns__InstanceName |
polaris-app-XXXX |
Advertised name; auto-generated per device from the hardware id unless set |
Terminal__Enabled |
false |
Enable the /ws/terminal SSH bridge |
Relay__Enabled |
false |
Enable the reverse-tunnel client |
Relay__ServerUrl |
none | For example wss://relay.example.com/_tunnel |
Relay__Token |
none | Bearer token matching a tenant on the relay server |
Relay__ClientCertPath |
none | .pfx client certificate for mutual TLS on the tunnel |
Relay__ClientCertPassword |
none | Password for the .pfx, if any |
Older references to ASPNETCORE_URLS (default http://0.0.0.0:5000) describe the pre-HTTPS arrangement; current builds configure Kestrel from the Server:* keys and serve HTTPS on port 5000 regardless, so prefer those keys.
Relay server configuration
The relay server is a separate process with its own appsettings.json (Chapter 27 covers deployment). Its keys, using the same __ convention:
| Key | Default | Purpose |
|---|---|---|
Relay__TenantsFile |
tenants.json |
JSON tenant store, hot-reloaded on change |
Relay__UsageStateFile |
tenant-state.json |
Persistent monthly byte counters |
Proxy__TimeoutSeconds |
60 |
Per-request timeout (long enough for plate-solve uploads) |
Proxy__HostnameSuffix |
none | For example .relay.example.com to enable subdomain routing |
Admin__Password |
empty | Password for /_admin/* and the admin UI; empty disables the admin API (503) |
Audit__Enabled |
true |
Per-request audit log |
Audit__Path |
audit.log |
JSON-lines audit file |
Audit__MaxFileBytes |
52428800 |
Rotate at this size (50 MB) |
Audit__RingBufferSize |
5000 |
In-memory ring for the admin audit view |
Tls__Mode |
off |
off, pfx, or letsencrypt |
Tls__ClientCertificateMode |
request |
none, request, or require (mutual TLS) |
Tls__HttpsPort |
443 |
HTTPS bind port when TLS is enabled |
Tls__RedirectHttpToHttps |
false |
Redirect plain HTTP to HTTPS |
Tls__PfxPath / Tls__PfxPassword |
none | Static certificate when Tls:Mode=pfx |
Tls__LetsEncrypt__Domains |
none | Domains for automatic certificate issuance |
Tls__LetsEncrypt__EmailAddress |
none | Contact address for Let’s Encrypt |
Tls__LetsEncrypt__UseStaging |
false |
Use the Let’s Encrypt staging API while testing |