Skip to main content

Web Services

Web Services let you expose custom HTTP endpoints backed by a script you write in the admin interface — for webhooks, third-party integrations, and bespoke APIs that don't fit the standard REST surface. Each service is served under /web_service/<name>.

How a request is routed

The path after the service name is handed to your script as a sub-path:

/web_service/myapi/devices/42
name = "myapi"
path = "/devices/42"
segments = ["devices", "42"]

Your service is matched by name and HTTP method, so one name can have separate handlers for GET, POST, etc.

Writing a service

A service is a script that defines a handleRequest function. The script runs in a sandboxed context with a curated set of globals:

GlobalWhat it is
gemThe GemServer instance — the full server API (gem.command, gem.getAttribute, gem.zones, …).
fetchOutbound HTTP, for calling external services.
storeA per-service in-memory object that persists across requests (resets on reload). Use it for caches, tokens, counters.
console, Buffer, URL, URLSearchParams, atob/btoaStandard utilities.
JSON, Math, Date, timers, and core built-insStandard JS.
// A minimal web service script
handleRequest = async (req, res) => {
if (req.segments[0] === 'devices') {
const id = Number(req.segments[1]);
const device = gem.devices[id];
return res.json({ id, online: device?.online ?? null });
}
res.status(404).json({ error: 'not found' });
};

Authentication

Each service sets an auth type:

  • none — open endpoint.
  • basic — HTTP Basic auth against the service's configured username/password.

Manage services, scripts, and auth from the admin interface.

Open Web Services

:::note gem.* is a frozen surface Like the macro-script surface, gem.* inside a web service is an API contract — methods are added, never repurposed. Build against the documented helpers and prefer them over raw database access. See Macro Scripts. :::