Crossdeck University
Watch — the gate that a browser can't tamper with Film in production
0:00 / 0:00
Lesson 1 of 3 · Server & frameworks

Server-side with the Node SDK

The web SDK gates your UI; the Node SDK gates the truth. Anything that costs money or exposes data should be checked on the server, where a browser can't tamper with it — and that's one package.

5 min Node

When you're done: your backend gates features, captures errors, and verifies rail webhooks — authoritatively.

1 What this is & why it matters

The client check is a courtesy; the server check is the law

Gating in the browser (the isEntitled you used to hide a button) is the right call for UI — it's fast and it makes the experience clean. But it lives on the user's machine, and a determined person can flip it. So the rule from Verify your setup applies in full here: anything that costs you money or exposes private data must be checked on the server.

The Node SDK is that server-side check — plus error capture for your backend and signature verification for inbound rail webhooks. One package, the three server jobs.

2 How to gate on the server

CrossdeckServer, with your secret key

Install the package, create a server client with your secret key, and check entitlements where they can't be tampered with:

// npm install @cross-deck/node
import { CrossdeckServer, verifyWebhookSignature } from "@cross-deck/node";

const server = new CrossdeckServer({ secretKey: process.env.CROSSDECK_SECRET_KEY });

// gate on the server, where the client can't lie about it
if (await server.isEntitled({ userId }, "pro")) {
  // …run the expensive / privileged operation…
}

And verify inbound rail webhooks before you trust them — a forged webhook should never grant access:

const event = verifyWebhookSignature(rawBody, req.headers["crossdeck-signature"], process.env.CROSSDECK_WEBHOOK_SECRET);
// throws on a bad signature — only verified events get here
3 How it works, piece by piece

Same truth, authoritative read

The server client authenticates with your secret key — never the publishable one that ships to browsers — and reads the same entitlement truth your dashboard does. isEntitled({ userId }, key) resolves the user through the very same resolver every webhook and event uses, and returns their current, authoritative access — not a warm client-side cache that could be stale or spoofed.

The same client captures server-side errors into the same Issues board as your front end, so a 500 in your API and a crash in your UI land in one place. And verifyWebhookSignature(payload, header, secret) rejects any rail event whose signature doesn't check out — the boundary that stops a forged "subscription active" from ever reaching your grant logic.

4 The green result

A gate that holds under pressure

With the server check in place, the privileged path runs only for genuinely entitled users — no matter what the browser claims — and your webhooks only act on events Crossdeck actually sent. That's the difference between a feature flag and a security boundary.

your backend · @cross-deck/node
server-verified

Entitlement checked server-side with the secret key, webhook signature verified — the gate the client can't pry open.

new CrossdeckServer({ secretKey }) gives you authoritative isEntitled({ userId }, key), server error capture, and verifyWebhookSignature(payload, header, secret) — all with your secret key, never the publishable one. Gate anything that costs money or exposes data here, not just in the browser.