Skip to Content

Billing & Credits

The isA Model Service includes a complete billing infrastructure for tracking API usage, enforcing credit limits, and attributing costs across projects and API keys.

Overview

All API calls consume credits. Credits are deducted atomically at request time via a Redis reservation, then finalized after execution. This ensures accurate accounting even under concurrent load and prevents overspending.

Request arrives Atomic Redis reservation (Lua script) ├── Insufficient credits → 402 Payment Required Execute request Finalize usage → emit NATS billing event → update balance Credit alert (if balance < alert_threshold_credits)

Credits

Credits are the billing unit for all isA platform services.

ConversionRate
1 credit$0.001 USD
1,000 credits$1.00 USD

Credits are pre-purchased and drawn down as you use the platform. When credits run out, requests are rejected with 402 Payment Required unless overage is configured.

Meter Types

Every billable operation is tracked against a MeterType:

MeterTypeTriggered byUnit
TOKEN_INPUTInput tokens sent to modelper 1K tokens
TOKEN_OUTPUTOutput tokens generatedper 1K tokens
TOOL_CALLMCP tool executionper call
STORAGE_READMinIO/storage readsper MB
STORAGE_WRITEMinIO/storage writesper MB
PIPELINE_RUNisA_Data pipeline executionper run

Example costs (illustrative — check your plan for actual rates):

MeterTypeExample cost
TOKEN_INPUT0.15 credits / 1K tokens
TOKEN_OUTPUT0.60 credits / 1K tokens
TOOL_CALL0.5 credits / call
STORAGE_READ0.01 credits / MB
STORAGE_WRITE0.02 credits / MB
PIPELINE_RUN1.0 credits / run

Credit Alerts

Configure a threshold below which a NATS alert is published:

# Environment variable BILLING_ALERT_THRESHOLD_CREDITS=100

Alert events are published to the billing.alerts.{api_key} NATS topic:

{ "event": "low_balance", "api_key": "isa_live_abc123", "balance_credits": 87.4, "threshold_credits": 100, "timestamp": "2026-03-27T12:00:00Z" }

Subscribe to alerts in your application:

import nats nc = await nats.connect("nats://localhost:4222") sub = await nc.subscribe("billing.alerts.isa_live_abc123") async for msg in sub.messages: alert = json.loads(msg.data) if alert["event"] == "low_balance": await notify_team(f"Credits low: {alert['balance_credits']} remaining")

Overage Configuration

By default, requests fail when credits reach zero. Configure an overage allowance to continue beyond zero (billed at the end of the period):

BILLING_OVERAGE_LIMIT_CREDITS=500 # Allow up to 500 credits of overage

When overage is active, responses include a header:

X-ISA-Overage-Credits: 42.3

Dead Letter Queue

Billing events that fail to persist (e.g., during a database outage) are written to the billing-dlq-stream NATS JetStream:

# Inspect DLQ nats stream info billing-dlq-stream # Replay failed events (admin only) nats consumer create billing-dlq-stream --deliver all

Cost Attribution

Costs are attributed to both a project_id and an api_key. Pass X-Project-ID in requests to enable project-level reporting:

curl -X POST http://localhost:8082/api/v1/invoke \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "X-Project-ID: proj_abc123" \ -d '{"model": "gpt-4o-mini", "messages": [...]}'

See Metering & Usage API for querying cost breakdown by project and API key.

Multi-Tenant Billing Scope (SaaS Deployments)

In multi-tenant deployments (e.g. isagent.io), a single API key can act on behalf of many users across many orgs. Every billable request must resolve to exactly one ledger — the requesting user’s personal balance, their org’s shared balance, or (for system-initiated calls) a system ledger — before it’s charged.

This resolution is carried in a signed billing_scope JWT claim, minted upstream by the identity/session layer (isA_user) and forwarded on every request that reaches the Model Service:

{ "billing_scope": { "ledger": "org", "org_id": "org_abc123", "user_id": "user_def456" } }

The Model Service validates the claim’s signature and charges the ledger it names — it does not infer scope from the API key or request context alone. This closed a real production bug: previously, any request the SDK couldn’t confidently attribute to a user fell back to charging the org’s payer pool, silently draining it even for individual-user activity.

Correct billing_scope handling requires isa-model 0.6.2 or later. Earlier versions (and deployments with a stale SDK pin) skip claim validation and fall back to the pre-fix org-payer-pool behavior — if you see unexplained org-pool drain, check your isa-model version first.

Billing Hooks (Internal Services)

Other isA services report usage to the Model Service via internal billing hooks. These endpoints are not for external use.

ServiceEndpointTriggered by
isA_MCPPOST /api/v1/tools/usageEvery MCP tool execution
isA_OSPOST /api/v1/storage/usageEvery MinIO read/write
isA_DataPOST /api/v1/pipelines/usageEvery pipeline run

Environment Variables

VariableDescriptionDefault
BILLING_ENABLEDEnable/disable billingtrue
BILLING_ALERT_THRESHOLD_CREDITSLow-balance alert threshold100
BILLING_OVERAGE_LIMIT_CREDITSOverage allowance (0 = no overage)0
BILLING_PRICING_CACHE_TTL_SECSPricing cache duration300
NATS_URLNATS server for billing eventsnats://localhost:4222
REDIS_URLRedis for atomic reservationsredis://localhost:6379

Next Steps