decisionshared last reviewed 2026-05-21

Always get explicit per-run approval before paid/metered API calls

Context

AI agents acting autonomously can rack up surprise bills on metered APIs (Google Places, Apify, OpenAI direct, ElevenLabs, Twilio, etc.) โ€” even within "free tier" allowances, because free tiers expire, change, or quietly start billing. A single agent loop calling a paid endpoint N times is a real money leak. This rule sets the guardrail.

Detail

Decision

Never invoke a paid/metered API without explicit per-run approval from Kom โ€” even within the free tier. Approval is per-run, not blanket.

What counts as "paid/metered"

APIWhy metered
ApifyPer-scrape compute units
Google Places / MapsPer-request after free quota
OpenAI direct (not via gateway)Per-token
ElevenLabsPer-character TTS
TwilioPer-SMS / per-minute
Perplexity (some tiers)Per-query
Freepik / image genPer-image

What does NOT need per-run approval

SourceWhy exempt
Aspire LLM Gateway (llm.aspiredigital.group/v1)$0 marginal โ€” OAuth subscriptions absorb cost (per aspire-llm-gateway-only-egress)
Self-hosted services (Postgres, MinIO, Coolify apps)Fixed cost, not per-call
Read-only internal APIs (BizDB, Xero gateway)Aspire-owned, no marginal cost
GitLab, Odoo (self-hosted)Fixed cost

The approval pattern

  1. Agent identifies it needs a paid API call
  2. Agent estimates the cost (e.g., "452 FB + 133 IG scrape โ‰ˆ $4 AUD")
  3. Agent presents the estimate + waits for explicit "yes, run it"
  4. Only then does the call fire
  5. Agent logs the actual cost after

Real example (per MEMORY.md project_bizdb_apify_pending)

The BizDB Apify scrape (452 Facebook + 133 Instagram, ~$4 AUD) was approved but deferred โ€” batched to a single future session so the scrape + writeback happen together, minimizing repeated runs. Even at $4 and pre-approved, it waits for a deliberate "fire it now" rather than running ad-hoc.

Why per-run, not blanket

Constraints we accepted

Revisit trigger

Related

๐Ÿ”— Relationships

graph LR ask_before_paid_api["ask-before-paid-api"]:::self ask_before_paid_api --> aspire_llm_gateway_only_egress["aspire-llm-gateway-only-egress"] ask_before_paid_api --> aud_pricing_default["aud-pricing-default"] ask_before_paid_api --> bizdb["bizdb"] classDef self fill:#715EE3,color:#fff,stroke:#291F50;