@pacspace-io/sdk
v0.7.0
Published
Official PacSpace Balance API SDK — zero-dependency TypeScript client for neutral usage-based billing verification
Maintainers
Readme
@pacspace-io/sdk
Official TypeScript SDK for the PacSpace Balance API. Zero dependencies, fully typed, built for Node.js 18+.
Install
npm install @pacspace-io/sdkQuick Start
import { PacSpace } from '@pacspace-io/sdk';
const pac = new PacSpace({ apiKey: process.env.PACSPACE_API_KEY! });
// Record a delta
const delta = await pac.balance.emit('cust_123', -42.50, 'usage_charge');
console.log(delta.receiptId); // Store for later verification
// Derive balance
const { computedBalance } = await pac.balance.derive('cust_123');
// Compare against counterparty
const report = await pac.balance.compare('cust_123', {
yours: 95000,
theirs: 98000,
});
// Period-end checkpoint
const checkpoint = await pac.balance.checkpoint('cust_123', {
period: '2026-02',
});API Reference
new PacSpace(config)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | required | Your Balance API key (pk_live_* or pk_test_*) |
| baseUrl | string | auto-routed | API base URL (overrides auto-routing if provided) |
| sandboxUrl | string | Sandbox URL from your PacSpace dashboard | Custom sandbox URL (used for pk_test_* keys) |
| productionUrl | string | https://app.pacspace.io | Custom production URL (used for pk_live_* keys) |
| chainId | number | auto-detected | Default chain ID (296 for sandbox, 295 for production) |
| maxRetries | number | 2 | Max retries on transient errors |
| timeout | number | 30000 | Request timeout in ms |
| webhookSecret | string | — | Webhook signing secret (enables pac.webhooks) |
Balance API
pac.balance.emit(customerId, delta, reason, options?)
Record a credit or debit delta. Returns immediately with QUEUED status.
const delta = await pac.balance.emit('cust_123', -42.50, 'usage_charge', {
referenceId: 'inv_001',
metadata: { plan: 'growth' },
});pac.balance.emitAndWait(customerId, delta, reason, options?)
Record a delta and poll until it reaches a terminal status (VERIFIED or FAILED).
const verified = await pac.balance.emitAndWait('cust_123', -42.50, 'usage', {
timeout: 30_000,
pollInterval: 1000,
});pac.balance.derive(customerId, options?)
Derive a customer's balance from all verified deltas.
const result = await pac.balance.derive('cust_123');
console.log(result.computedBalance);
// With checkpointing for efficiency:
const next = await pac.balance.derive('cust_123', {
startingBalance: result.computedBalance,
startingCheckpoint: result.latestReceiptId!,
});pac.balance.compare(customerId, balances, options?)
Compare balances against neutral truth for dispute resolution.
const report = await pac.balance.compare('cust_123', {
yours: 95000,
theirs: 98000,
});
if (report.matchesYours) {
console.log('Your balance is correct');
}pac.balance.receipt(customerId, options?)
Generate a verifiable receipt. With options.period (YYYY-MM), returns a period-specific receipt with proofRoot, verifyUrl, and verificationReference. Treat verifyUrl as an opaque invoice link; when Shared Record is enabled it may point at a persistent /c/{handle} customer record instead of a proof-root URL.
// Period receipt (shareable proof for invoices)
const receipt = await pac.balance.receipt('cust_123', { period: '2026-02' });
console.log(receipt.proofRoot); // Proof root for verification
console.log(receipt.verifyUrl); // Share this opaque URL on invoices
console.log(receipt.verificationApiUrl); // Machine endpoint for tools and auditors
console.log(receipt.finalBalance);
// Full record receipt (all verified deltas)
const full = await pac.balance.receipt('cust_123');
console.log(full.finalBalance);
console.log(full.verification.itemHashes);pac.verify(proofRoot, options?)
Verify a proof root via the public verification endpoint. No API key required — the proof root is the access key.
const result = await pac.verify(receipt.proofRoot);
if (result.verified) {
console.log(result.verification?.verificationExplorerUrl);
console.log(result.summary?.recordCount);
}Use options.source to select the verification source:
await pac.verify(receipt.proofRoot, { source: 'both' }); // 'db' (default), 'chain', or 'both'Use receipt.verifyUrl for human invoice links. Use
receipt.verificationApiUrl when a tool, agent, or auditor needs to call the
machine verification endpoint. receipt.verificationExplorerUrl is a deprecated
legacy compatibility field and will be retained until a future major version.
pac.balance.checkpoint(customerId?, options?)
Commit a period-end checkpoint. The proof root is recorded on the public verification layer for independent verification.
const checkpoint = await pac.balance.checkpoint('cust_123', {
period: '2026-02',
});
console.log(checkpoint.proofRoot); // Include in your invoiceWebhooks
Verify webhook signatures and process events with type safety.
const pac = new PacSpace({
apiKey: process.env.PACSPACE_API_KEY!,
webhookSecret: process.env.PACSPACE_WEBHOOK_SECRET!,
});
// Express middleware
app.use('/webhooks', express.json({
verify: (req, _res, buf) => { (req as any).rawBody = buf.toString(); }
}));
app.post('/webhooks/pacspace', pac.webhooks!.middleware(), (req, res) => {
const event = (req as any).pacspaceEvent;
if (event.event === 'delta.verified') {
console.log('Delta verified:', event.data.receiptId);
}
res.status(200).json({ received: true });
});
// Manual verification
const event = pac.webhooks!.verify(signature, timestamp, rawBody);Error Handling
All errors extend PacSpaceError with structured data:
import { PacSpace, InsufficientCreditsError, RateLimitError } from '@pacspace-io/sdk';
try {
await pac.balance.emit('cust_123', -1000, 'charge');
} catch (err) {
if (err instanceof InsufficientCreditsError) {
console.log('Buy more credits');
} else if (err instanceof RateLimitError) {
console.log(`Retry after ${err.retryAfter}s`);
}
}| Error Class | Status | When |
|-------------|--------|------|
| ValidationError | 400 | Invalid request data |
| InvalidApiKeyError | 401 | Bad or missing API key |
| InsufficientCreditsError | 402 | Not enough credits |
| NotFoundError | 404 | Resource not found |
| ContractNotDeployedError | 412 | No contract provisioned |
| RateLimitError | 429 | Rate limit exceeded |
| TimeoutError | — | Polling timeout exceeded |
Environment Detection & Auto-Routing
The SDK automatically routes requests to the correct API endpoint based on your API key prefix:
pk_test_*→ Sandbox API (the Sandbox URL shown in your PacSpace dashboard, orsandboxUrlwhen configured)pk_live_*→ Production API (https://app.pacspace.io)
Examples
Sandbox (automatic):
const pac = new PacSpace({ apiKey: 'pk_test_...' });
// Automatically uses the SDK's configured/default sandbox API URL.
// Pass sandboxUrl if your dashboard provides a different Sandbox URL.Production (automatic):
const pac = new PacSpace({ apiKey: 'pk_live_...' });
// Automatically uses production API URLCustom URLs:
// Override sandbox URL
const pac = new PacSpace({
apiKey: 'pk_test_...',
sandboxUrl: 'https://custom-sandbox.example.com',
});
// Override production URL
const pac = new PacSpace({
apiKey: 'pk_live_...',
productionUrl: 'https://custom-production.example.com',
});
// Override with explicit baseUrl (disables auto-routing)
const pac = new PacSpace({
apiKey: 'pk_test_...',
baseUrl: 'https://custom-api.example.com',
});Requirements
- Node.js 18+ (uses native
fetch) - TypeScript 5+ (for full type inference)
