npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reclaimprotocol/client

v0.1.11

Published

Typed SDK/HTTP client SDK for the Reclaim Protocol — the programmatic way to use Reclaim Protocol to verify **data points** about your users (a follower count, an account balance, an order). Can be directly integrated by apps to request verification for t

Readme

@reclaimprotocol/client

Typed SDK/HTTP client SDK for the Reclaim Protocol — the programmatic way to use Reclaim Protocol to verify data points about your users (a follower count, an account balance, an order). Can be directly integrated by apps to request verification for their users using Reclaim Protocol and verify the results.

One package gives you:

  • createReclaim(...) — a small, declarative client for the consumer happy path: create verification sessions, register callbacks, and receive + verify results.
  • ReclaimClient — a low-level typed HTTP client (call('OperationId', args)) for any operation in the spec.
  • Crypto helpers — ECIES decryption with the org's eth key, result-JWS signing/verification, JWKS discovery, and nested attestor-proof verification.

Works in Node, Deno, browsers, and edge runtimes (it uses WebCrypto + @noble, no native deps).

The model in one paragraph

Authentication is a single per-org secret (rorg_…, the orgSecret) — a bearer credential; no signatures, no per-call secrets. The org is identified by an Ethereum (secp256k1) public key, set as its keypair. When that keypair's canEncryptResult is enabled, verification results are encrypted with ECIES (secp256k1 + AES-256-GCM) to the org's eth public key, and you keep the matching eth private key (0x-hex — the form any wallet/ethers emits) to decrypt. Ethereum also signs the result JWS with ES256K, which you verify against the Builder's JWKS.

                          ┌─ one-time setup ─────────────────────────┐
  reclaim.callbacks.register({ callbackUrl })      ← where results go
  reclaim.keypair.set(orgId, { publicKey,          ← identity + (optional)
                       canEncryptResult: true })      encrypt to your key
                          └──────────────────────────────────────────┘
                          ┌─ per verification ───────────────────────┐
  reclaim.sessions.create(...) → open session.verificationUrl
        … end-user completes it …
  POST → your callbackUrl → reclaim.results.receive(body) → verified payload
                          └──────────────────────────────────────────┘

Install

npm install @reclaimprotocol/client

Quick start (consumer)

Configure once with your org secret. orgId below is your organization's id (the org the secret belongs to).

import { createReclaim } from '@reclaimprotocol/client'

const reclaim = createReclaim({
  orgSecret: process.env.RECLAIM_ORG_SECRET,   // rorg_… (issued by an org owner)
})
const orgId = process.env.RECLAIM_ORG_ID       // your org's id

1. Register a callback (one-time)

Tell the Builder where to POST verification results. This is one-time setup and can also be done from the dashboard or the agent — see Three ways to set up.

await reclaim.callbacks.register({ callbackUrl: 'https://my.app/reclaim/callback' })

2. Enable encryption (optional, one-time)

By default results are delivered as plaintext signed JWS (still verified). To have the Builder encrypt results so only you can read them, register your org's eth public key with canEncryptResult: true. Use any eth key (the 0x-hex form wallets/ethers emit), or mint one with the SDK:

import { generateEthKeypair } from '@reclaimprotocol/client'
// keep privateKeyHex secret — it decrypts results
const { privateKeyHex, publicKeyHex } = generateEthKeypair()
// (also doable from the dashboard, which can mint the keypair in-browser)
await reclaim.keypair.set(orgId, {
  publicKey: publicKeyHex,   // 0x04+128 hex (or pass publicKeyJwk)
  canEncryptResult: true,
})

3. Create a session (per verification)

const session = await reclaim.sessions.create({
  providers: [{ providerId: 'prov_…' }],   // one or more, proved in order → one combined result
  context: { userId: 'abc' },
})
console.log('open this:', session.verificationUrl)   // hand to the end-user

It's a plain authenticated POST, so you don't need the SDK — curl works too (the org secret is the only auth):

curl -X POST "$BUILDER_URL/verifications/sessions" \
  -H "Authorization: Bearer $RECLAIM_ORG_SECRET" \
  -H 'Content-Type: application/json' \
  -d '{
        "providers": [{ "providerId": "prov_…" }],
        "context": { "userId": "abc" },
        "verificationClientUrl": "https://your.app/v/{sessionId}"
      }'
# → 201 { "id": "…", "verificationUrl": "…", "status": "pending", "mode": "…", … }

