> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jaza.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Consume credits

> Debit a wallet with jaza.consume() using featureCode and an idempotency key.

Call `jaza.consume()` from your **backend** after a billable action succeeds. Pass a dashboard `featureCode` (or raw `credits`) and an **idempotency key** so retries never double-charge.

## Prerequisites

* Features configured in the [dashboard](/guides/dashboard-setup)
* A customer with a wallet (`cus_…`)
* `@jazadev/node` with `secretKey` + `publicKey`

## Call consume

```ts theme={null}
import { Jaza } from '@jazadev/node';

const jaza = new Jaza({
  secretKey: process.env.JAZA_SECRET_KEY!,
  publicKey: process.env.JAZA_PUBLIC_KEY!,
});

const result = await jaza.consume({
  customerId: user.jazaCustomerId,
  featureCode: 'SEND_MESSAGE',
  idempotencyKey: `msg_${messageId}`,
});
```

You can pass `credits` instead of `featureCode` when you need an explicit debit amount.

## Charge after success

Prefer deducting **after** the feature succeeds so failed work is not billed:

```ts theme={null}
const response = await runBillableFeature(...);
await jaza.consume({
  customerId,
  featureCode: 'AI_PROMPT',
  idempotencyKey: `ai_${jobId}`,
});
return response;
```

## Errors

The Node SDK throws `JazaError` with `statusCode`, `code`, and `raw`. Handle insufficient balance by prompting a top-up ([Top up](/guides/top-up)). See [Errors](/reference/errors) for the envelope shape.

## Idempotency

Use a key that is **stable for the same business event** (customer + feature + your action id). Do not use a fresh timestamp on every retry, or a timed-out success can deduct twice.

```ts theme={null}
const idempotencyKey = `consume-${customerId}-SEND_MESSAGE-${messageId}`;
```

## Next steps

* [Node SDK](/sdks/node) · [Ledger](/concepts/ledger) · [Features and bundles](/concepts/features-and-bundles)
