@reclaimprotocol/ai-sdk
v0.2.0
Published
Backend SDK for AI-built Reclaim proofs from any website through Popcorn.
Downloads
450
Maintainers
Keywords
Readme
Reclaim AI SDK
A backend JavaScript SDK. Your backend creates a Popcorn verification URL, your frontend opens it in an iframe, popup, or tab, and your backend receives and validates the proofs. Node.js 18+ is required.
import { Reclaim } from '@reclaimprotocol/ai-sdk'
const reclaim = new Reclaim({
appId: process.env.RECLAIM_APP_ID!,
appSecret: process.env.RECLAIM_APP_SECRET!,
})
const session = await reclaim.initAiVerification('example.com/account', [
{ name: 'fullName' },
{ name: 'email', isOptional: true },
], 'Find the authenticated user’s profile.')
// Return this URL from your endpoint; initialization does not open a window.
const verificationUrl = session.verificationUrl
const proofs = await session.waitForProofs()params accepts parameter names as strings, or { name, isOptional } objects when
you need optional fields. Keep application credentials and proof handling on your
backend. The frontend does not need this package.
Endpoint example
Mount these routes behind your application's authentication and bind each verification to its requesting user. This example uses memory for brevity; use persistent storage and a background worker in production, especially on serverless hosts where work cannot continue after an HTTP response.
import express from 'express'
import { randomUUID } from 'node:crypto'
import { Reclaim, type Proof } from '@reclaimprotocol/ai-sdk'
const app = express()
app.use(express.json())
const reclaim = new Reclaim({
appId: process.env.RECLAIM_APP_ID!,
appSecret: process.env.RECLAIM_APP_SECRET!,
})
const verifications = new Map<string, {
status: 'pending' | 'completed' | 'failed'
proofs?: Proof[]
}>()
app.post('/verifications', async (req, res, next) => {
try {
const session = await reclaim.initAiVerification(
req.body.website, req.body.params, req.body.prompt,
)
const verificationId = randomUUID()
verifications.set(verificationId, { status: 'pending' })
void session.waitForProofs().then(
proofs => verifications.set(verificationId, { status: 'completed', proofs }),
() => verifications.set(verificationId, { status: 'failed' }),
)
res.json({ verificationId, verificationUrl: session.verificationUrl })
} catch (error) {
next(error)
}
})
app.get('/verifications/:id', (req, res) => {
const result = verifications.get(req.params.id)
if (!result) { res.sendStatus(404); return }
// Consume result.proofs in your backend's application logic.
res.json({ status: result.status })
})The frontend calls your endpoint and displays the URL:
const { verificationId, verificationUrl } = await fetch('/verifications', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
website: 'example.com/account',
params: ['fullName'],
prompt: 'Find the authenticated user’s profile.',
}),
}).then(response => response.json())
iframe.src = verificationUrl
// Or open verificationUrl in a popup or another tab on a user click.
// Poll /verifications/:verificationId for your application status.Backend callback URLs
Configure proof and error callback destinations at initialization:
const session = await reclaim.initAiVerification(
'example.com/account', ['fullName'], 'Find the authenticated user’s profile.',
{
callbackUrl: 'https://your-app.example/reclaim/proofs',
cancelCallbackUrl: 'https://your-app.example/reclaim/errors',
jsonProofResponse: true,
},
)
// Save the private descriptor before returning the URL to the frontend.
await saveSession(session.sessionId, session.toJSON())
return { verificationUrl: session.verificationUrl }The JS SDK setters are also available on a freshly initialized session:
session.setAppCallbackUrl('https://your-app.example/reclaim/proofs', true)
session.setCancelCallbackUrl('https://your-app.example/reclaim/errors')
const verificationUrl = await session.getRequestUrl()Set callbacks before opening or sharing verification. After a setter, call
getRequestUrl() to regenerate the URL; reading verificationUrl or serializing
the session before regeneration throws instead of returning an outdated URL.
Restored sessions use their saved URL and callback configuration; configure
destinations before serialization. getAppCallbackUrl(),
getCancelCallbackUrl(), and getJsonProofResponse() expose the current settings.
Portal sends a POST to the proof callback with X-Reclaim-Session-Id. With
jsonProofResponse: true, the body is a JSON proof array. The default is false,
which sends URI-encoded JSON with application/x-www-form-urlencoded; read the
raw text and use JSON.parse(decodeURIComponent(rawBody)) for that mode.
Errors are always JSON POSTs to the cancel callback, including the session ID,
exception type, and message. Custom destinations must be public HTTPS URLs on
port 443; a public HTTPS tunnel is needed for a callback served on localhost.
Portal enforces its existing callback destination and DNS rules.
Custom proof callbacks send proofs directly to your endpoint instead of storing
them at Reclaim. In this mode, waitForProofs() throws immediately. Validate the
incoming proofs with session.verifyProofs(payload) and save the result before
acknowledging the callback. This method retrieves the session's actual generated
provider version from the configured SDK backend, validates signatures, provider
content, signed session context, and extraction requirements using the same
checks as waitForProofs(). It accepts a proof or proof array and returns
Proof[]. It has a 30-second timeout by default and supports
{ timeoutMs, signal }. Empty or invalid payloads fail validation. It can run
before the callback is acknowledged; it does not require a submitted backend status.
For example, with the existing Express app and JSON proof mode:
import { AiVerificationSession } from '@reclaimprotocol/ai-sdk'
app.post('/reclaim/proofs', async (req, res, next) => {
try {
const sessionId = req.get('X-Reclaim-Session-Id')
const saved = sessionId && await loadSession(sessionId)
if (!saved) { res.sendStatus(404); return }
const session = AiVerificationSession.fromJSON(saved)
const proofs = await session.verifyProofs(req.body)
await saveVerifiedResult(session.sessionId, proofs)
res.sendStatus(200)
} catch (error) {
next(error)
}
})saveSession, loadSession, and saveVerifiedResult are your application's
persistent storage operations. Use the signed session and validated proofs for
acceptance; the session header identifies which saved descriptor to load. Keep
callback handling idempotent because a failed delivery can be retried. A custom
cancel callback routes errors to your handler, so coordinate cancellation there
with your application's worker/frontend instead of relying on backend polling.
Proof context
Include application data such as a user or order ID in the signed proof context:
const session = await reclaim.initAiVerification(
'example.com/account', ['fullName'], '',
{ context: { userId: 'user-123', orderId: 'order-456' } },
)Configure context when initializing the session. It is saved in the private session descriptor and retained when restoring the session.
Context must be a JSON-serializable object. The SDK adds reclaimSessionId and
attestation fields automatically; custom context cannot override those fields or
extractedParameters. Context is specific to a session. It is visible in the
verification request, so keep secrets out of it.
Both verifyProofs() and waitForProofs() require every configured context field
to match the signed JSON.parse(proof.claimData.context) on every proof. Nested
objects and arrays are compared as JSON values. Missing or mismatched context
fails with SESSION_MISMATCH; generated proof fields may accompany your context.
Without custom context, verification keeps its existing behavior.
API behavior
Verification reads the authenticated account and returns signed proofs for the requested fields. All required fields must be present in verified proof data; missing optional fields do not block completion.
After confirmed login, account data collection runs for at least two minutes. When the required fields and their supporting evidence are ready, verification can continue. Missing required fields or evidence after three minutes fail the verification. Proof generation and delivery may take additional time.
Portal proofs trust the Portal's registered signer. The SDK validates the signed proof's registered signer, signature, session, application context and required parameters. Witness proofs also require matching provider content.
Reclaim accepts optional backendUrl and portalUrl settings for local or
staging deployments. backendUrl must be an HTTP(S) origin. Session descriptors
retain this origin so backend workers poll and fetch the same provider versions.
The underlying JS SDK sets its backend globally during initialization; use a
separate process for each backend environment.
Request 1–32 unique identifier names of up to 64 characters. At least one must be
required; isOptional defaults to false. Custom instructions can be empty and
are limited to 10,000 characters. Each generated HTTP request can use at most 12
response matches to bound the underlying SDK's optional hash enumeration.
Missing optional values are absent. Required values must be present in verified
proof data.
waitForProofs({ timeoutMs, signal }) returns Proof[] after JS SDK
signature verification, signed session, and extraction validation. Witness proofs
also match the exact session provider configuration. Portal proofs attest facts
directly and do not require a published provider version or config lookup. For
batches whose signed contexts contain both isPortalProof: true and
isAiProof: true, the SDK uses JS SDK verifyProof with provider-content matching
disabled; registered-signer and signature verification stay enabled. A batch
containing a witness proof retains provider-version and hash validation. Default wait:
10 minutes; normal polling: 3 seconds; proof-generation failure grace: 30 seconds.
Transient status network errors use bounded backoff. Aborting stops local waiting.
Verified values come from JSON.parse(proof.claimData.context).extractedParameters;
the proof's display-only extractedParameterValues is not independently trusted.
AiVerificationError.code distinguishes provider creation, session mismatch,
invalid proofs, missing/conflicting values, cancellation, verification failure,
timeout, and permanent HTTP errors.
For a background worker, persist session.toJSON() on the backend and restore it
with AiVerificationSession.fromJSON(savedDescriptor). Keep descriptors and
verification URLs private to the owning user's session. Only return the URL and
your application's verification ID to the frontend.
Every initialization creates a fresh verification session and URL. Persist the
complete descriptor returned by session.toJSON() rather than constructing or
editing it yourself.
Provider resolution is not retried automatically. A concurrent creation may
return HTTP 409 with Retry-After: 1; retry initAiVerification to sign a fresh
request. Disabled or archived providers are rejected. The Reclaim backend
must have a registered enabled application and an active creator. Proof delivery
uses Reclaim's default backend callback and polling unless a custom callback is
configured. Frontends choose how to open the verification URL.
Local playground
Run npm run build, configure the server-side application credentials using
the playground setup, then start
node examples/web/server.mjs. Open http://localhost:3333 to request and inspect
verified proofs. Form inputs survive refresh, and recent inputs can be reused.
The example binds to loopback and keeps application secrets on the server.
Development: npm ci, npm run typecheck, npm test, npm run build.
