Spend caps
A per-run credit limit. Work past the limit is skipped, not charged.
A spend cap is the maximum number of credits one run can consume. It is required on every live table, enrichment, or workflow run that touches a paid provider.
Setting a cap
Preview first — it costs nothing — then re-run with the cap:
oxygen columns run <table-id> <column-key> --dry-run --json
oxygen columns run <table-id> <column-key> --background --limit 50 --approved --max-credits 50 --json
oxygen table-runs create <table-id> --column <column-key> --limit 100 --approved --max-credits 100 --json
oxygen enrich-column preview <table-id> --capability work_email --limit 100 --json
oxygen enrich-column run <table-id> --capability work_email --limit 100 --approved --max-credits 100 --json
oxygen workflows call <workflow> --input-json '{...}' --mode dry_run --json
oxygen workflows call <workflow> --input-json '{...}' --mode live --approved --max-credits 100 --jsonThe cap applies to the whole run, not per item. A run with 500 items and a 50-credit cap can process at most 50 items if each costs 1 credit.
What happens when a cap is hit
The run does not pause and it does not fail. Items still pending when the reservation reaches the cap are marked skipped with skippedReason: "credit_limit_reached", and the run then finalizes as completed (or completed_with_errors if unrelated items failed). Items that already succeeded keep their results and their captured cost.
oxygen table-runs items <run-id> --status skipped --json | jq '.data.items[].skippedReason'Because a capped run terminates rather than parks, table-runs resume will not continue it — that command only resumes a run you paused yourself with table-runs pause. To process the remaining rows, create a new scoped run with a new --max-credits value.
oxygen table-runs pause <run-id> --json
oxygen table-runs resume <run-id> --json
oxygen table-runs cancel <run-id> --jsonWhen you do not set one
An autonomous trigger — a workflow cron, webhook, or event, or a table auto-run — armed without an explicit --max-credits resolves a default per-delivery ceiling from your plan tier at fire time. An explicit cap always wins. The per-tier numbers are in the pricing reference.
How binding that default is depends on the trigger:
| Trigger | Default ceiling without --max-credits |
|---|---|
| Table auto-run (import, insert, upsert, webhook-written rows) | Binding. Rows past the ceiling are skipped with credit_limit_reached, and a delivery that cannot resolve the ceiling is not enqueued at all |
| Workflow cron, webhook, or event | Monitored, not binding. The ceiling is resolved and recorded per delivery, but it does not stop the run |
Pass an explicit --max-credits when you arm a workflow trigger. An uncapped autonomous workflow delivery is bounded only by your credit balance and the org-daily guard.
Picking a cap
For a blueprint: preflight, then cap at the forecast plus a small buffer. Only price-aware seeds return pricing_forecast; the rest carry no estimate.
oxygen blueprints preflight <slug> --input-json '{...}' --json \
| jq '.data.preflight.pricing_forecast | {total_credits, minimum_required_credits, cap_status}'For batched column runs: rows × per-call cost.
oxygen tables query <table-id> --limit 1 --json | jq '.data.total_count'
oxygen tools get <tool-id> --json | jq '.data.tool.estimated_credits_per_call'Org-level guardrails
Standing caps above the per-run ceiling are budget policies, one per scope + window:
oxygen budget set --scope org --window daily --max-credits 5000 --action hard_block --json
oxygen budget list --json--scope is org, table, workflow_trigger, or monitor; --window is per_run, daily, or monthly; --action is hard_block (default) or warn.
Underneath those sit two guards you do not configure: the plan-tier default per-delivery ceiling above, and — only while the org has no explicit org-daily policy of its own — an implicit org-daily guard that warns at 25% and blocks at 100% of your monthly credits spent in one day. Watch the balance with:
oxygen billing balance --json
oxygen billing usage --limit 50 --jsonSee Billing.