Skip to content

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.

  • 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:

Terminal window
npm install
npm --workspace @varco/server run build

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.

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.

  • Unknown path or method: 404 with { "error": "not_found" }.
  • Connection not ready within readyTimeoutMs: 503 with { "error": "unavailable", "message": "Varco connection not ready" }.
  • Route success: 200 with { "ok": true }.
  • Handler success: 200 with 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 as permission_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.

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.