OXYGENOxygen/ Docs
Safety & cost

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 --json

Knowledge synthesis is one AI call, so its cap is per call: oxygen knowledge synthesize --max-credits <n> (MCP max_credits) is charged exactly its estimate, never more than n. A higher estimate is refused with spend_cap_too_low before the AI call, nothing charged, and the refusal reports the estimate, so --max-credits 0 is a free quote. Saving a reviewed body (--approved --body) makes no AI call and needs no cap.

The 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. Skipped items are not billed.

A capped run is never reported as a plain success: oxygen table-runs get <run-id> returns credit_limit_skipped (the rows the cap stopped), the credit_cap bottleneck and user_status: "needs_review", and the grid marks each stopped cell Credit cap.

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. For a column, run it again without --force: filled cells are skipped, so only the rows the cap stopped run and are billed.

oxygen table-runs pause <run-id> --json
oxygen table-runs resume <run-id> --json
oxygen table-runs cancel <run-id> --json

When 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 size at fire time. An explicit cap always wins. Every plan size's number is in the pricing reference, and yours is in oxygen limits show and in the web app under Settings → Plan limits:

oxygen limits show --json | jq '.data.spend_safety_defaults'

How binding that default is depends on the trigger:

TriggerDefault 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 eventMonitored, 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.

Agents and the Copilot

An Agent never spends without a ceiling. A manual run carries the max_credits you approve for it, and a scheduled, event or webhook trigger carries the per-delivery max_credits you approve when you arm it. One run then spends at most the smaller of that approval and your plan's default Agent run ceiling, agent_run_max_total_credits; its own model calls stop at agent_run_max_inference_credits. To give one run or trigger more than the default, approve the higher max_credits for it explicitly (--approve-above-default, or approve_above_default on MCP, or the card in the web app), up to agent_run_approved_max_credits. So the most a nightly Agent can spend without asking you again is its trigger's max_credits, capped at the default unless you approved more when you armed it, each night.

The Workspace Copilot asks before any paid step larger than your plan's approval-free allowance: copilot_approval_free_per_call_credits for one step and copilot_approval_free_per_session_credits for all of them in one conversation. Sends, launches, publishing and other writes outside the workspace always ask, at any price.

oxygen limits show --json | jq '.data.spend_safety_defaults | {agent_run_max_total_credits, agent_run_max_inference_credits, agent_run_approved_max_credits, copilot_approval_free_per_call_credits, copilot_approval_free_per_session_credits}'

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. If the tool's managed_credit_estimate names scales_with, its per-call cost grows with those request fields, so use a dry run of the real request rather than the listed default-request quote.

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

Run ceilings cover OXYGEN credits, including own-key platform fees, and leave your vendor-account spend outside the ceiling. A dispatched own-key miss is not safe to repeat for free. See own-key fees and retries for retry_safe, own_key_spend and enforced provider call limits.

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-size 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 5× and blocks at 20× your monthly credits spent in one day. Monthly reservations (connected accounts, sending mailboxes, deliverability) and managed mailbox and warmup subscriptions do not count toward that guard. oxygen budget list --json reports its thresholds for your plan under org_daily_spend_guard, and the plan's per-run ceilings (one Agent run, one trigger delivery, one auto-run batch) under per_run_ceilings, so a list with no caps of your own never reads as unbounded. Watch the balance with:

oxygen billing balance --json
oxygen billing usage --limit 50 --json

See Billing.

Storage limits

Storage is a limit, not a meter: it costs no credits. Rows per Table, retained Table rows per workspace and the Table database size follow your plan size, and one import file may hold as many rows as one Table. oxygen limits show --json lists your plan's storage limits, every plan size's, and your current usage. At a limit nothing is deleted: only new growth is refused, and reads, exports, edits and deletes keep working. See Tables for recovery.

Table and signal limits

Import file size, the columns one action run or standing auto-run may carry, and the signals feed (events per read and look-back days) also follow your plan size. They cost nothing and are never sold. oxygen limits show --json lists them under plan_bands[].storage, plan_bands[].tables and plan_bands[].signals; the pricing reference has the full table.

Workflow schedule and time limits

These are limits, not meters: they cost no credits. Your plan sets the fastest cron schedule a workflow or Agent trigger runs; a Code step may run up to 30 seconds and one run attempt up to 30 minutes on every plan, after which the run resumes from its last checkpoint. oxygen limits show --json | jq '.data.workflow_limits' lists them, and Triggers explains what happens to a schedule faster than your plan allows.

Sending limits are not spend caps

Daily sending limits per mailbox, WhatsApp number, LinkedIn account and phone number protect each account's reputation. They are the same on every plan and never charge credits. See them, with the command that changes each one:

oxygen limits show --json | jq '.data.sending_limits'
oxygen mailboxes cap set <mailbox> --daily-cap <n>
oxygen voice numbers cap set <number> --daily-cap <n>
  • Approvals — caps are part of the approval card.
  • Modes — only live runs require caps.
  • Runs — inspecting run status and item outcomes.

On this page