Confluye
Endpoints

Usage

Workspace limits plus the usage ledger, budgets, alerts, and reconciliation.

Usage v1 routes require a workspace bearer API key. 401 / 403 use { ok: false, error: { code, message, retryable }, meta }. Success is { ok: true, data, meta } with meta.contractVersion: "api-contracts.v1".

Mutations require header Idempotency-Key. Replays with the same fingerprint return the stored response; a different body is a conflict.

Session GET /api/usage?workspaceId= returns { usage } without the contract envelope (membership required).

Endpoints

MethodPathDescription
GET/api/v1/usage/limitsSubscription-style usage summary
GET/api/v1/usage/eventsList ledger events
POST/api/v1/usage/eventsAppend a priced event
GET/api/v1/usage/budgetsList budgets
PUT or POST/api/v1/usage/budgetsUpsert a budget
GET/api/v1/usage/alertsList alerts
POST/api/v1/usage/alertsEvaluate alerts now
GET/api/v1/usage/reconciliationCompare ledger vs legacy credits

Limits

GET /api/v1/usage/limits → data: { usage }. limits[] always use the internal-test caps documented in Usage, budgets, and alerts. Without a billing account to read, usage.plan is "internal-test" and usage.billingEnabled is false.

Viewer keys receive the workspace-safe.v1 aggregate projection without organization billing identifiers, provider/customer coordinates, or invoice URLs. Keys with workspace.secret.read or workspace.write retain the full billing summary.

On a database-backed deployment the summary also carries usage.billing, the organization's billing summary, and usage.plan / usage.billingEnabled are taken from it (usage.plan falls back to "internal-test" while no plan is current: none has been chosen yet, or the subscription was canceled. In both cases usage.billing.currentPlanKey is null and usage.billing.compute.includedCredits is "0"). usage.billing.state is one of:

StateMeaning
trialingA trial the owner started is running.
activeA paid plan is current.
gracePayment is outstanding; access continues until graceEndsAt.
restrictedPayment failed past the grace window.
canceledThe subscription ended. No plan is current, runs are refused with billing_subscription_canceled, and the owner can choose any plan again through Checkout.
operator_compedBilling is waived by the operator.
unentitledProvisioned but not yet entitled: the account exists, nobody comped it, and no plan or trial has been chosen. Self-provisioned organizations start here, and it is not a trial — the owner still starts one through onboarding.

Organization costs (session and capability)

The session tRPC query organizationCosts.report reads numeric organization evidence for an organization Owner or Admin, preserving the organization's MFA checks. Ordinary members cannot read it. The read-only capability billing-usage.costs provides the same report for the authenticated workspace's organization, requiring its existing workspace read grant and an acting organization Owner or Admin. A caller cannot select another organization through capability input. API-key and OAuth capability calls are additionally limited to the authenticated target workspace; their workspaceId filter cannot select another workspace. Filter options also remain inside that workspace, including workflow, user, provider and model names. Organization-wide session/internal-agent queries still require the acting user's current organization Owner/Admin role and verified MFA assurance when required by policy.

Inputs: from, to (exclusive ISO UTC midnight boundaries, 1–366 days), optional workspaceId, workflowId, responsiblePrincipalId, provider, model, nodeId, groupBy (workflow, user, workspace, node, invocation), metric (usd, mxn, equivalent, compute, tokens), cursor, optionsSearch (optional name/ID substring, at most 100 characters), and limit (1–100, default 25). Session input additionally requires organizationId; timeZone is UTC only.

The safe report contains totals, descending ranked rows, daily, name-based filter options, captureStartedAt, generatedAt, and nextCursor. Filter options are bounded to 50 per kind on the first page. optionsTruncated signals that a more specific optionsSearch is needed; continuation pages return empty option lists and do not rerun that inventory. Search affects the choices only, preserving the report's totals and authorization scope. Numeric quantities serialize as integer strings; unknown monetary/token quantities are null, never an inferred zero. USD and MXN costs are in millionths; equivalentCreditMicros is millionths of an informational credit. Coverage counts include invocations, measuredInvocations, pricedInvocations, knownMxnInvocations, convertedInvocations, and known-token counts. MXN coverage is independent of equivalent-credit coverage; the mxn metric sorts by known MXN amounts, including measured zero before unavailable values. Equivalent credits do not alter billing, budgets, balances or Stripe exports.

