Scrawn LogoScrawn Docs
SDK ReferenceReference & Guides

Pricing DSL

Type-safe pricing expressions with mul, add, tag, inputTokens, and more

Scrawn includes a type-safe DSL for expressing pricing logic. Use the builder functions (mul, add, tag, etc.) to build expressions, then wrap them with biller.expr() wherever a debit value is accepted.

Builder functions

These functions create expression nodes. Combine them with biller.tag(), biller.expr(), and numeric values.

biller.tag("NAME") Events & AI

Reference a price tag managed via dashboard on the backend.

biller.tag("PREMIUM_CALL");
biller.tag("EXTRA_FEE");

Prop

Type

mul(...args) Events & AI

Multiply two or more values.

import { mul } from "@scrawn/core";

mul(biller.tag("PER_TOKEN"), 100);
mul(biller.tag("BASE_RATE"), 2, biller.tag("MULTIPLIER"));

Prop

Type

add(...args) Events & AI

Add two or more values.

import { add } from "@scrawn/core";

add(biller.tag("PREMIUM_CALL"), biller.tag("EXTRA_FEE"));
add(biller.tag("BASE"), 250, biller.expr(100));

Prop

Type

sub(...args) Events & AI

Subtract values.

import { sub } from "@scrawn/core";

sub(biller.tag("GROSS"), biller.tag("DISCOUNT"));

Prop

Type

div(...args) Events & AI

Integer division.

import { div } from "@scrawn/core";

div(biller.tag("TOTAL"), 2);

Prop

Type

inputTokens() AI only

outputTokens() AI only

Token count placeholders that resolve to the actual token count of the current AI call. Only valid inside AITokenUsagePayload objects.

No parameters. Returns a PriceExpr that resolves to the token count at billing time.

import { mul, inputTokens, outputTokens } from "@scrawn/core";

mul(biller.tag("GPT_INPUT_RATE"), inputTokens());
mul(biller.tag("GPT_OUTPUT_RATE"), outputTokens());

Nested expressions

You can nest biller.expr() references inside compound expressions - useful when one persisted expression references another, or when mixing persisted expressions with inline pricing:

import { mul } from "@scrawn/core";

// biller.expr("COMPLEX_FEE") returns an ExprRef that serializes
// to "expr(COMPLEX_FEE)" inside the larger expression string.
biller.expr(mul(biller.tag("EXTRA_FEE"), biller.expr("COMPLEX_FEE")));
// → serialized as: mul(tag(EXTRA_FEE), expr(COMPLEX_FEE))

The backend resolves expr(COMPLEX_FEE) from its persisted expression store at evaluation time.

Usage in Events

Pass a pricing expression where a debit value is accepted.

Tag reference:

import { biller } from "./scrawn/biller";

await biller.basicUsageEventConsumer({
  userId: "user-123",
  debit: biller.tag("PREMIUM_CALL"),
});

Compound expression - wrap with biller.expr():

import { biller } from "./scrawn/biller";
import { mul } from "@scrawn/core";

await biller.basicUsageEventConsumer({
  userId: "user-123",
  debit: biller.expr(mul(biller.tag("PER_CALL"), 3)),
});

For AI token billing, use inputTokens() and outputTokens() as placeholders:

async function* generateUsage() {
  yield {
    userId: "user-123",
    model: "gpt-4",
    inputTokens: 100,
    outputTokens: 50,
    inputDebit: biller.expr(mul(biller.tag("GPT_INPUT_RATE"), inputTokens())),
    outputDebit: biller.expr(mul(biller.tag("GPT_OUTPUT_RATE"), outputTokens())),
  };
}

await biller.aiTokenStreamConsumer(generateUsage());

Persisted Expressions

Store complex expressions on the backend and reference them by name:

await biller.basicUsageEventConsumer({
  userId: "user-123",
  debit: biller.expr("MY_PRICING_EXPR"),
});

Error Handling

Invalid expressions (wrong types, missing operands, etc.) throw PricingExpressionError at runtime. The TypeScript types catch most issues at compile time.