SDK guide
JavaScript SDK
Use the JavaScript SDK from trusted Node.js, Next.js API routes, workers, or backend services. Do not ship Meterly API keys to browser or mobile clients.
Installation
Install the package in trusted server-side code only.
npm install @meterly/sdkCreate client
Hosted Meterly Cloud usage only needs an API key. HMAC keys are detected and signed automatically. Use baseUrl only for local or self-hosted development.
import { MeterlyClient } from "@meterly/sdk"; const meterly = new MeterlyClient({ apiKey: process.env.METERLY_API_KEY!});Create customer
Use your product's stable customer identifier. Validation or duplicate conflicts throw MeterlyApiError.
import { MeterlyApiError } from "@meterly/sdk"; try { const customer = await meterly.createCustomer( { customerExternalId: "user_123", creditUnit: "image_credits" }, { idempotencyKey: "customer-user_123" } ); console.log(customer.customerId);} catch (error) { if (error instanceof MeterlyApiError) { console.error({ status: error.status, requestId: error.requestId, method: error.method, path: error.path, message: error.message }); } throw error;}Grant credits
Use stable references and idempotency keys for grants you may retry. externalCustomerId identifies the customer in your product. creditAmount is the positive integer amount to grant. creditUnit is the usage unit being granted. reference is the stable business reference for dedupe. idempotencyKey is the retry-safe key for the same logical grant.
const grant = await meterly.grantCredits( { externalCustomerId: "user_123", creditAmount: 1000, creditUnit: "image_credits", reference: "order_123", description: "Starter image credits" }, { idempotencyKey: "grant-order_123" });Check balance
getBalance(customerId) returns the customer's default credit account. getBalances(customerId) returns all usage-unit balances for the customer. Use getBalances when a customer can hold more than one usage unit.
const balance = await meterly.getBalance(grant.grant.customerId);const balances = await meterly.getBalances(grant.grant.customerId); for (const account of balances.balances) { console.log(account.creditUnit, account.available);}Direct usage
guard reserves, runs the callback, commits on success, and releases if the callback throws. addCredits grants usage units directly with a stable reference and idempotency key. useCredits spends usage units directly with a stable reference and idempotency key. Insufficient credits throw MeterlyInsufficientCreditsError.
import { MeterlyInsufficientCreditsError } from "@meterly/sdk"; try { const result = await meterly.guard( { customerId: grant.grant.customerId, amount: 25, unit: "image_credits", reference: "image-job-42", idempotencyKey: "reserve-image-job-42", ttlSeconds: 300 }, async () => runImageGeneration() ); console.log(result);} catch (error) { if (error instanceof MeterlyInsufficientCreditsError) { return { ok: false, reason: "insufficient_credits" }; } throw error;}Reserve / commit / release
reserveCredits(customerId, amount, options) holds available usage before work starts. commitReservation(reservationId, options) finalizes successful reserved work. releaseReservation(reservationId, options) returns held usage when work fails before commit.
const reservation = await meterly.reserveCredits(customerId, 100, { unit: "gpu_seconds", reference: "render-42", idempotencyKey: "reserve-render-42", ttlSeconds: 900}); try { await renderVideo(); await meterly.commitReservation(reservation.reservationId, { idempotencyKey: "commit-render-42" });} catch (error) { await meterly.releaseReservation(reservation.reservationId, { idempotencyKey: "release-render-42" }); throw error;}