API Reference
EmberCORE exposes a REST API under /api. EmberCORE's own web interface is
built on it, and the same routes are available to integrations. This page lists
the routes a customer integration can use, with the access each one needs.
Base URL
The API is served by EmberCORE itself. There is no separate API host.
https://dashboard.embernet.ai/api
Paths are versionless (/api/nodes, /api/alerts). There is no /v1 prefix.
Authentication
There are no API keys. Every request is made as a person, and is allowed or refused by that person's role in the tenant it touches.
- From a browser, requests carry your EmberCORE sign-in session. See SSO Integration. Calls made by the EmberCORE interface inherit it automatically.
- From the EmberNet Endpoint client, the
client sends a Microsoft bearer token (
Authorization: Bearer <token>) on the routes that accept one:/api/tenants/me,/api/endpoints, and/api/endpoints/cards.
Rate limiting
There is no general rate limit on the API. The one exception is the public
access request form, POST /api/access-request, which accepts three requests
per ten minutes from one address and answers 429 Too Many Requests after that.
Tenant scope
Routes that return per-tenant data take an optional ?tenant=<id>:
- Below Global Command, leaving it out selects your primary tenant, and
asking for a tenant you do not belong to returns
403 Forbidden. - Global Command may pass any tenant, or leave it out for a platform-wide view.
A few routes differ:
/api/nodesand/api/metricsalways answer for your primary tenant below Global Command, whatever?tenant=says.- The App Store routes take
?tenant_id=instead. - Routes that act on one device or alert check who owns it: an unknown ID
returns
404, and another tenant's returns403. - The Flux routes return an empty result, not
403, for a tenant you cannot see.
Responses
Successful responses are plain JSON: an array for lists and an object for a
single resource, with no {status, data} envelope. Errors come back with the
matching HTTP status and a short message, usually as plain text and on some
routes as {"error": "..."}.
Access levels
The Access column below uses these terms:
| Term | Who |
|---|---|
| Any role | Every tenant role, Viewer included |
| Engineer | Engineers and Admins |
| Admin | Tenant Admins |
| Global Command | Fireball platform staff only |
Session and tenants
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/ui/session | Any role | The view model the console renders from: who you are, your role, your tenant, and what your view shows. 403 if you have no tenant |
GET | /api/tenants | Any role | The tenants you belong to |
GET | /api/tenants/me | Any role (bearer accepted) | Your identity's tenant context |
GET | /api/tenants/{id} | Members of that tenant | One tenant |
GET | /api/tenants/{id}/members | Admin | The tenant's members |
GET | /healthz | Public | Health check, {"status":"ok"} |
Nodes and metrics
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/nodes | Any role | Edge nodes and the apps running on each |
GET | /api/metrics | Any role | Pod and ingress counts for your tenant |
GET | /api/ws | Any role | WebSocket of live node and pod updates |
GET | /api/cluster/metrics | Engineer | Cluster CPU, memory, and disk |
GET | /api/k8s/nodes | Engineer | Node names, roles, and readiness |
GET | /api/events | Engineer | Cluster events for your tenant; /api/events/filtered filters them |
Sites and devices
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/sites | Any role | Sites; /api/sites/{id}, /api/sites/{id}/buildings/{bid}, and /api/sites/health drill in |
GET POST | /api/admin/sites | Admin | List and create sites; /api/admin/sites/{id} edits and removes them |
GET | /api/devices | Any role | Registered devices |
POST PUT DELETE | /api/devices | Admin | Register, update (?id=), and remove (?id=) a device |
POST | /api/devices/health/check?id= | Any role | Run a health check on a device now |
GET PUT | /api/devices/credentials?id= | Admin | A device's stored credentials |
ANY | /api/device/proxy?id=&path= | Any role | Proxy to a device's own web interface |
GET | /api/admin/devices/import/template | Admin | The bulk import template; detect, validate, and execute under the same path run an import |
/api/facilities and /api/admin/facilities are aliases of the site routes.
SNMP
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/devices/snmp/config?id= | Any role | A device's SNMP settings, with secrets masked |
PUT | /api/devices/snmp/config?id= | Admin | Save SNMP settings. An empty secret keeps the stored one |
GET | /api/devices/snmp/metrics?id= | Any role | The latest SNMP metrics |
GET | /api/devices/snmp/interfaces?id= | Any role | Interface status and traffic |
See SNMP Configuration.
Industrial collectors
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/industrial/protocols | Any role | Every supported protocol, and what the gateway protocols ride |
GET | /api/industrial/config?id=[&protocol=] | Any role | A device's collector configuration |
PUT | /api/industrial/config | Admin | Save a collector. Refused with the reason if the configuration cannot work |
GET | /api/industrial/summary?id= | Any role | Each protocol on the device, with connected, lastPoll, lastError, and tagCount |
GET | /api/industrial/metrics?id=&protocol= | Any role | Recent tag values, with quality and sparkline history |
GET | /api/industrial/tsdb/status | Any role | Whether the time-series store is enabled |
GET | /api/industrial/tsdb/query?id=&protocol=&tag=&range= | Any role | A tag's history (default range one hour) |
GET | /api/industrial/opcua/client-cert?tenant= | Admin | Your tenant's OPC UA client certificate: {tenant, applicationUri, thumbprint, certificatePem}. Created on first call |
A collector body is
{deviceId, protocol, enabled, address, interval, protocolConfig}, where
interval is in seconds and protocolConfig is the protocol's settings as a
JSON object serialized into a string. Save "enabled": false to stop a
collector. Every protocol's settings are described in
Device Connectivity.
Alerts
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/alerts | Any role | Alerts for your tenant. Filters: tenant, status, facility, severity, limit |
GET | /api/alerts/summary | Any role | Alert counts by severity |
POST | /api/alerts/acknowledge?id= | Any role | Acknowledge an alert. {"acknowledged":true} |
POST | /api/alerts/resolve?id= | Admin | Resolve an alert. {"resolved":true} |
An alert has an id, severity (critical, warning, or info), alertType,
title, message, status (active, acknowledged, or resolved), its
tenant, the device, site, and building it concerns where there is one, and
timestamps. Alert types include device_offline, device_online, degraded,
and site_down from rules, cinder_volume_degraded, cinder_volume_faulted,
and cinder_capacity_guardrail from storage, and cluster_unreachable and
cluster_degraded from cluster health.
Alert rules and webhooks
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/alerts/rules | Global Command | List rules |
POST | /api/alerts/rules | Global Command | Create a rule. 201 with {"id"} |
PUT | /api/alerts/rules | Global Command | Update a rule; the id goes in the body |
DELETE | /api/alerts/rules?id= | Global Command | Delete a rule |
Rules are managed by Fireball, so ask us to add or change one. A rule has a
name, enabled, a scope (device, building, facility, or global) and
scopeId, an alertType, a severity, cooldownMinutes, the thresholds
degradedThreshMin and siteDownThreshold, and its channels: notifyEmail,
notifyDashboard (the bell), notifyWebhook, and webhookUrl.
When a rule with a webhook fires, EmberCORE sends a JSON POST to the
webhookUrl:
{
"type": "alert",
"alert": { "id": "...", "severity": "critical", "alertType": "device_offline", "title": "...", "message": "..." },
"sentAt": "2026-10-05T02:14:07Z"
}
The request carries User-Agent: Embernet-Dashboard/alerts and times out after
ten seconds. A network failure, a 429, or a 5xx is retried up to three
attempts in all, two and then four seconds apart. Any other 4xx is not
retried. A rule with the webhook ticked must have a full http or https URL,
or it is refused when saved.
Phone alerts
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/push/key | Any signed-in user | The public key a browser subscribes with, {"publicKey"} |
POST | /api/push/subscribe | Any signed-in user | Register a browser push subscription: {endpoint, keys: {p256dh, auth}} |
POST | /api/push/unsubscribe | Any signed-in user | Remove one: {endpoint} |
POST | /api/push/test | Any signed-in user | Send yourself a test notification. {"sent": n} |
Phone alerts are Web Push notifications for critical alerts. In EmberCORE, the bell's "Phone alerts" switch in the Global Command, Admin, and Engineer views does all of this for you, and sends a test notification as soon as you turn it on. Device alerts push only from rules that also ring the bell, and storage and cluster alerts push when they are critical. Who receives an alert is decided when it fires: the alert's tenant, plus Fireball staff. Subscriptions must point at a real browser push service (Google, Mozilla, Microsoft, or Apple). On iPhone and iPad, push works only from EmberCORE installed to the Home Screen, not from a Safari tab.
Storage
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/storage/dashboard | Admin | Capacity, volume health, and guardrail status |
GET | /api/storage/devices | Admin | Disks and nodes in the storage pool |
GET | /api/storage/backups | Admin | Backup inventory |
GET | /api/storage/snapshots | Admin | Snapshots |
GET | /api/storage/recurring-jobs | Admin | Scheduled snapshot and backup jobs |
GET | /api/storage/volume-detail | Admin | One volume in detail |
POST | /api/storage/action | Admin | Volume actions |
GET POST PUT | /api/storage/settings | Admin | Storage settings, including guardrails |
POST PUT | /api/storage/backup-target | Admin | Set the backup target |
POST | /api/storage/backup-target/test | Admin | Test a backup target before you rely on it |
Below Global Command, the routes that change storage work on a tenant's own external cluster. See Cinder.
App Store and apps
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/store/apps?tenant_id= | Any role | The catalog you can see |
POST | /api/store/deploy | Engineer | Deploy an app to a node. The response can carry a note, for example when a CODESYS app went on the pod network because its host ports were taken |
GET | /api/store/deployments?tenant_id= | Engineer | Your deployments |
DELETE | /api/store/deployments/{uid} | Engineer | Remove a deployment |
GET | /api/store/safe-control-targets?app_id=&node_name= | Engineer | The Safe Control releases a CODESYS Safe Time Provider can send to |
GET | /api/apps/services | Any role | Running apps for your tenant |
POST | /api/apps/uninstall | Engineer | Uninstall a running app |
ANY | /api/proxy?target= | Any role | Proxy to a running app's interface. The target must belong to your tenant |
AnvilMQ broker
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/admin/broker/status | Engineer | Broker health for your tenant; /drivers, /tags, /connections, and /config under the same path |
GET POST DELETE | /api/admin/broker/users | Admin | Your tenant's broker users |
Flux
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/flux/tenant/summary | Any role | Your tenant's Flux overview |
GET | /api/flux/tenant/services | Any role | Your Flux services; /identities, /edge-routers, and /topology under the same path |
GET | /api/flux/latency?tenant= | Any role | Measured latency to each of your services |
Users
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/admin/users | Admin | Users in your tenant |
POST | /api/admin/users | Admin | Invite a user: {email, displayName, role, tenant, tenantRole}. tenantRole is admin, engineer, operator, or viewer, up to your own |
GET PUT DELETE | /api/admin/users/{email} | Admin | One user |
POST | /api/admin/users/{email}/suspend | Admin | Suspend a user; /activate restores them |
GET | /api/admin/grantable-tenants | Admin | The tenants you may invite people into |
GET | /api/admin/permissions | Admin | Every permission, and which roles hold it |
Audit
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/admin/audit | Operator and above | The recent activity feed |
GET | /api/audit/search | Admin | Search your tenant's audit log: q, actor, action, from, to, limit. Returns {entries, stats} |
GET PUT POST DELETE | /api/audit/export?tenant= | Admin | Export your audit trail to your own S3, Azure Blob, PostgreSQL, or HTTPS endpoint |
POST | /api/audit/export/run | Admin | Run the export now |
GET | /api/audit/export/schema?table= | Admin | The exported schema |
GET | /api/audit/retention | Admin | How long the audit log is kept. Changing it is Global Command only |
GET POST PUT DELETE | /api/reports/schedules | Admin | Scheduled reports; /api/reports/generate runs one |
Ignition
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/ignition/status | Operator and above | Your Ignition gateway's status |
GET | /api/ignition/tenant | Operator and above | Your tenant's Ignition setup |
GET POST PUT | /api/ignition/users | Admin | The gateway accounts EmberCORE signs your people in with |
GET PUT POST | /api/ignition/role-map | Admin | Which Ignition roles each EmberCORE role receives |
Shells and support
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/shell | Admin | WebSocket shell on a node your tenant owns |
GET | /api/shell/pod | Admin | WebSocket shell into one of your tenant's pods |
POST | /api/support/ticket | Any signed-in user | Open a support ticket |
EmberNet Endpoint
| Method | Route | Access | Description |
|---|---|---|---|
GET | /api/endpoints?tenant= | Bearer token | Endpoints in your tenant |
GET | /api/endpoints/cards | Bearer token | Endpoint liveness cards |