# Define your pricing

Source: https://docs.orbytelabs.com/pricing

Set fixed prices, quantity rates, and callback prices in credits.

Define credit types and feature prices in `orbyte.config.ts`. The property names in `features` become the keys you use with `check` and `track`.

```ts title="orbyte.config.ts"
import { credit, feature, Orbit, usd } from "@orbytelabs/credit";

export const credits = credit("credits", "Credits").worth(usd("0.01"));

export default Orbit({
  features: {
    "api-call": feature("API call", "request", credits(1)),
    inference: feature(
      "AI inference",
      "request",
      usd
        .markup("10%", (data: { costUsd: string }) => data.costUsd)
        .in(credits),
    ),
  },
});
```

Prices must be nonnegative. A price of zero records usage without deducting credits. Use decimal strings for fractional amounts, such as `credits("0.125")`.

## Fixed prices [#fixed-prices]

`credits(1)` charges one credit per unit. `feature("API call", "request", credits(1))` labels that unit as a request. Omitting the unit with `feature("API call", credits(1))` uses `"use"`.

The event quantity defaults to 1. To charge for five requests at once:

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

await track("api-call", "customer_123", {
  id: "batch_123:api-calls",
  quantity: 5,
});
```

This deducts five credits. The deployed catalog supplies the fixed price. The generated `_orbyte.ts` types the name without loading your config. You can still pass a feature object from your config.

## Prices per quantity [#prices-per-quantity]

Use `.per(amount, quantity)` to express a proportional rate. For example, this separate configuration prices 1,000 input units at 10 credits:

```ts title="orbyte.config.ts"
import { credit, feature, Orbit } from "@orbytelabs/credit";

export const credits = credit("credits", "Credits");

export default Orbit({
  features: {
    "input-units": feature("Input units", "unit", credits.per(10, 1_000)),
  },
});
```

An event with `quantity: 250` costs 2.5 credits. This is a proportional rate, so the customer does not pay for a whole block of 1,000.

The API calculates `ceil(costNanocredits × quantity / quantityPerUnit)`. It rounds up once for the event, to the next nanocredit. Splitting one quantity across several events can therefore produce a different total at nanocredit precision.

Quantities must be positive safe integers. For [period features](/usage-periods), each event must have quantity 1, even when the configured price uses `.per(...)`.

## Prices from event data [#prices-from-event-data]

Use a callback when the amount depends on data available to your server. With the `inference` feature from the first config on this page:

```ts
import { check, track } from "@orbytelabs/credit";
import config from "./orbyte.config";

const data = { costUsd: "0.02" };
const access = await check(config.features.inference, "customer_123", { data });

if (!access.allowed) throw new Error("Not enough credits.");

// Perform the work priced by this data.
await track(config.features.inference, "customer_123", {
  id: "request_123:inference",
  data,
});
```

A provider cost of $0.02 plus 10% is $0.022. At $0.01 per credit, this event costs 2.2 credits.

Pass the config feature object so the SDK has the callback and its data type. A feature key alone cannot evaluate callback pricing. Callbacks return the total event charge and require quantity 1, which is the default.

The SDK evaluates the callback once before each `check` or `track` request, then sends the resolved nanocredit amount. It does not send the callback or raw `data` to the API. Automatic transport retries reuse that resolved amount.

Keep the callback and its input on your trusted server. Validate customer input before using it to calculate a charge. If the final cost is only known after work finishes, a check using an estimate cannot guarantee that the wallet will cover the final charge.

## Markup and percentage [#markup-and-percentage]

Both helpers require a callback selecting the base amount:

| Price                                                               | Charge when the base is 10 credits |
| ------------------------------------------------------------------- | ---------------------------------- |
| `credits((data: { amount: string }) => data.amount)`                | 10 credits                         |
| `credits.markup("10%", (data: { amount: string }) => data.amount)`  | 11 credits, including the base     |
| `credits.percent("10%", (data: { amount: string }) => data.amount)` | 1 credit, only the percentage      |

The same helpers work with `usd`. A callback can calculate a price from several inputs before returning a decimal amount.

## Convert dollar costs to credits [#convert-dollar-costs-to-credits]

Declare a value with `.worth(usd(...))`, then convert a USD price with `.in(credits)`:

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

const credits = credit("credits", "Credits").worth(usd("0.01"));
const imagePrice = usd("0.04").in(credits); // Four credits.
```

The conversion uses the declared value in your server code. It does not exchange balances or set a customer purchase price. A plain `usd("0.04")` price uses a separate USD-denominated balance; `.in(credits)` makes it spend your credit balance instead.

Only USD prices support `.in(...)`. The destination credit must have a positive fixed USD value.

## Precision and limits [#precision-and-limits]

One credit equals `1,000,000,000` nanocredits. Fixed credit amounts and top-ups support up to nine decimal places. Callback charges and proportional rates round up once when the final event charge is calculated.

API amounts are integer strings, such as `"2500000000"` for 2.5 credits. The maximum balance or individual charge is `"9223372036854775807"` nanocredits. Avoid converting these strings to JavaScript `number` for arithmetic; use `BigInt`.

## Apply changes [#apply-changes]

Deploy catalog changes with the [SDK CLI](/sdk). Fixed-price changes affect new events after deployment. An identical retry of an existing event returns its original transaction and original charge.

Callback functions remain in your application code. Deploy the application when changing their calculation, and sync the catalog when changing the feature's credit, pricing mode, labels, or period. Callback features store `pricingMode: "callback"` and a zero catalog rate because each request supplies its resolved charge.

After a feature's first period charge, its deduplication period is locked. Use a new feature key to change that policy. See [usage periods](/usage-periods#changing-period-settings).