Sign In

@clocknext/sdk

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@clocknext/sdk

Official ClockNext SDK — record usage signals and manage customers from your backend.

npmnpm
Version
0.1.0
Version published
Weekly downloads
14
-91.81%
Maintainers
1
Weekly downloads
 
Created
Source

@clocknext/sdk

Official ClockNext SDK for Node/TypeScript. Record usage signals and manage customers from your backend.

Server-side only. Your cnk_… API key has org-wide write access — never ship it to a browser. For embedding the customer portal in a browser, mint a short-lived token with cnk.portal.createToken().

Install

npm install @clocknext/sdk

Requires Node ≥ 18 (native fetch). Zero runtime dependencies.

Quick start

import { ClockNext } from "@clocknext/sdk";

const cnk = new ClockNext({ apiKey: process.env.CLOCKNEXT_API_KEY! });

await cnk.signals.credit({
  customerId: "cus_123",
  model: "llama-3.3-70b-versatile", // catalog model id (OrgModel.modelId)
  key: "api_credit",                // the credit's stable key
  member: "alice@acme.com",
  tokens: { input: 1200, output: 340, cache: 0 },
  custom: { feature: "summarize" },
});

// Before a serverless function returns (or on shutdown):
await cnk.flush();

Recording signals

There are three signal types, matching ClockNext's billable primitives. Each has a typed helper that enforces the right required fields at compile time.

model is always the catalog model id enabled in your org (matched case-insensitively against OrgModel.modelId). Credits and outcomes are referenced by their stable key (assigned when you create the credit / outcome step), not by display name.

// Credit — metered against a credit (by key); charged at the model's real cost + your margin
await cnk.signals.credit({ customerId, model: "llama-3.3-70b-versatile", key: "api_credit", tokens: { input: 1200, output: 340 } });

// Wallet — debits the customer's USD wallet at model cost
await cnk.signals.wallet({ customerId, model: "openai/gpt-oss-120b", tokens: { input: 800, output: 5000 } });

// Outcome — advances one step of a workflow (key = the OutcomeStep's key)
await cnk.signals.outcome({ customerId, model: "llama-3.3-70b-versatile", key: "resolved_ticket.triage" });

Usage /api/v1/usage responses use the envelope { statusCode, statusDetail, result }; customer endpoints currently use { ok, … }. The SDK unwraps both for you.

Async vs sync

By default the client is in async mode: track() buffers the signal and returns immediately ({ queued: true }), and a background flusher sends batches on a size/interval trigger. This keeps billing telemetry off your LLM hot path.

To get the computed result back (and any real-time allowance rejection), either:

// per call
const res = await cnk.signals.credit({ … }, { wait: true });
console.log(res.usageLog);

// or globally
const cnk = new ClockNext({ apiKey, mode: "sync" });

In async mode, server-side rejections (e.g. insufficient allowance, 422) surface via the onError hook or flush(), not from the track() call.

Reliability

  • Idempotency: every signal carries an idempotency key (auto-generated once, reused across retries) so a transport retry can't double-count. Pass your own idempotencyKey for deterministic, cross-process dedup. (Server-side dedup enforcement is rolling out — see the SDK plan.)
  • Retries: transient failures (network, timeout, 408/409/429/5xx) are retried with exponential backoff + jitter, honoring Retry-After. Deterministic 4xx (400/401/404/422) are never retried.
  • Flushing: call await cnk.flush() before a serverless return, and await cnk.close() on graceful shutdown to drain the buffer.

Customers

const c   = await cnk.customers.create({ name: "Acme", email: "ops@acme.com" });
const one = await cnk.customers.get(c.id);
const page = await cnk.customers.list({ limit: 50, q: "ac" });
await cnk.customers.update(c.id, { notes: "VIP" });
await cnk.customers.delete(c.id);

// iterate every customer (auto-follows the cursor)
for await (const cust of cnk.customers.iterate()) { /* … */ }

// insights
await cnk.customers.usage(c.id, { limit: 50 });
await cnk.customers.balances(c.id);
await cnk.customers.plan(c.id);
await cnk.customers.revenue(c.id, { range: "30d" });

Customer portal token

const { token, expiresAt } = await cnk.portal.createToken({ customerId: "cus_123", ttlSeconds: 600 });
// render <iframe src={`https://app.clocknext.com/portal/embed?token=${token}`} />

Errors

All failures throw typed errors you can branch on:

import { AllowanceError, AuthError, RateLimitError } from "@clocknext/sdk";

try {
  await cnk.signals.credit({ … }, { wait: true });
} catch (err) {
  if (err instanceof AllowanceError) {/* upsell */}
  else if (err instanceof AuthError) {/* bad key */}
  else if (err instanceof RateLimitError) {/* back off */}
}

ValidationError (400) · AuthError (401) · NotFoundError (404) · PlanError (422) · AllowanceError (422) · ConflictError (409) · RateLimitError (429) · ServerError (5xx) · NetworkError — all extend ClockNextError (.status, .retryable, .idempotencyKey).

Configuration

new ClockNext({
  apiKey: "cnk_…",                 // required
  baseUrl: "https://app.clocknext.com",
  mode: "async",                    // "async" | "sync"
  timeoutMs: 10_000,
  batch: { maxSize: 20, maxIntervalMs: 2000, maxConcurrency: 5, maxQueueSize: 10_000 },
  retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 10_000 },
  onError: (err, signal) => {},
  onFlush: (count) => {},
  onRetry: ({ attempt, delayMs }) => {},
  onDrop: (signal, reason) => {},
});

License

MIT

Keywords

clocknext

FAQs

Package last updated on 23 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts