Install
The snippets below use Next.js App Router, but@opendatalabs/vana-sdk isn’t tied to Next — the server controller runs in any Node backend and the hook works in any React app. Use an existing Next.js project (or adapt the routes to your framework). To start fresh:
Configure the backend
Set server-side environment variables in.env.local:
.env.local
VANA_ENV=production for the current Direct app flow. Set VANA_NETWORK=moksha (testnet) while developing, or VANA_NETWORK=mainnet for your live app — see Choose your network.
Create lib/vana.ts:
lib/vana.ts
network — you don’t pass an address. Just fund escrow for your app on that network. (Pass an escrow config only to override the defaults for a custom deployment.) Replace source and scopes with values from the selected source detail or connector schema.
Create API routes
Create an access request route:app/api/vana/request/route.ts
app/connect/return/page.tsx
app/api/vana/status/route.ts
app/api/vana/data/route.ts
readApprovedData reads from the user’s Personal Server. If payment is required, the SDK signs the protocol challenge with your app key, pays from your app’s escrow using the network’s escrow contract and gateway, retries with X-PAYMENT, and returns the paid read result. If your app’s escrow balance is unfunded, the read fails with Insufficient finalized balance — fund escrow and retry.
readApprovedData reads one scope. Called with no scope, it reads the first scope the user approved. If your request covered several, either name the one you want or read them all:
readAllApprovedData reads the scopes in approval order and returns each one under results, keyed by scope. A scope that fails lands in errors instead of aborting the rest, because the scopes read before it have already settled their fees — check errors before treating the read as complete.
If any scope fails, the request is left unacknowledged so the failed scopes stay retryable. Read them with readApprovedData({ requestId, scope }), and let the last of those calls acknowledge.
Reading several scopes with your own loop needs one extra step: a successful readApprovedData acknowledges the request, which moves it to completed — terminal, and no longer readable. Pass acknowledge: false on every call but the last, or use readAllApprovedData, which acknowledges once at the end.
Add React
The frontend calls your backend, opens Vana approval, polls status, asks your backend to read approved data, and renders the returned result.app/components/ConnectSpotifyButton.tsx
<ConnectSpotifyButton /> from a page in your app.
Polling is bounded by the SDK:
useDirectVanaConnect accepts pollIntervalMs and timeoutMs (default 5 minutes, then the flow errors out instead of polling forever). Use these instead of hand-rolling a polling loop — an abandoned approval tab on a hand-rolled loop keeps hitting your backend and the gateway indefinitely.