TypeScript SDK for the Privara — confidential stablecoin payments on compliant rails.
pnpm add @privara/sdknpm install @privara/sdkyarn add @privara/sdkRequirements: Node.js 18+. Zero runtime dependencies.
import {Privara} from '@privara/sdk';
const privara = new Privara({
clientId: 'pvr_...',
clientSecret: 'pvrs_...',
});
const balance = await privara.balance.get();
console.log(balance.balance, balance.currency);The SDK automatically obtains and refreshes OAuth tokens via the client_credentials grant. Concurrent requests share a
single token refresh — no duplicate auth calls.
If you already have an access token (e.g. from a different auth flow), pass it directly:
const privara = new Privara({
accessToken: 'eyJ...',
});All API resources follow a consistent pattern:
await privara.invoices.create({...}); // POST
await privara.invoices.get('inv_123'); // GET by ID
await privara.invoices.list({limit: 10}); // GET listconst invoice = await privara.invoices.create({
from: 'alice@example.com',
due_date: '2025-12-31',
reference: 'Order #1234',
amount: 100,
currency: {type: 'crypto', code: 'USDC'},
wallet_id: 'wallet_uuid',
});
// Get invoice (public, no auth required)
const inv = await privara.invoices.get(invoice.public_id);
// List with filters
const page = await privara.invoices.list({
limit: 20,
status: 'paid',
sort_order: 'desc',
});const withdrawal = await privara.withdrawals.create({
invoice_ids: [1, 2, 3],
destination_chain: 'BASE',
recipient_address: '0x...',
wallet_id: 'wallet_uuid',
});
// After redeem completes, initiate bridge
const bridge = await privara.withdrawals.createBridgeChallenge(withdrawal.public_id);const {balance, currency, paid_invoices_count} = await privara.balance.get();await privara.transactions.report({
tx_hash: '0x...',
entity_type: 'invoice',
entity_id: 'inv_uuid',
});The SDK provides a separate entry point for on-chain invoice operations via ZeroDev account abstraction. See the full guide: docs/blockchain.md
Quick start:
import {Privara} from '@privara/sdk';
import {PrivaraBlockchain} from '@privara/sdk/blockchain';
const privara = new Privara({clientId: 'pvr_...', clientSecret: 'pvrs_...'});
const blockchain = new PrivaraBlockchain(privara, {serializedSessionKey: '...'});
const result = await blockchain.invoices.createAndSubmit({
from: 'alice@example.com',
due_date: '2025-12-31',
reference: 'Order #1234',
amount: 100,
currency: {type: 'crypto', code: 'USDC'},
});Requires additional peer dependencies: viem, @zerodev/sdk, @zerodev/permissions.
List endpoints that return paginated data support automatic iteration via listAutoPaginate(). This returns an
AsyncIterable that fetches pages on demand:
for await (const invoice of privara.invoices.listAutoPaginate({status: 'paid'})) {
console.log(invoice.public_id, invoice.amount);
}Invoices use offset-based pagination, withdrawals use cursor-based — the SDK handles both transparently.
To collect all results into an array:
import {toArray} from '@privara/sdk';
const allInvoices = await toArray(privara.invoices.listAutoPaginate());
const allWithdrawals = await toArray(privara.withdrawals.listAutoPaginate());All API errors extend PrivaraApiError and follow RFC 7807 Problem
JSON:
import {Privara, PrivaraApiError, PrivaraValidationError} from '@privara/sdk';
try {
await privara.invoices.create({ /* ... */});
} catch (error) {
if (error instanceof PrivaraValidationError) {
// 422 — check field-level errors
console.log(error.invalidParams);
// [{ name: 'amount', reason: 'must be positive' }]
} else if (error instanceof PrivaraApiError) {
console.log(error.status); // HTTP status
console.log(error.title); // Short description
console.log(error.detail); // Detailed message
console.log(error.type); // Error type URI
}
}| Class | Status | When |
|---|---|---|
PrivaraAuthenticationError |
401 | Invalid or expired credentials |
PrivaraNotFoundError |
404 | Resource doesn't exist |
PrivaraConflictError |
409 | Idempotency conflict |
PrivaraValidationError |
422 | Invalid request parameters |
PrivaraRateLimitError |
429 | Too many requests |
PrivaraConnectionError |
— | Network failure or timeout |
PrivaraRateLimitError exposes retryAfter (seconds) from the Retry-After header when available.
The SDK automatically retries failed requests with exponential backoff and jitter:
- Retryable: 429, 500, 502, 503, 504, and network errors
- Default: 3 retries, 500ms initial delay, 8s max delay
- 429 responses respect the
Retry-Afterheader
const privara = new Privara({
clientId: 'pvr_...',
clientSecret: 'pvrs_...',
retry: {
maxRetries: 5,
initialDelayMs: 1000,
maxDelayMs: 16000,
},
});Set maxRetries: 0 to disable retries.
POST endpoints that create resources (invoices.create, withdrawals.create) automatically generate an
X-Idempotency-Key header (UUID v4). This ensures safe retries — the server returns the same response for duplicate
requests.
To use your own key:
await privara.invoices.create(
{ /* params */},
{idempotencyKey: 'my-unique-key'},
);Default timeout is 30 seconds. Configure globally or per-request:
// Global
const privara = new Privara({
clientId: 'pvr_...',
clientSecret: 'pvrs_...',
timeout: 10_000, // 10s
});
// Per-request via AbortSignal
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
await privara.balance.get({signal: controller.signal});Add request or response interceptors for logging, tracing, or custom headers:
privara.addRequestInterceptor((req) => {
console.log(`${req.method} ${req.path}`);
return req;
});
privara.addResponseInterceptor((res) => {
console.log(`${res.status} ${res.statusText}`);
return res;
});The SDK is written in TypeScript and exports all request/response types:
import type {
Invoice,
CreateInvoiceParams,
Withdrawal,
Balance,
OffsetPage,
CursorPage,
} from '@privara/sdk';All types use snake_case matching the wire format — no runtime transformation.
| Option | Type | Default | Description |
|---|---|---|---|
clientId |
string |
— | OAuth client ID (pvr_...) |
clientSecret |
string |
— | OAuth client secret (pvrs_...) |
accessToken |
string |
— | Pre-existing Bearer token |
baseUrl |
string |
https://api.privara.io |
API base URL |
timeout |
number |
30000 |
Request timeout in ms |
retry.maxRetries |
number |
3 |
Max retry attempts |
retry.initialDelayMs |
number |
500 |
Initial retry delay |
retry.maxDelayMs |
number |
8000 |
Max retry delay |
Provide either clientId/clientSecret (recommended) or accessToken.