Skip to content

The <wallet-verify> element

import '@wallet/verify' // registers <wallet-verify>
import type { WalletVerifyElement, VerifyResult } from '@wallet/verify'
Attribute Default Meaning
endpoint your tenant URL: https://hakim-verify-api.vercel.app/v1/<publishable key>
wallet-name Digital wallet shown in the header, the QR centre and the tag under the QR
wallet-logo image URL for the same places; a square mark works best
fields comma-separated labels for “We will ask for”; add =claim or =a+b to show values once verified
session-id adopt an existing session instead of starting a new one (a reloaded page keeps its QR)
tagline Instant approval header subtitle
heading, heading-tap, heading-verified, heading-expired copy above the QR: desktop, phone, done, expired
ask-heading, received-heading We will ask for, We have received list titles
reload-label Reload QR the button on a dimmed QR
link-host iOS Universal Link host of the wallet (https://<host>/open)
ios-scheme the wallet’s private iOS URL scheme, used when no Universal Link applies
app-link true Android: open an App Link on your own site instead of the wallet deep link; set false unless you serve the /w/<id> route (Same device)
dc-api off also try the browser’s Digital Credentials API on tap
expanded true accordion state
no-header off hide the header row
auto true start a session as soon as the element is shown
idle-timeout 15000 ms before an untouched QR dims and offers Reload (0 = never)
fire-timeout 10000 ms before an unanswered tap dims and offers Reload
Event detail When
session { id, deepLink, expiresAt, … } a session started (also after Reload; then POST /new carried { supersedes })
verified the server result with claims, docType the wallet’s answer verified
failed { error } verification failed
cancelled the holder dismissed the request
expired the request expired
toggle { expanded } the header was clicked

All events bubble and cross the shadow boundary.

const el = document.querySelector('wallet-verify')
el.addEventListener('session', (e) => saveSessionId(e.detail.id))
el.addEventListener('verified', (e) => confirmOnServer(e.detail))

start(), reload(), cancel(); state (idle, loading, ready, waiting, cancelled, verified, failed), session, result.

CSS custom properties on the element: --wallet-accent, --wallet-accent-tint, --wallet-ink, --wallet-muted, --wallet-line, --wallet-star, --wallet-spinner, --wallet-ok, --wallet-radius, --wallet-frame-size. Fonts are inherited from the page. Parts for deeper styling: card, header, icon, title, tagline, body, heading, frame, status, tag, note, ask, reload, spinner.

wallet-verify { --wallet-accent: #235f3f; --wallet-accent-tint: #e9f4ee; --wallet-star: #c2691f; }
wallet-verify::part(card) { box-shadow: none; }
  • Desktop: the QR is scan-only. Phone: tapping it opens the wallet (App Link, Universal Link, private scheme, or openid4vp://).
  • Same device: if the tab regains focus while waiting with no result, the QR dims and offers Reload.
  • Verified: the QR becomes a tick and the fields list shows the received values.

Next: the Verify API.