Costs
Cost tracking in Esy separates estimates from provider-confirmed billing. Every cost has a source, a calculation method, and a confidence state — at the step, run, workflow, project, and organization level.
Runtime responses include estimated costs immediately so operators can plan budgets, but provider-reported and reconciled costs are tracked separately. Final billing comes from provider truth, not Esy estimates.
Cost vs. price
Two axes are easy to conflate, so Esy keeps them strictly separate. Cost is what it costs Esy to execute a step — metered provider and resource spend. Price is what a customer is charged for a template. Everything on this page is about cost; price is a separate concern. A template can be priced at $0 to a customer and still incur real cost to run, so cost docs never describe a step as “free” — that word only ever describes price.
Metered and unmetered steps
Every runtime step is one of two kinds, on the cost axis:
| Step kind | Meaning |
|---|---|
| metered | Consumes a cost-bearing resource — an LLM call, image generation, a tool call, an agent loop, or a sub-workflow. A metered step carries a rate (its per-unit cost). |
| unmetered | Consumes no cost-bearing resource — a local string transform, a passthrough, local computation. It contributes nothing to run cost. This is not “free”; it simply incurs no cost because there is no metered resource behind it. |
A metered step’s rate is resolved from the registry. A rate is resolved when the registry has a known per-unit cost for the step’s model or tool — and that known value may legitimately be 0. A rate is unresolved when the registry cannot price it, which is the case that silently reads as $0 while real spend still happens.
Estimability (the publish guard)
A template is estimable when every metered step resolves to a known rate. Estimability is enforced at publish time: a template cannot become active if any metered step has an unresolved rate. The invariant is about knowing the cost, not about the total being non-zero.
| Case | Publish |
|---|---|
| Unmetered step (no cost-bearing resource) | Allowed — contributes nothing to cost. |
Metered step with a resolved rate (even 0) | Allowed — the cost is known. |
| Metered step with an unresolved rate | Blocked — the cost is unknown and would silently read as $0. |
A template made entirely of unmetered steps genuinely costs nothing and publishes fine. A template whose metered step references a model or tool the registry can’t price is blocked — not because it’s expensive, but because Esy refuses to publish a cost it cannot account for.
Cost states
| State | Meaning |
|---|---|
estimated | Computed from Esy pricing snapshots and captured request settings. |
provider_reported | Reported directly by a provider response or usage API. |
reconciled | Matched against provider billing or usage records. |
disputed | Internal estimate and provider-reported cost diverge beyond tolerance. |
Provider cost ledger
Every provider interaction creates a ledger entry. Run telemetry rolls up these entries into a run-level totalCosts object with the same status semantics; project and organization burn roll up from runs.
{
"provider": "fal.ai",
"providerOperation": "background.remove",
"model": "birefnet-light",
"quantity": 1,
"unit": "request",
"unitPriceUsd": 0.0003,
"estimatedCostUsd": 0.0003,
"actualCostUsd": 0.0003,
"status": "provider_reported",
"pricingSource": "pricing_snapshot",
"pricingVersion": "2026-05-16",
"reconciledAt": "2026-05-16T22:14:43.902Z",
"sourceUrl": "https://fal.ai/models/fal-ai/birefnet/v2/api"
}When a run composes a sub-workflow, each child run keeps its own ledger entries; the parent run’s totalCosts includes the child run totals, so a composed workflow reports the full cost of producing its artifact.
Each ledger entry is immutable and freezes the price that was applied: its unitPriceUsd and pricingVersion are snapshotted at write time. When a provider price changes later, existing entries keep the price they were charged at — nothing recomputes them.
Vendor vs. model: direct providers and gateways
Every ledger entry records two things that are easy to conflate: provider is the vendor that invoices you, and model is what actually ran. For direct providers — OpenAI, Anthropic — these coincide: you call them, they bill you. For a gateway like fal.ai they diverge: fal.ai is an API that routes to many underlying models (today birefnet-light for background removal; other hosted models later), so the vendor on the invoice is fal.ai while the model that ran is something else.
Because both fields live on every entry, the two questions stay separate. Roll up spend by model to see what ran — the honest answer to “what am I paying for”. Roll up by vendor to reconcile against the invoices you actually receive. A gateway only ever shows up as a vendor; it is never the answer to “which capability did I use.”
If fal.ai later proxies a model such as Gemini, the same fields still carry the truth: provider stays the billing vendor (fal.ai) and model names the real model. A dedicated route/gateway field can annotate “via fal.ai” if that level of provenance becomes useful — the cost model does not have to change to support it.
Pricing versions are effective dates
pricingVersion is an ISO date (YYYY-MM-DD), not an opaque label: it is the day a price snapshot took effect. Because every ledger entry copies it, each entry answers “what price was in effect, and from when” on its own — the entry’s createdAt tells you when the cost was incurred, and its pricingVersion tells you which price schedule applied.
For spend reporting and point-in-time price provenance, the per-entry snapshot plus its createdAt is sufficient — no separate dated rate-card table is required. A standalone pricing history is only needed for price-trend or drift analysis, which Esy adds when that need is real.
Pricing sources
| Provider | Source of truth |
|---|---|
| fal.ai | Provider usage API for actuals; pricing snapshot for estimates while a run is in-flight. |
| openai | Documented pricing snapshot per model and quality, keyed to a dated pricingVersion. |
| storage (R2) | Cloudflare published per-operation rate (Class A writes); reconciled at write time, with monthly usage export as a cross-check. |