Usage Metering
Overview
Circuitry meters workflow execution at a fine-grained level so teams can understand cost and enforce limits. Metering is recorded per workflow run, per node execution, and per resource type (AI tokens, execution time, HTTP calls, bytes transferred, etc.).
This document defines the metering model and how it integrates with the headless WorkflowExecutorCore
, including webhooks and plugin nodes.
Goals
- Attribute cost and usage precisely to nodes and workflows
- Support quotas and throttling per user/project
- Provide auditable logs for billing and analytics
- Safely meter untrusted code (Code node) and external AI usage
Metering Dimensions
- Node execution count: every node execution emits a metering event
- Execution time: measured wall-clock duration per node
- AI token usage: prompt/response tokens per AI provider (OpenAI and others)
- HTTP usage: request count, status, and bytes in/out
- Plugin execution: per-plugin invocation, time, and optional custom units
- Memory ceiling breaches/timeouts: count and reason
Data Model (conceptual)
type MeterEvent = {
runId: string
workflowId: string
userId: string
nodeId: string
nodeType: string
timestamp: string
// Dimensions
durationMs?: number
ai?: {
model: string
promptTokens: number
completionTokens: number
totalTokens: number
}
http?: {
requests: number
bytesOut: number
bytesIn: number
}
plugin?: {
pluginId: string
invocations: number
}
code?: {
cpuTimeMs?: number // optional if available
}
outcome: 'success' | 'error' | 'timeout' | 'cancelled'
errorMessage?: string
}
Storage options: Supabase table (append-only), external billing pipeline, or queue + batch writer.
Executor Integration
Emission points
- On
NODE_STARTED
: capture start timestamp for the node - On
NODE_COMPLETED
/NODE_ERROR
: computedurationMs
, include outcome - AI nodes: capture tokens from provider response metadata
- Code nodes: measure duration; if available, track CPU usage in worker
- HTTP helper: increment request/bytes counters per node context
Implementation hooks
- Extend
WorkflowExecutorCore
to acceptonMeter(event: MeterEvent)
callback - Maintain a per-node accumulator in execution context for HTTP and plugin usage
- Wrap AI client calls to intercept token usage
- Wrap fetch in executor context to count requests/bytes per node
new WorkflowExecutorCore({
onMeter: (event) => queue.enqueue(event) // or direct DB insert
})
AI Token Metering
- Use provider response fields (e.g., OpenAI
usage.prompt_tokens
,completion_tokens
) - If provider lacks usage fields, estimate via tokenizer libraries
- Record model, tokens, and error cases (zero tokens on failure)
Code Node Time Metering
- Execute Code nodes inside a Worker thread per request
- Start high-resolution timer before execution; stop on completion/timeout
- Record
durationMs
; markoutcome=timeout
if watchdog triggers - Optionally read
performance.eventLoopUtilization()
inside the worker to approximate CPU time
Webhook Runs
- Each webhook invocation creates a
runId
- All meter events include
runId
to correlate - Recommend 202-accepted + async processing for long runs; meter events flushed progressively or at end
Quotas and Limits (optional phase)
- Per-user/project daily caps on:
- Total AI tokens
- Total node executions
- Code node time
- HTTP requests
- Enforce via pre-execution check; block or degrade when exceeded
Export and Analytics
- Provide
/api/metering/runs/:runId
and summaries per user/project - Dashboard cards: tokens by model, time by node type, top workflows by cost
Security & Reliability
- Never log secrets
- Batch writes to reduce DB load
- Back-pressure if queue is full; drop debug-only dimensions first
Next Steps
- Add
onMeter
toWorkflowExecutorCore
- Instrument AI helper and HTTP helper to emit usage
- Wrap Code node execution timing in worker
- Add Supabase schema and writer
- Build docs UI page and sidebar link