Edge API
The Edge web app is a plain client of a REST API under /api/v1.
Everything the app does, you can do with the API. The full endpoint
reference is the Edge API reference, rendered from
entrosity-edge.backend/api/openapi.yaml, the spec the server's router and
request validation (and the app's TypeScript types) are generated from.
Conventions
- Base URL:
https://hub.entrosity.com/edge/api/v1(the proxy strips/edge). The connector API is under/edge/api/connector/v1(Edge connector protocol). - Authentication:
Authorization: Bearer <product token>, a five-minute token from Entrosity Hub for the productedge(POST /api/platform/v1/auth/product-token {"product":"edge"}with the Hub's session, Hub API). Edge has no sign-in of its own; users, tenants and roles come from the Hub. - Access rules: every route has a rule in
entrosity-edge.backend/internal/http/portal/access.go: public, signed in, global admin, or a tenant permission (Roles and permissions). Tenant users may only call/tenants/{their tenant id}/…; any other tenant answers 404. A missing permission answers 403. - JSON bodies with
snake_casekeys, at most 1 MB. Request schemas refuse unknown fields (422). - Lists: cardholders, cards, the audit logs and the admin tenant list
take
?page=&page_size=(default 50, maximum 200) and returnitems,page,page_sizeandtotal. Other lists return all items. - Access log:
GET /tenants/{tenantID}/eventsis newest first with keyset pagination:limit(1–500, default 100), filtersfrom,to,door_id,controller_id,cardholder_idandtype(repeat for several). The answer hasitems,has_moreand, when there is more,next_before_atandnext_before_idto pass asbefore_atandbefore_id. - Rate limit: 20 requests per second per client IP, burst 60
(
EDGE_API_RATE_PER_SECOND,EDGE_API_RATE_BURST); 429 beyond. - Every response carries
X-Request-ID.
Errors
Errors are RFC 7807 application/problem+json with a stable code:
{
"type": "/problems/card_already_registered",
"title": "Conflict",
"status": 409,
"code": "card_already_registered",
"detail": "a card with this number is already registered",
"request_id": "…"
}
Validation errors have code: "validation" and fields with a message
per field. All codes: Error codes.
Asynchronous operations
Some calls start a job on the connector and return the job at once:
| Call | Job | Result |
|---|---|---|
POST /tenants/{tenantID}/connectors/{connectorID}/discover {driver, transport?, addresses?} | edge.discover | {controllers: [{driver, target, model, serial, firmware, door_mode}]} |
POST /tenants/{tenantID}/controllers/{controllerID}/test | edge.controller.test | {driver, target, model, serial, firmware} |
POST /tenants/{tenantID}/doors/{doorID}/open | edge.door.open | none; the event door_opened_remote follows |
Poll GET /tenants/{tenantID}/jobs/{jobID} until status is
succeeded, failed, timeout or cancelled; result, error_code
and error carry the outcome. Discovery and door opening need an online
connector (409 connector_offline).
Configuration syncs are not started by the API: every change that affects
a controller schedules one (Configuration sync).
POST /tenants/{tenantID}/controllers/{controllerID}/sync forces a full
resend.
Controller PINs
A TrackBase002 controller's PIN is write-only:
POST /tenants/{tenantID}/controllersandPATCH /tenants/{tenantID}/controllers/{controllerID}takepin(integer, 1–4294967294). Only the drivertrackbase002has a PIN: for any other driverpinis a 422validationerror on the fieldpin.- The update also takes
clear_pin: true, which removes the PIN;pinandclear_pintogether are a 422. - Controllers are returned with
pin_set(boolean); the PIN itself is never returned, and the audit log records only whether one is set. - Edge stores the PIN encrypted with
EDGE_MASTER_KEY. Without it, setting or clearing a PIN answers 409master_key_missing. - A new or cleared PIN, like a new address, transport or unit id, sends the controller's configuration again (Edge connector protocol).
Connector updates
- Connectors carry
update_channel(stableorbeta), thecapabilitiesof their lasthello(edge_update: the connector updates itself), andupdate_versionandupdate_offered_at: the release last offered to it. Tenant admins change the channel withPATCH /tenants/{tenantID}/connectors/{connectorID}{"update_channel": "beta"}. GET /admin/connector-releases(global admins) lists the stored releases, newest version first, withsigning_enabledand thepublic_keyconnector builds must embed.- CI publishes releases with
POST /admin/connector-releases?version=&channel=stable|beta¬es=,Authorization: Bearer <EDGE_RELEASE_TOKEN>(not a Hub token) and the raw MSI as the body (application/octet-stream, at most 64 MiB). This call is not in the API reference; see Running Entrosity Edge.
Live updates
The app receives live updates as server-sent events:
POST /auth/sse-token {"tenant_id": …}returns a token valid for 60 seconds, bound to the tenant and the session.GET /tenants/{tenantID}/stream?sse_token=<token>(or with the bearer token) streamstext/event-stream, with a keep-alive comment every 25 seconds.
| Event | Data |
|---|---|
access.events | An array of new access log entries (as in the list API). |
controller.status | {controller_id, status, error?} |
controller.sync | {controller_id, sync_status, error?} |
door.state | {door_id, controller_id, open, locked} |
connector.status | {connector_id, status} (online, offline, deleted) |
job.update | A connector job changed state. |
Sensitive actions
Deleting an enrollment token permanently
(POST /tenants/{tenantID}/enrollment-tokens/{tokenID}/delete) is for
global admins and needs a step_up_token: the Hub issues it for the
admin's password (POST /api/platform/v1/auth/step-up, product edge,
Hub API).