4. Receive & verify the result

When the end-user finishes, the Builder POSTs to your callbackUrl. Pass the request body to results.receive. It decrypts (if encrypted) and verifies the signature + nested proofs, and throws if the result is invalid — so reaching the result branch means it's verified.

// in your callback HTTP handler:
const outcome = await reclaim.results.receive(requestBody, {
  credential: process.env.RECLAIM_ETH_PRIVATE_KEY,  // 0x-hex eth key; omit for plaintext
  expectedReclaimSessionId: session.id,
})
if (outcome.kind === 'result') {
  console.log('verified!', outcome.result.payload)
} else {
  console.log('progress event:', outcome.event)          // non-terminal notification
}

Three ways to set up

The one-time setup (registering the callback subscription + the encryption keypair) can be done however suits you — they all hit the same API:

  • This SDKreclaim.callbacks.register(...) + reclaim.keypair.set(...), as above (good for scripting/CI).
  • The dashboard — the org's web console has a callback-subscription form and a "Set encryption keypair" dialog that can generate the eth keypair in your browser and download the private key for you.
  • The agent (@reclaimprotocol/agent — the reclaim CLI / MCP server) — handy while building and testing a provider locally.

Whichever you use, at runtime your backend needs only the org secret (to authenticate) and the eth private key (to decrypt) — the results.receive(...) step is unchanged.


API reference

createReclaim(config)

Returns a Reclaim with four namespaces (sessions, callbacks, keypair, results) plus an api escape hatch.

| ReclaimConfig field | type | required | notes | |---|---|---|---| | orgSecret | string | yes | The org secret rorg_…. Sent as Authorization: Bearer; identifies the org server-side. | | baseUrl | string | no | Defaults to the production Builder. | | fetch | typeof fetch | no | Custom fetch (tests, edge runtimes). | | headers | Record<string,string> | no | Extra headers merged into every request. |

orgSecret is the only config. Everything else is passed where it's used: the orgId to keypair.set/get, and the eth private key (0x-hex) to results.receive/decrypt.

reclaim.sessions

reclaim.sessions.create({ providers, context, verificationClientUrl? })
  // providers: { providerId, version? }[] — proved in order, one combined result; all required.
  // → VerificationSession (has `id`, `verificationUrl`, `status`, `mode`, …)
  // verificationClientUrl defaults to the Builder's own /v/{sessionId} page.
reclaim.sessions.get(sessionId)         // → VerificationSession
reclaim.sessions.listEvents(sessionId)  // → VerificationEventRecord[]

reclaim.callbacks

reclaim.callbacks.register({ callbackUrl, events? })  // events defaults to the 3 terminal events
  // → CallbackSubscription. Org-scoped; no address/publicKey/secret.
reclaim.callbacks.list()                  // → CallbackSubscription[]
reclaim.callbacks.remove(subscriptionId)  // → void

events is any subset of VerificationEvent. The terminal events (verification_success / verification_rejected / verification_error, exported as TERMINAL_EVENTS) deliver the signed result; non-terminal events deliver a plaintext progress notification.

