Action Pricing
How a connector action gets a commercial price — the two-cost invariant, tenant override precedence, the charge-policy matrix, and why a resolved price is stamped before any provider call.
An action's quota cost and its price are different things, and Ductor keeps them apart on purpose. Quota cost is capacity: how much of a provider's rate limit one call consumes. Price is money: what the tenant is charged for that call. They live in different tables, use different types, and never convert into each other.
This page describes the pricing engine that resolves what an action costs and decides whether an execution bills.
Ships as an opt-in surface
Set connector_pricing.enabled: true to construct the price resolver and wire
it into every connector action executor. While disabled — the default — no
resolver is built, no pricing query is issued, and every action resolves as
unpriced, so nothing bills.
Enabling it makes actions billable only for tenants with price rows on file. An action with no price row stays unpriced, which is not the same as free. If the pricing store is unreachable, executions are refused rather than run unpriced, and the reason is recorded on the execution log.
The two-cost invariant
One call consumes two independent budgets, and confusing them is the failure this design exists to prevent.
- Quota cost is capacity
CostUnitson the action's quota declaration — how much of the provider's rate limit this call spends. An integer count of calls, never money.- Price is money
unit_price_microson the price row — integer micros of an ISO 4217 currency. No floats anywhere in the path.- They never convert
- Separate tables, separate types, and lexically distinct metadata keys (
pricing_*versusquota_*), so no call site can quietly read one as the other.
A priced action with no quota declaration still spends one quota unit; a zero-price action still consumes its declared capacity. Neither axis infers the other.
Resolving a price
A price is resolved before any provider I/O and stamped onto the execution. That ordering is the point: the terminal usage event bills the price that was in force when the call started, not whatever is current when it finishes.
Notice how many paths end without billing. Denied and replayed executions are never billable regardless of what the policy says, and a store failure surfaces as an error rather than silently resolving to unpriced — a transient outage can never turn paid actions into free ones.
Precedence
Resolution picks one row by walking three tie-breaks in order:
- Tenant override beats platform default. A row scoped to the tenant wins over the platform-wide listing.
- Environment-specific beats tenant-wide. Within tenant scope, a row naming an environment beats one that leaves it blank.
- Highest version wins. Among rows still in force, the greatest
pricing_versionis selected.
Effective windows make versions abut cleanly: effective_from is inclusive and
effective_to is exclusive, so consecutive versions meet without overlapping and
without leaving a gap.
Unpriced is not free
When no active row exists, the action resolves as unpriced — its commercial
status is unknown. That is not the same as free: only the free tier is free.
Catalog surfaces must render this as "Unpriced", never as $0.
The charge-policy matrix
A price declares what happens for each terminal outcome. Defaults bill on success and on async acceptance, and stay free everywhere else.
| Terminal outcome | Default decision | Configurable |
|---|---|---|
success | charge | yes |
async_accept | charge | yes |
provider_failure | free | yes |
timeout | free | yes |
denied | free | no — never billable |
replayed | free | no — never billable |
Notice how many routes end without a charge — that asymmetry is the design.
Two safeguards are deliberate. denied and replayed are hard-coded unbillable
regardless of policy, so a throttled, circuit-broken, auth-rejected, or replayed
mutation can never produce a charge. And an unrecognized policy value resolves
to free rather than to charge — an unknown policy can under-bill, but it can
never over-bill.
Tiers also gate billing: only tiers that charge per unit produce a non-zero
amount. The free and included tiers still emit a zero-cost usage event so
volume stays observable.
Why the price is stamped, not looked up later
The resolved price and its pricing_version are copied onto the execution and
carried into the terminal usage event. Billing therefore reads the price that
applied at execution time.
This is what makes historical bills immune to later price changes. Re-pricing an action tomorrow cannot restate what yesterday's calls cost, because yesterday's usage events carry yesterday's price and version — nothing recomputes from the current table.
Environment overrides read differently on the catalog
A price row can be scoped to an environment, and execution honours that scope: the executor resolves with the request's real environment, so the charge is the environment-specific one.
The catalog does not. A catalog read has no environment on its request context, so it resolves tenant-wide rows only. A tenant that has set an environment override therefore sees the tenant-wide price in the catalog while execution bills the environment-specific one. Treat catalog price as the tenant-wide default rather than a per-environment quote until an environment reaches the read path.
What is not built yet
- No seller payout
- A capped agent draw now settles to the balanced ledger as showback (tenant wallet debited, platform credited), but an action price carries no payee, so a settled purchase credits the platform, never a marketplace seller.
- No environment on catalog reads
- Catalog projections resolve tenant-wide rows only, so an environment override is invisible there.
- No deferred async billing
- Async acceptance bills or does not; deferring the decision until an async job completes is not implemented.
- No RFQ price basis
rfqexists as a basis value but nothing quotes per engagement; resolution returns fixed, metered, or unpriced.
A Worker drawing against a budget to buy an action now settles that spend to the balanced ledger as showback. A catalog that pays a seller for the bought action — an action price with a payee — remains roadmap.
Where to go deeper
Action Assurance
The certified/community axis — what a certified claim carries, why expiry and revocation downgrade at read time, and how an agent surface can require certified before auto-executing.
Policies, Quotas & Audit
Egress (SSRF) policy, tenant provider policy, header-derived quotas, GCRA rate limiting, and the append-only audit hash chain.