The global ranking precedes pagination. Totals and daily values cover the full filtered set. Signed cursors are bound to the organization, period, filters, grouping, measure and page size; changing any of them starts a new page. Cursors also bind a content revision of the filtered evidence. If that evidence changes before the next page, the request is refused as an invalid cursor (tRPC BAD_REQUEST, MCP -32602 / invalid_cursor). Omit the cursor to restart; never combine the old rows/totals with a new revision. Changes outside a selected workspace do not invalidate its cursor. Node/provider/model views return compute quantities as null, because workflow compute cannot be allocated to those dimensions. Invocation rows include retained numeric quote references, not prompts or tool payloads. Public POST /api/v1/usage/events does not populate this trusted node ledger.

The existing GET /api/billing/organization-usage, billing-usage.organization, usage v1 routes, and their payloads retain their current contracts. See the organization costs guide.

Events query

Query: dateFrom, dateTo (ISO; dateFrom must be earlier than dateTo; dateTo is exclusive), provider, model, usageClass, usageTag (1–64 letters, digits, ., _, :, or -), cursor (base64url { occurredAt, id }), limit (1–100, default 50). groupBy must be provider, model, or usageClass if set. The API key's workspace always scopes the query. usageTag filters events attributed to that product tag within the selected date range; use groupBy to total cost and quantity for the period.

Usage from a workflow run started with productContext.usageTag inherits the pseudonymous tag stored on that run. The event's own runId decides when it is a workflow run of the workspace; metadata.workflowRunId is read only when runId is not a workflow run (the agent runtime records a step under its own run ID and names its workflow run there). An event whose runId and metadata.workflowRunId name two different workflow runs of the workspace is refused with 400, so an event never belongs to one run while its usage is attributed to another. Internal, manual, and scheduled runs without product context have no tag. The ledger stores this product tag, never an end user's external ID.

data: { events, nextCursor, breakdown? }. Bigints serialize as strings; costCredits is a number.

The workspace-safe.v1 event projection omits metadata, eventKey, and requestId. For valid audio_transcription events measured in audio_millisecond, it can include evidence: { durationMs, status, providerCompleted }. Duration must match the event quantity and be at most 60 seconds; invalid or missing evidence is omitted. status is succeeded or failed. A completed provider request can be charged even when its transcript is empty or later validation fails. This evidence contains neither recording bytes nor transcript content.

POST required fields: eventKey, provider, model, usageClass, unit, quantity (positive integer string). Optional: inputTokens, outputTokens (non-negative ints, default 0), runId, stepId, requestId, metadata, occurredAt. Server sets costMicros and pricingVersion from the catalog. Mismatched client costMicros / pricingVersion: 400. Duplicate eventKey with the same payload: 200 { event, deduplicated: true }. Different payload: 409 CONFLICT. Created: 201.

Budgets

GET → data: { budgets[] } (limitMicros string, period, warningPercent, enabled).

PUT/POST body: limitMicros (positive bigint string), period (daily | monthly, default monthly), optional warningPercent (1–100 integer, default 80), optional enabled (default false on create). Invalid: 400.

Alerts

GET → data: { alerts[] }. POST (empty body, still needs idempotency) → data: { enabled: true, created[] } for newly opened alerts this evaluation.

Reconciliation

Query legacyCostCredits (required, ≥ 0) and optional dateFrom / dateTo. data includes ledgerCostMicros, legacyCostMicros, deltaMicros (strings), credit floats, and status: balanced or mismatch (tolerance default 0 micros). 1 cost credit = 10_000 micros.

Next steps