> ## Documentation Index
> Fetch the complete documentation index at: https://docs.proofable.me/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> SDK and CLI for verification gates, reusable proof, and agent permissions.

Verify a user or agent once. Check that result on your server.

Use `@proofable/sdk` for `api.proofable.me` so headers and paths stay correct.

## Browser vs server

| Layer | Use |
| - | - |
| Browser | **Hosted Verify** or **`VerifyGate`** for interactive checks ([Hosted sign-in](../verification/hosted)) |
| Server | **`gateCheck`** for the final decision; advanced: **`verifyFromApp`**, access keys |

One check needs no gate. Send the person to Hosted Verify and read the returned proof ID. Reach for **`defineGate`** + **`gateCheck`** when the decision needs several checks, a price, or a schedule.

Do not put **`npk_*`** or other secrets in browser bundles. If you need calls the SDK does not expose from the client, **proxy through your backend**. [API overview](../api/overview).

## Install

```bash theme={"system"}
npm install @proofable/sdk
```

Upgrading from the earlier `@neus` packages? See [Migrate from @neus](../migrate) for the rename map and the one-path upgrade.

## Hosted URL (browser default)

```javascript theme={"system"}
import { getHostedCheckoutUrl } from '@proofable/sdk';

window.location.assign(
  getHostedCheckoutUrl({
    verifiers: ['proof-of-human'],
    returnUrl: 'https://app.example.com/callback',
  }),
);
```

`getHostedCheckoutUrl` supports login, a published gate, or direct verifier checks. These are separate recipes: `intent: 'login'` removes checkout parameters, and `gateId` owns the gate policy instead of mixing with `verifiers` or `preset`. Shared handoff options are `returnUrl`, `mode`, `origin`, and `oauthProvider`; advanced sponsor options are `appId` and `billingWallet`.

For agent identity + delegation, use `getHostedAgentCreateUrl` so dedicated-wallet identity and controller approval stay in the correct order. [Agent create](../mcp/agent-create) and [Hosted Verify](../verification/hosted).

React: [Widgets](../widgets/overview).

## Client configuration

```javascript theme={"system"}
const client = new ProofableClient({
  // Optional: API base URL (default: https://api.proofable.me)
  apiUrl: 'https://api.proofable.me',
  // Optional: request timeout (ms)
  timeout: 30000,
});
```

Other options (advanced): `apiKey` (server key, sent as `Authorization: Bearer`), `appId`, `billingWallet`, `appLinkQHash`, `paymentSignature`, `extraHeaders`, `hubChainId` (advanced; defaults to the canonical identity chain — on-chain anchoring is per-proof opt-in), `enableLogging`. Keep `apiKey` and any secret server-side only.

## App attribution (`appId`)

Set `appId` only for advanced server/app attribution flows. It is public, not a secret, and it is not required for inline `gateCheck`, published listing checkout, or `gateCheck({ gateId })`.

```javascript theme={"system"}
const client = new ProofableClient({
  apiUrl: 'https://api.proofable.me',
  appId: 'acme-web',
});
```

## Verification options

Pass these under `options` on `client.verify(...)`:

| Option | When to use |
| - | - |
| `privacyLevel: 'public' \| 'unlisted' \| 'private'` | Control who can read the proof |
| `enableIpfs: true` | Pin proof data to IPFS |
| `storeOriginalContent: true` | Keep the original content with the proof |
| `targetChains: [...]` | Anchor/publish to specific chains |
| `publishToHub: true` | Record the proof on the hub chain. Offchain by default. Does not change who can see the proof. |
| `publicDisplay: true` | Allow public display of the proof |
| `meta`, `verifierOptions` | App metadata and per-verifier options |

Reuse-vs-create is a **widget** concern: the `strategy` prop (`reuse-or-create` default, `fresh`, `reuse`) lives on [`VerifyGate`](../widgets/verifygate), not on `client.verify()`. [Verification patterns](./verifications) for privacy and widget options.

## Optional: `client.verify()` (signs in the browser)

Use only when you intentionally keep signing in **your** page (wallet extension or injected provider):

```javascript theme={"system"}
import { ProofableClient } from '@proofable/sdk';

const client = new ProofableClient({ apiUrl: 'https://api.proofable.me' });
const provider = window.ethereum; // EVM injected provider

const res = await client.verify({
  verifier: 'ownership-basic',
  content: 'Hello Proofable',
  wallet: provider,
});

const qHash = res.qHash;
const record = await client.getProof(qHash);
```

## Advanced: manual signing (full control)

