REST API
ApexOS exposes a JSON REST API on the same HTTP server that serves the frontend. The default port is 1702, so a typical base URL on a local network is:
http://apexos.local:1702
All examples below assume that base URL.
Authentication
Every request must carry a bearer token in the Authorization header. For scripts and third-party integrations, use a long-lived access token (created from a user's profile page, or via the WebSocket command auth/long_lived_access_token — see Authentication):
curl \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
http://apexos.local:1702/api/
Requests without a valid token receive 401 Unauthorized. Some routes additionally require the token's user to be an administrator; these are marked below. Read access to states is filtered by the user's entity permission policy — non-admin users only see entities they are allowed to read.
Route overview
| Method | Path | Admin | Purpose |
|---|---|---|---|
| GET | /api/ | no | API alive check |
| GET | /api/core/state | no | Core run state + recorder migration status |
| GET | /api/stream | yes | Server-sent event stream of the event bus |
| GET | /api/config | no | Current core configuration |
| GET | /api/states | no | All entity states (permission-filtered) |
| GET | /api/states/{entity_id} | no | Single entity state |
| POST | /api/states/{entity_id} | yes | Set/create a state |
| DELETE | /api/states/{entity_id} | yes | Remove an entity's state |
| GET | /api/events | no | Event types with listener counts |
| POST | /api/events/{event_type} | yes | Fire an event on the bus |
| GET | /api/services | no | Registered services with descriptions |
| POST | /api/services/{domain}/{service} | no | Call a service |
| GET | /api/components | no | Loaded components |
| POST | /api/template | yes | Render a template |
| GET | /api/error_log | yes | Retrieve the error log |
| GET | /api/history/period[/{timestamp}] | no | State history over a period |
| POST | /api/webhook/{webhook_id} | no auth | Inbound webhooks (see Native apps) |
Routes
GET /api/
Returns a simple message if the API is up.
{"message": "API running."}
GET /api/core/state
Fast, lightweight check of the core's run state, intended for supervising processes. Returns the core state plus recorder migration flags:
{
"state": "RUNNING",
"recorder_state": {
"migration_in_progress": false,
"migration_is_live": false
}
}
GET /api/stream (admin)
A text/event-stream (server-sent events) feed of the event bus. Each event is delivered as a data: line containing the JSON-serialized event. The server writes a ping payload immediately on connect and then every 50 seconds of inactivity as a keep-alive.
Use the restrict query parameter to limit the stream to a comma-separated list of event types:
curl -N \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
"http://apexos.local:1702/api/stream?restrict=state_changed,service_registered"
GET /api/config
Returns the current core configuration dictionary (location, unit system, version, loaded components, URLs, etc.).
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
http://apexos.local:1702/api/config
GET /api/states
Returns a JSON array of all entity state objects. Admin users get every entity; other users get only entities their permission policy allows them to read. The response is compressed when the client supports it.
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
http://apexos.local:1702/api/states
GET /api/states/{entity_id}
Returns the state object for one entity, or 404 with {"message": "Entity not found."}. Requesting an entity the user may not read raises 401.
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
http://apexos.local:1702/api/states/sensor.kitchen_temperature
POST /api/states/{entity_id} (admin)
Sets a state directly in the state machine (this does not communicate with a device — it only writes the state representation). Body fields:
state(required) — the new state valueattributes(optional) — attribute objectforce_update(optional, defaultfalse)
Returns the stored state: 201 Created if the entity did not exist before, 200 OK otherwise, with a Location: /api/states/{entity_id} header. Invalid JSON, a missing state, a malformed entity ID, or an invalid state value return 400.
curl -X POST \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"state": "25", "attributes": {"unit_of_measurement": "°C"}}' \
http://apexos.local:1702/api/states/sensor.kitchen_temperature
DELETE /api/states/{entity_id} (admin)
Removes the entity from the state machine. Returns {"message": "Entity removed."} or 404 if unknown.
GET /api/events
Returns an array of event types currently having listeners, with counts:
[{"event": "state_changed", "listener_count": 5}]
POST /api/events/{event_type} (admin)
Fires an event on the bus. The body, if present, must be a JSON object and becomes the event data. For state_changed events, old_state/new_state dictionaries are converted back into state objects. Returns {"message": "Event <event_type> fired."}.
curl -X POST \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"next_rising": "2026-07-21T05:55:24+00:00"}' \
http://apexos.local:1702/api/events/event_lights_updated
GET /api/services
Returns all registered services grouped by domain, including their field descriptions:
[{"domain": "light", "services": {"turn_on": {"...": "..."}}}]
POST /api/services/{domain}/{service}
Calls a service and blocks until it completes. The JSON body is passed as service data. The response is the list of states that changed while the service was executing (matched by request context).
curl -X POST \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"entity_id": "light.study"}' \
http://apexos.local:1702/api/services/light/turn_on
Services that return data require the return_response query parameter; the result then has the shape {"changed_states": [...], "service_response": {...}}:
curl -X POST \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"entity_id": "weather.home", "type": "daily"}' \
"http://apexos.local:1702/api/services/weather/get_forecasts?return_response"
Calling a service that only supports responses without ?return_response — or requesting a response from a service that cannot return one — yields 400. An unknown service also yields 400.
GET /api/components
Returns the list of currently loaded component names.
POST /api/template (admin)
Renders a template server-side and returns the result as plain text. Body fields: template (required) and variables (optional object). Template errors return 400.
curl -X POST \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"template": "The kitchen is {{ states(\"sensor.kitchen_temperature\") }} degrees."}' \
http://apexos.local:1702/api/template
GET /api/error_log (admin)
Returns the captured error log file. Only registered when log capture is active.
GET /api/history/period[/{timestamp}] (history component)
Available when the history component is loaded. Returns significant state changes for one or more entities over a window that starts at the URL timestamp (ISO 8601; defaults to one day ago) and ends at end_time (defaults to start + 1 day).
Query parameters:
filter_entity_id(required) — comma-separated entity IDs (permission-filtered per user)end_time— ISO 8601 end of the windowminimal_response— return minimal state objects for intermediate pointsno_attributes— skip attributessignificant_changes_only=0— include minor changesskip_initial_state— omit the state at the start of the window
curl -H "Authorization: Bearer <ACCESS_TOKEN>" \
"http://apexos.local:1702/api/history/period/2026-07-19T00:00:00+00:00?filter_entity_id=sensor.kitchen_temperature&end_time=2026-07-20T00:00:00%2B00:00"
Errors
Errors are returned as JSON messages with an appropriate HTTP status: 400 for malformed bodies or invalid service calls, 401 for missing/invalid credentials or insufficient entity permission, 404 for unknown entities, and 405 for unsupported methods.