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.

Current

Installation

Install the package in trusted server-side code only.

shell
npm install @meterly/sdk

Create 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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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.

typescript
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;}