F FourIA GitHub ↗

Cost Observability

On this page

The worker instruments per-client cost telemetry using Cloudflare Analytics Engine. All events are written to the fourai_cost_metrics dataset.

Billing independence: Cost tracking is separate from client billing. Billing uses credits consumed (messages, automations) only. Cost observability tracks all platform events (AI queries, container activity, R2 ops, cron triggers, WebSocket sessions, action completions, action steps) for internal cost analysis and infrastructure optimization. Billing events are a subset of cost tracking events. See VISION.md.

Setup

The ANALYTICS binding must exist in your Cloudflare account before deploying. It is declared in wrangler.jsonc and maps to the fourai_cost_metrics dataset.

Customer identity is read from the AGENT_TENANT env var (e.g. arua). If unset it defaults to default.

Event Schema

ai-query

Emitted for each AI response received over the WebSocket proxy.

FieldTypeDescription
blob1stringEvent type: "ai-query"
blob2stringModel name (e.g. claude-3-5-sonnet)
blob3stringProvider (e.g. anthropic)
blob4stringCustomer identifier
blob5stringAction type
blob6stringRequest ID
blob7stringSession ID (empty if not set)
double1numberInput tokens
double2numberOutput tokens
double3numberEstimated cost in USD
double4number1 if cached, 0 otherwise

container-active

Emitted on every ensureGateway() call (≈ every request) with a 10-second index bucket to allow deduplication.

FieldTypeDescription
blob1stringEvent type: "container-active"
blob2stringCustomer identifier
blob3stringInstance type (e.g. standard-1)
blob4stringAction: start | heartbeat | stop
blob5stringGateway process ID
double1numberAlways 1 (count)

r2-operation

Emitted for R2 backup/restore operations.

FieldTypeDescription
blob1stringEvent type: "r2-operation"
blob2stringCustomer identifier
blob3stringOperation type: backup | restore | sync | head | delete
blob4stringR2 pricing class: ClassA | ClassB
double1numberBytes transferred
double2numberDuration in milliseconds

cron-trigger

Emitted on every scheduled cron invocation.

FieldTypeDescription
blob1stringEvent type: "cron-trigger"
blob2stringCustomer identifier
blob3stringTrigger reason
double1numberAlways 1 (count)
double2number1 if container was woken, 0 otherwise

ws-session

Emitted when a WebSocket session closes.

FieldTypeDescription
blob1stringEvent type: "ws-session"
blob2stringCustomer identifier
blob3stringSession ID
double1numberSession duration in seconds
double2numberTotal messages exchanged

action-completed

Emitted when an agent action completes (message or automation).

FieldTypeDescription
blob1stringEvent type: "action-completed"
blob2stringCustomer identifier
blob3stringInstance ID (empty if not set)
blob4stringAction type: message | automation
blob5stringChannel type (e.g. slack, api)
blob6stringAgent identifier
blob7stringStatus: success | failure
blob8stringFailure reason (empty on success)
double1numberInput tokens
double2numberOutput tokens
double3numberCost in USD
double4numberDuration in milliseconds

action-step (v0.3.0)

Emitted for each individual step within an action (AI query, tool call, exec call, etc.).

FieldTypeDescription
blob1stringEvent type: "action-step"
blob2stringCustomer identifier
blob3stringAction ID
blob4stringStep type: ai_query | tool_call | etc.
blob5stringTool name (empty if not a tool step)
blob6stringOutcome: completed | error | blocked
double1numberDuration in milliseconds
double2numberAI cost in USD (0 for non-AI steps)
double3numberInfrastructure cost in USD
double4numberCredits consumed (1 if completed step)

SQL Queries

Analytics Engine uses a SQL-like syntax. Replace fourai_cost_metrics with your actual dataset name if it differs.

Per-customer AI spend (last 24 hours)

SELECT
  blob4  AS customer,
  blob2  AS model,
  SUM(double1) AS tokens_in,
  SUM(double2) AS tokens_out,
  SUM(double3) AS cost_usd
FROM fourai_cost_metrics
WHERE blob1 = 'ai-query'
  AND timestamp > NOW() - INTERVAL 1 DAY
GROUP BY customer, model
ORDER BY cost_usd DESC

Container active time per customer

Each heartbeat covers a 10-second bucket. Multiply count by 10 for seconds.

SELECT
  blob2 AS customer,
  blob3 AS instance_type,
  COUNT(*) * 10 AS estimated_active_seconds
FROM fourai_cost_metrics
WHERE blob1 = 'container-active'
  AND blob4 = 'heartbeat'
  AND timestamp > NOW() - INTERVAL 1 DAY
GROUP BY customer, instance_type

Container start events

SELECT
  blob2 AS customer,
  COUNT(*) AS cold_starts
FROM fourai_cost_metrics
WHERE blob1 = 'container-active'
  AND blob4 = 'start'
  AND timestamp > NOW() - INTERVAL 7 DAY
GROUP BY customer
ORDER BY cold_starts DESC

R2 bandwidth per customer

SELECT
  blob2 AS customer,
  blob3 AS op_type,
  SUM(double1) AS bytes_transferred,
  AVG(double2) AS avg_duration_ms,
  COUNT(*) AS op_count
FROM fourai_cost_metrics
WHERE blob1 = 'r2-operation'
  AND timestamp > NOW() - INTERVAL 7 DAY
GROUP BY customer, op_type

Cron wake rate (container sleep efficiency)

A high woke_rate means the container is frequently sleeping between crons — consider lowering SANDBOX_SLEEP_AFTER.

SELECT
  blob2 AS customer,
  COUNT(*) AS total_crons,
  SUM(double2) AS woke_count,
  SUM(double2) / COUNT(*) AS woke_rate
FROM fourai_cost_metrics
WHERE blob1 = 'cron-trigger'
  AND timestamp > NOW() - INTERVAL 7 DAY
GROUP BY customer

WebSocket session duration distribution

SELECT
  blob2 AS customer,
  COUNT(*) AS sessions,
  AVG(double1) AS avg_duration_s,
  MAX(double1) AS max_duration_s,
  AVG(double2) AS avg_messages
FROM fourai_cost_metrics
WHERE blob1 = 'ws-session'
  AND timestamp > NOW() - INTERVAL 7 DAY
GROUP BY customer

Cost Estimation

The GET /api/admin/costs endpoint returns the pricing constants used by estimateContainerCost() and an example GraphQL query for the AI Gateway dashboard.

Container pricing (Cloudflare, as of Jul 2026)

ResourceRateBilling model
CPU$0.00002 / vCPU-secondActive usage × 0.2 utilisation
Memory$0.0000025 / GiB-secondProvisioned (always billed)
Disk$0.00000007 / GB-secondProvisioned (always billed)

Instance types

TypevCPUMemoryDisk
lite1/160.25 GiB2 GB
basic1/41 GiB4 GB
standard-11/24 GiB8 GB
standard-216 GiB12 GB
standard-328 GiB16 GB
standard-4412 GiB20 GB

The default instance is standard-1. Memory and disk are billed continuously regardless of CPU activity, so the sleep setting (SANDBOX_SLEEP_AFTER) does not reduce memory/disk costs.