REST API & webhooks
On the Pro plan and above you can integrate KNX Clarity with your own systems: pull data over a read-only REST API, and receive webhooks when things change. Both are managed under Settings.
The REST API and webhooks are Pro / Enterprise features — see Plans & billing. Pro includes up to 5 API keys and 5 webhooks; Enterprise raises both to 50.
API keys
Open Settings → API-Schlüssel and click Neuer API-Key. Give it
a name and an expiry (never, 30 days, 90 days, or 1 year). The key is
issued with the four read scopes — me:read, projects:read,
devices:read, groupaddresses:read. Write access is not available
yet.
The plaintext token (kx_…) is shown exactly once at creation
— copy it then, because only a hash is stored and it can never be shown
again. Revoke a key any time with the trash icon; the row stays for the
audit trail but the token stops working immediately.
Using the REST API
Send the token as a bearer header:
Authorization: Bearer kx_your_token_here
The base URL is issued per environment (the RestApiUrl value for your
stack). Every response is scoped to the token's organisation — you
never pass an org id. Endpoints:
| Method & path | Scope | Returns |
|---|---|---|
GET /v1/health | none | { "ok": true } |
GET /v1/me | me:read | the org, the user, and the key |
GET /v1/projects | projects:read | a list of projects |
GET /v1/projects/:id | projects:read | a single project |
GET /v1/projects/:id/devices | devices:read | the project's devices |
GET /v1/projects/:id/groupaddresses | groupaddresses:read | the project's group addresses |
The device and group-address endpoints are addressed per project
rather than per organisation, because that is how the data is
indexed — and how you almost certainly want to pull it. Use
GET /v1/projects to get the ids.
Every list returns { items, count, hasMore } and stops at 100 items.
hasMore: true means there were more than that: v1 has no cursor yet,
so treat it as a signal to narrow your query rather than to page.
Errors come back as { "error": { "code", "message" } } — a 401 for
a missing/invalid/expired key, a 403 (scope_required) when the key
lacks the scope, and a 404 for a project your key's organisation
does not own (the same 404 as for one that does not exist, so the
API cannot be used to probe for project ids). The API is intentionally
minimal for now: read-only, no cursor pagination, no per-key rate
limiting, no CORS.
Webhooks
Open Settings → Webhooks and click Neuer Webhook. Provide a name, an HTTPS endpoint URL, and the events to subscribe to:
project.created,project.updated,project.archivedservice_case.created,service_case.status_changed
A signing secret (whsec_…) is shown once at creation. Use the
Test-Event senden button to fire a webhook.test delivery while
you build your receiver.
Payloads and signatures
Each delivery is a POST with these headers:
X-KNX-Event— the event type.X-KNX-Delivery— a per-attempt UUID (use it as an idempotency key).X-KNX-Signature—t=<unix-seconds>,v1=<hex>, a Stripe-style HMAC.
The body is JSON: { id, type, orgId, eventId, createdAt, triggeredByUserId, data }.
To verify a delivery, compute the HMAC over "<t>.<rawBody>". The HMAC
key is the SHA-256 hash (hex) of your signing secret, not the
secret itself:
import crypto from "node:crypto";
function verify(signingSecret, header, rawBody) {
const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
const key = crypto.createHash("sha256").update(signingSecret).digest("hex");
const expected = crypto
.createHmac("sha256", key)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
const ok = crypto.timingSafeEqual(
Buffer.from(parts.v1),
Buffer.from(expected)
);
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return ok && fresh;
}
Reject anything where the signature doesn't match or the timestamp is more than a few minutes old (replay protection).
Retries and auto-disable
A delivery is attempted up to 3 times (immediately, after 5 s,
after 30 s). Network errors, 5xx, and 429 are retried; any other
4xx is treated as permanent. If a webhook fails 5 batches in a
row it is automatically disabled and the creator is notified —
re-enable it with Aktivieren once your endpoint is healthy. Every
attempt is recorded in the delivery log (Zustellungen anzeigen),
kept for 30 days.