reclaim.keypair (the org's single eth encryption key)

await reclaim.keypair.set(orgId, { publicKey, canEncryptResult?: true, label?: 'prod' })  // → OrgKeypair
await reclaim.keypair.get(orgId)  // → OrgKeypair | undefined (undefined ⇒ keyless / plaintext delivery)

publicKey is the org's eth uncompressed public key (0x04 + 128 hex — the form generateEthKeypair()/ethers produce); alternatively pass publicKeyJwk (a secp256k1 JWK). set stores the JWK + derived eth address. There is exactly one keypair per org; set replaces it (rotation). canEncryptResult toggles whether results are encrypted to it (default false ⇒ plaintext). Keep the matching eth private key (0x-hex) and pass it as the decryption key to results.receive/decrypt.

reclaim.results

reclaim.results.receive(body, opts?)        // → ReceivedDelivery  ← the 90% path
reclaim.results.decrypt(body, credential?)  // → Jws   (ECIES or plaintext → signed JWS)
reclaim.results.verify(jws, opts?)          // → VerifyResultFullOutcome

receive branches on the delivery:

type ReceivedDelivery =
  | { kind: 'result'; result: VerifyResultFullOutcome }  // terminal: decrypted + verified
  | { kind: 'event'; event: VerificationEvent; eventData: Record<string, unknown> } // non-terminal: plaintext

opts (ReceiveOptions) is VerifyResultFullOptions (expectedReclaimSessionId nonce check, expectedAud, attestor allowlists, fetchImpl, now) plus an optional credential — the eth decryption key as a 0x-hex private key string (the key content, not a path or env name). All three methods are async; pass credential only when the delivery is encrypted.

Invalid results throw. receive and verify (and the underlying verifyResult / verifyResultFull) never return a failed verdict — they throw a ResultVerificationError carrying the failure reason and the full outcome (payload, reclaimSessionId, per-proof detail). So the result branch is always verified; wrap the call to inspect failures:

import { ResultVerificationError } from '@reclaimprotocol/client'
try {
  const { result } = await reclaim.results.receive(body, { credential })
  // result.payload is verified
} catch (err) {
  if (err instanceof ResultVerificationError) {
    console.error(err.reason, err.outcome)  // e.g. 'expired' / 'bad-signature' / 'invalid-proof'
  } else {
    throw err
  }
}

A non-terminal event delivery is not a result and never throws — it returns { kind: 'event', … }.

reclaim.api — escape hatch

// fully typed against the OpenAPI spec — for any operation the namespaces don't wrap
const { data } = await reclaim.api('GetVerificationSession', { params: { sessionId } })

Crypto & key helpers

// Eth encryption keys (consumer side)
generateEthKeypair(): { privateKeyHex, publicKeyHex, publicJwk, ethAddress }  // mint an org keypair
loadDecryptionKey(hex): DecryptionKey            // 0x-hex eth private key → raw scalar
uncompressedPubkeyToJwk(hex): Jwk                // 0x04+128 hex → secp256k1 JWK
pubkeyJwkToEthAddress(jwk): string               // secp256k1 JWK → 0x eth address

// ECIES (encryptForEthPublicKey is used by the first-party Verification Client; consumers only decrypt)
encryptForEthPublicKey(publicJwk, bytes): Promise<string>  // → ECIES ciphertext (base64url)
decryptCallback(decryptionKey, body): Promise<Jws>          // ECIES/plaintext → JWS

// Result JWS (eth ES256K)
signResultJws(ethPrivHex, payload, issuer): Jws                // VC-side
verifyResult(jws, opts): VerifyResultOutcome                   // offline signature/nonce check (throws if invalid)
verifyResultFull(jws, opts): Promise<VerifyResultFullOutcome>  // + JWKS discovery + attestor proofs (throws if invalid)
verifyJwsSignature(jws, signerPublicKey), decodeJwsPayload(jws)

// JWKS (signature-key discovery / publishing)
fetchJwks(url), jwksFromPrivateKey(ethPrivHex), selectKey(...), jwkToUncompressedPublicKey(jwk)

// Nested attestor-proof verification
verifyNestedProof(...): NestedProofOutcome

Eth keypair utils (low-level)

generateKeypair(), deriveFromPrivateKey(hex), addressFromPublicKey(...), plus the secp instance and addressFromUncompressedPubkey(...), are exported for the Verification Client and the agent (proof-owner signing). A consumer that enables encryption holds an eth private key (the same key family) to decrypt — see the encryption-key helpers above. canonicalJson (RFC 8785) is also exported (used by content-hash binding).

ReclaimClient (low-level)

import { ReclaimClient } from '@reclaimprotocol/client'
const client = new ReclaimClient({ baseUrl, token, fetch, headers })
const { data, response } = await client.call('GetVerificationSession', { params: { sessionId } })

Errors are thrown as ProblemError (RFC 9457). throwProblemError, DEFAULT_BASE_URL, USER_AGENT, and encoding helpers (b64url, base64urlToBytes, bytesToHex, hexToBytes) are also exported. The call method is fully typed against the generated OpenAPI operations.

Security notes

  • The org secret is a bearer credential — treat it like a password; rotate it from the dashboard (POST /orgs/{orgId}/token, which invalidates the prior one). The server stores only an HMAC of it.
  • Encryption is ECIES over secp256k1 + AES-256-GCM (via eciesjs / @noble) — runs in browsers, Node, Deno, and edge runtimes, and uses the same eth key family as signing.
  • Signature verification stays secp256k1 (not routed through jose) so it works on WebCrypto-only runtimes that lack ES256K.
  • The callback delivery is unauthenticated transport — never trust the raw POST body; trust only the decrypted, signature-verified result that results.receive returns.