Server-side and webhook access
Use @varco/server to drive Home Assistant from a backend or a webhook: an external tool sends an HTTP request, and Varco runs a scoped Home Assistant operation. It runs the same Varco Consumer that the browser client runs, just server-side with its own HTTP endpoint.
For the shortest setup, start with the Server Consumer quickstart. This guide adds the pairing, webhook, and framework details.
No Home Assistant token leaves Home Assistant. The bridge stays opaque. Home Assistant stays the Authority for consent, policy, and audit.
Prerequisites
Section titled “Prerequisites”- Node 22 or newer.
- A base64url Ed25519 private key for the Server Consumer identity.
- The Authority id from your Home Assistant Varco panel.
- A reachable bridge URL.
@varco/server is currently a repository workspace package and is not published to npm. Check out this repository, then install dependencies and build it with Node.js 22 or newer:
npm installnpm --workspace @varco/server run build1. Pair the Server Consumer
Section titled “1. Pair the Server Consumer”Pairing is a one-time, human-approved step. It is not driven by the runtime. Provide a manifest and request a pairing code, then approve it in the Varco panel.
import { createVarcoServer } from "@varco/server";
const server = createVarcoServer({ privateKey: process.env.VARCO_PRIVATE_KEY!, authorityId: process.env.VARCO_AUTHORITY_ID!, bridgeUrl: "wss://varco-bridge.example.com", manifest: { name: "My backend", version: "1.0.0", read_entities: [], subscriptions: [], history: [], camera_snapshots: [], actions: ["switch.turn_on@switch.ev_charger", "switch.turn_off@switch.ev_charger"], },});
const { pairing_code } = await server.requestPairing();console.log("Approve this code in the Varco panel:", pairing_code);The Owner approves the request in Home Assistant, which creates the grant bound to the Server Consumer public key.
2. Run the server
Section titled “2. Run the server”Once a grant exists, the manifest is optional: the consumer authenticates against the stored grant. Define Routes and Handlers, then listen.
import { createVarcoServer } from "@varco/server";
const server = createVarcoServer({ privateKey: process.env.VARCO_PRIVATE_KEY!, authorityId: process.env.VARCO_AUTHORITY_ID!, bridgeUrl: "wss://varco-bridge.example.com", routes: [ { path: "/charger/on", domain: "switch", service: "turn_on", target: { entity_id: "switch.ev_charger" } }, { path: "/charger/off", domain: "switch", service: "turn_off", target: { entity_id: "switch.ev_charger" } }, ], handlers: [ { path: "/webhook", handle: (req, client) => { if (req.headers["x-secret"] !== process.env.WEBHOOK_SECRET) { return { status: 401, body: "unauthorized" }; } const body = req.body as { scene?: unknown }; if (typeof body?.scene !== "string") return { status: 400, body: "scene is required" }; return client.callService("scene", "turn_on", { entity_id: body.scene }); }, }, ],});
await server.start();await server.listen(8080);A POST /charger/on now turns on the charger and returns { "ok": true }. The webhook Handler verifies the caller before acting.
How responses map
Section titled “How responses map”- Unknown path or method:
404with{ "error": "not_found" }. - Connection not ready within
readyTimeoutMs:503with{ "error": "unavailable", "message": "Varco connection not ready" }. - Route success:
200with{ "ok": true }. - Handler success:
200with the returned JSON, or the explicit{ status, headers, body }you return. - Permission, PIN, or target-resolution denial from Home Assistant:
403. The Authority reports unresolved area, device, or label targets aspermission_denied. - A remote error with code
not_found:404. The current Authority does not use that code for service-target resolution. - Other Route or Handler failures:
502. - An unexpected failure in the Node
listen()shim:500. - A Handler can return its own
401,400, or other status for caller authentication and validation.
Framework integration
Section titled “Framework integration”server.handle(req) is framework-agnostic. Build a ServerRequest from your framework’s request and forward its response. server.listen() is a thin Node http shim for when you do not already run a web framework.
See the Server API reference for the full option and type list. The design rationale is in the server Consumer ADR.