Skip to main content

Dashboard & Policies

The ZKPassport Dashboard lets you define a verification request — its query and branding — in one place and reference it from your code by id. A saved request is called a policy.

Applying a policy with .policy("pol_xyz") produces the same verification flow as a self-served request; the difference is that the request lives in the dashboard, so you can change it without redeploying, reuse it across apps, and get auditable proof storage.

Setup

Register your domain at dashboard.zkpassport.id, set your branding, and create a policy. You'll get a policy id like pol_xyz to use below.

Applying a policy

Call .policy() with your policy id instead of chaining builder methods. The query and branding come from the dashboard, so you don't repeat them in code.

import { ZKPassportQRCode } from "@zkpassport/ui/react";

export default function VerifyPage() {
return (
<ZKPassportQRCode
query={(queryBuilder) => queryBuilder.policy("pol_xyz").done()}
onResult={({ verified, uniqueIdentifier }) => {
if (verified) console.log("Verified", uniqueIdentifier);
}}
/>
);
}

What a policy locks

A policy is immutable: the SDK fetches it from the dashboard by domain and locks the request. The query is fixed, branding defaults to your dashboard project, and the scope is locked to <policy-id>:<version> (e.g. pol_xyz:1) — which keeps the user's unique identifier stable until you bump the policy version. You can still override purpose in code for a request-specific message.

Because the query is fixed, .policy() must be called on its own and only once:

queryBuilder.gte("age", 18).policy("pol_xyz").done(); // ❌ can't combine with builder methods
queryBuilder.policy("pol_abc").policy("pol_xyz").done(); // ❌ can't call twice
queryBuilder.policy("pol_xyz").done(); // ✅

If the domain isn't registered or the id doesn't match a policy, .policy() throws with a clear message — register the domain (or check the id) in the dashboard, or fall back to the self-served flow.