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 first and only once. The exception is bind: bound values (like the user's wallet address) are only known at request time, so they are never part of a policy and can be added after .policy():

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(); // ✅
queryBuilder.policy("pol_xyz").bind("user_address", address).done(); // ✅ .bind() may follow

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.