Skip to main content

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

MethodPathAdminPurpose
GET/api/noAPI alive check
GET/api/core/statenoCore run state + recorder migration status
GET/api/streamyesServer-sent event stream of the event bus
GET/api/confignoCurrent core configuration
GET/api/statesnoAll entity states (permission-filtered)
GET/api/states/{entity_id}noSingle entity state
POST/api/states/{entity_id}yesSet/create a state
DELETE/api/states/{entity_id}yesRemove an entity's state
GET/api/eventsnoEvent types with listener counts
POST/api/events/{event_type}yesFire an event on the bus
GET/api/servicesnoRegistered services with descriptions
POST/api/services/{domain}/{service}noCall a service
GET/api/componentsnoLoaded components
POST/api/templateyesRender a template
GET/api/error_logyesRetrieve the error log
GET/api/history/period[/{timestamp}]noState history over a period
POST/api/webhook/{webhook_id}no authInbound 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 value
  • attributes (optional) — attribute object
  • force_update (optional, default false)

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 window
  • minimal_response — return minimal state objects for intermediate points
  • no_attributes — skip attributes
  • significant_changes_only=0 — include minor changes
  • skip_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.