@unitpay/node
v1.31.9
Published
UnitPay server-side event ingestion — auto-batching, graceful shutdown, zero dependencies
Readme
@unitpay/node
Server-side event ingestion SDK for UnitPay. Auto-batches usage events with graceful shutdown support. Track usage, deduct credits automatically via FIFO, and check entitlements.
Installation
npm install @unitpay/node
# or
bun add @unitpay/nodeQuick Start
import { UnitPayIngestion } from '@unitpay/node';
const unitpay = new UnitPayIngestion({
apiKey: 'upay_sk_...',
});
// Track a usage event (deducts credits automatically)
unitpay.track({
customerId: 'cus_...',
eventName: 'ai_generation',
quantity: 1, // defaults to 1 if omitted
properties: { model: 'gpt-4', tokens: 1500 },
});
// Track with custom quantity (e.g., 5 units at 2 credits/unit = 10 credits deducted)
unitpay.track({
customerId: 'cus_...',
eventName: 'ai_generation',
quantity: 5,
});
// Graceful shutdown (flushes remaining events)
process.on('SIGTERM', async () => {
await unitpay.shutdown();
process.exit(0);
});How It Works
Events are buffered in memory and flushed in batches:
track()adds events to an in-memory buffer- A flush timer fires every
flushIntervalMs(default: 1s) - If the buffer hits
maxBatchSize(default: 100), it flushes immediately - Each event auto-generates an
idempotencyKeyandtimestampif not provided - On flush, credits are deducted via FIFO from the customer's credit account
- On
shutdown(), all remaining events are flushed before resolving
Credit Deduction
Credits are deducted at track time — no separate deduct call. The cost per event is determined by the plan charge's rate (e.g., rate=2 means 2 credits per unit).
unitpay.track({
customerId: 'cus_...',
eventName: 'ai_generation',
quantity: 3, // 3 units x 2 credits/unit = 6 credits deducted
});
const result = await unitpay.flush();
console.log(result);
// {
// accepted: 1,
// rejected: 0,
// rejections: [],
// creditsDeducted: 6,
// creditBalance: 116
// }Entitlement Check
Check feature access server-side before performing expensive operations:
const result = await unitpay.check({
customerId: 'cus_...',
featureSlug: 'ai-generation',
requestedUsage: 5, // optional: check if customer has enough credits
});
if (result.access) {
// proceed with the operation
unitpay.track({ customerId: 'cus_...', eventName: 'ai_generation', quantity: 5 });
} else {
console.log(`Denied: ${result.deniedReason}`);
}Configuration
const unitpay = new UnitPayIngestion({
apiKey: 'upay_sk_...', // required — secret API key
baseUrl: 'https://api-beta.useunitpay.com/v1', // default
autoBatch: true, // buffer + flush on interval (default: true)
flushIntervalMs: 1_000, // 1s default
maxBatchSize: 100, // flush when buffer reaches this size
maxBufferSize: 10_000, // drop oldest events when exceeded
maxRetries: 3, // retries on 5xx/network errors
timeout: 30_000, // request timeout in ms
onFlushError: (error, events) => { // handle flush failures
console.error('Flush failed:', error, `${events.length} events dropped`);
},
});Track Parameters
unitpay.track({
eventName: 'ai_generation', // required — must match a billable metric
customerId: 'cus_...', // required — the customer being billed
quantity: 1, // optional — defaults to 1
properties: { model: 'gpt-4' }, // optional — custom metadata
idempotencyKey: 'req_abc123', // optional — auto-generated if omitted
timestamp: '2026-03-23T08:00:00Z', // optional — auto-set to now if omitted
});Reliability
- Auto-retry: 5xx and network errors are retried with exponential backoff
- Idempotency: Every event gets a unique idempotency key — safe to retry
- No duplicate flushes: Concurrent flush calls are deduplicated via mutex
- Bounded memory:
maxBufferSizeprevents OOM during outages (drops oldest events) - 4xx = permanent failure: Bad requests are not retried (logged via
onFlushError) - Graceful shutdown:
shutdown()flushes remaining events and stops the timer - Buffer recovery: On 5xx after retries, events are pushed back into the buffer
License
MIT