When you assemble `verifierIds` / `data` yourself, then sign the standardized string. The example below is EVM. For non-EVM, pass the provider explicitly and include `chain` as a CAIP-2 value. See [CAIP-380 Portable Proof](../learn/standards/caip-380).

```javascript theme={"system"}
import { ProofableClient, standardizeVerificationRequest, signMessage } from '@proofable/sdk';

const provider = window.ethereum; // EVM injected provider
const client = new ProofableClient({ apiUrl: 'https://api.proofable.me' });
const [walletAddress] = await provider.request({ method: 'eth_requestAccounts' });
const signedTimestamp = Date.now();
const body = {
  verifierIds: ['ownership-basic'],
  data: {
    owner: walletAddress,
    content: 'Hello Proofable',
    reference: { type: 'url', id: 'https://example.com' }
  },
  walletAddress,
  signedTimestamp,
};

const standardized = await standardizeVerificationRequest(body, {
  apiUrl: 'https://api.proofable.me',
});

const signature = await signMessage({
  provider,
  walletAddress,
  message: standardized.signerString
});

const res = await client.verify({
  ...body,
  signature,
  options: { privacyLevel: 'private' }
});
```

Live verifier list: `GET /api/v1/verification/verifiers`.

## Gate checks: `gateCheck` vs `checkGate`

| Method | Use when |
| - | - |
| **`gateCheck()`** | **Allow/deny** via `POST /api/v1/proofs/check` for inline requirements (server-enforced). |
| **`checkGate()`** | Preview against proofs you already loaded. Not a substitute for **`gateCheck()`** where trust matters. |

## Link a wallet with Hosted Verify

For typical UX, use Hosted Verify so wallet selection and secondary signing stay on Proofable. Direct mode is for integrations that already control the secondary wallet and provider.

```javascript theme={"system"}
const walletLinkData = await client.createWalletLinkData({
  primaryWalletAddress: '0x0000000000000000000000000000000000000001',
  secondaryWalletAddress: '0x0000000000000000000000000000000000000002',
  wallet: window.ethereum, // EVM provider; pass explicit provider + chain for non-EVM
  relationshipType: 'linked',
  label: 'my-wallet'
});

const res = await client.verify({
  verifier: 'wallet-link',
  data: walletLinkData
});
```

## Gate checks from your servers

```javascript theme={"system"}
import { defineGate } from '@proofable/sdk';

const gate = defineGate([{ verifierId: 'proof-of-human' }]);
const result = await client.gateCheck({
  gate,
  subject: { accountId: user.accountAddress },
});

if (!result.satisfied) {
  throw new Error('Access denied');
}
```

<Warning>
  **`gateCheck`** uses **public** and **unlisted** proofs by default. **Private** proofs count when that user is **signed in**. For strict live checks, create a fresh proof and wait for **verified** status.
</Warning>

`defineGate()` only normalizes requirements locally. It makes no request and persists nothing. `result.proofs` contains zero or more matching proof references; `result.missing` identifies checks that still need verification. A persisted listing and `gateId` remain optional for managed checkout and fulfillment.

## Polling

`pollProofStatus()` backs off on `429` and transient errors.

## Advanced: private proof operations

```javascript theme={"system"}
// EVM examples. For non-EVM, pass an explicit wallet/provider and CAIP-2 chain options.

// Private proof by qHash
const privateData = await client.getPrivateProof(qHash, window.ethereum);

// Private proofs by wallet/DID (requires owner signature)
const privateProofs = await client.getPrivateProofsByWallet(
  'YOUR_WALLET_OR_DID',
  { limit: 50, offset: 0 },
  window.ethereum
);

// Revoke your proof
await client.revokeOwnProof(qHash, window.ethereum);
```

## Catalog and health

List the live verifier ids or fetch the full catalog with metadata and access levels.

```javascript theme={"system"}
const ids = await client.getVerifiers();        // ['ownership-basic', 'token-holding', ...]
const catalog = await client.getVerifierCatalog(); // full metadata + access levels
const ok = await client.isHealthy();             // true / false
```

For private-proof gate access, create a signed private auth payload, then pass it to `gateCheck`:

```javascript theme={"system"}
const privateAuth = await client.createGatePrivateAuth({
  address: 'YOUR_WALLET',
  wallet: window.ethereum,
});
const res = await client.gateCheck({ gateId: 'gate_your-app-name', privateAuth });
```

## Public proofs by wallet

```javascript theme={"system"}
const proofs = await client.getProofsByWallet('0x...', { limit: 50 });
```

## React widgets

[Widgets overview](../widgets/overview).

```javascript theme={"system"}
import { VerifyGate, ProofBadge } from '@proofable/sdk/widgets';
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.