# Wallets and top-ups

Source: https://docs.orbytelabs.com/wallets-and-topups

Choose a paying identity, add credits, and read its balances.

A wallet belongs to one external identity within an application. Use your own stable customer or organization ID so the same customer always reaches the same balance.

Direct top-ups add credits through deposits. The `topup` function does not collect money. Call it from your server after your own payment integration confirms payment, or when your application grants credits. To collect payment through Credit's connected Stripe account and fund the wallet after confirmation, use [paid top-up checkout](/subscriptions).

## Choose the paying identity [#choose-the-paying-identity]

For individual accounts, use your user ID. For shared company balances, use the organization ID on every check, event, and deposit.

```ts
const identity = "organization_456";
```

Identities are trimmed and must contain 1 to 200 characters. The values `.` and `..` cannot provision a new wallet. Avoid email addresses if customers can change them, since a changed identity points to a different wallet.

The same identity in Development and Production has separate wallets. The API key selects the application.

## Add credits [#add-credits]

The credit type must already exist in the application's catalog. This example imports the credit helper from the [quickstart](/quickstart):

```ts
import { topup } from "@orbytelabs/credit";
import { credits } from "./orbyte.config";

const result = await topup("customer_123", credits(100), {
  idempotencyKey: "payment_123:credits",
});
```

The first call creates a deposit, adds 100 credits, and returns `duplicate: false`. It creates the wallet if needed. An identical retry returns the original deposit and `duplicate: true` without adding credits again.

Use a stable key tied to the payment or grant. For a manually scheduled allowance, your application could use `customer_123:allowance:2026-10`. Your scheduler must call `topup` when that manual allowance is due; usage counting periods do not schedule deposits. For Stripe subscriptions, configure [recurring plan credit grants](/subscriptions) instead. Credit fulfills those once per confirmed creation, renewal, or full-period migration invoice; do not also deposit them from your own payment handler.

Changing the identity, credit type, or amount under an existing deposit key returns `409 idempotency_conflict`. Deposit keys are unique within the application and separate from usage event IDs.

If you omit `idempotencyKey`, the SDK creates a UUID for that call and reuses it on automatic transport retries. A separate call creates a new key and adds credits again. Always supply a stable key when handling repeated payment notifications or retryable jobs.

## Amounts and credit types [#amounts-and-credit-types]

Pass a fixed price such as `credits(100)` or `credits("2.5")`. A top-up must be positive and support exact conversion to nanocredits, with at most nine decimal places. Callback prices cannot be used as deposit amounts.

The amount carries its credit type. If a wallet holds image credits and compute credits, topping up one balance leaves the other balance unchanged. Credit does not automatically convert between them.

A wallet can hold up to 100 credit balances. Each balance is limited to `"9223372036854775807"` nanocredits. A deposit that would exceed that amount is rejected without changing the balance.

## Read a wallet [#read-a-wallet]

```ts
import { getWallet } from "@orbytelabs/credit";

const wallet = await getWallet("customer_123");
const amount =
  wallet?.balances.find((balance) => balance.creditKey === "credits")
    ?.amountNanocredits ?? "0";

const nanocredits = BigInt(amount);
```

`getWallet` returns `null` when no wallet exists. It never creates one. An existing wallet contains its external `identity`, its billing-period settings, and a list of stored balances. A missing credit entry means zero. A balance can include `debtNanocredits` after a Stripe refund or dispute reverses credits already spent. New deposits repay the debt before adding spendable credits.

Amounts are decimal strings in nanocredits. Use `BigInt` for exact arithmetic, since the supported range exceeds JavaScript's safe integer range.

`check`, `track`, and `topup` accept either an identity string or a wallet object containing `identity`. Pass the external identity, not the internal wallet `id`.

## Read deposit history [#read-deposit-history]

Use the client when you need administrative operations or lists:

```ts
import { OrbyteClient } from "@orbytelabs/credit";

const client = new OrbyteClient();
const deposits = await client.listDeposits({
  identity: "customer_123",
  creditKey: "credits",
});
```

This returns deposits newest first and follows all pages. Omit `creditKey` to include every credit type for the wallet. The wallet must exist.

## Permissions [#permissions]

Funding requires `deposits:write`. Reading a wallet requires `wallets:read`; listing deposits requires `deposits:read`. A key with only `events:ingest` cannot fund wallets.

Keep funding credentials on your server. See [authentication](/authentication) for setup and the [API reference](/api) for direct HTTP operations.