UsageTap Integration Guide

Completion reason

completionReason is an optional top-level call_end / SDK endCall field: a trimmed, non-empty provider-reported string of at most 128 characters, without control characters. Use the primary Chat Completions choice's finish_reason, Anthropic's stop_reason, or Responses incomplete_details.reason (otherwise its terminal status, such as completed). Omit it when unknown; never infer it from HTTP 200, zero tokens, or UsageTap lifecycle status. Call details display missing values as Unknown. This field does not change pricing, lifecycle status, or activate a Guard detector.

Production and dev/test

Use one environment setting for all UsageTap operations, including customer creation and pure HTTP/API usage. Set USAGETAP_ENVIRONMENT=test in development, staging and QA, and USAGETAP_ENVIRONMENT=production in production. The SDK and observer read it automatically. An explicit SDK/observer defaultEnvironment: "test" overrides the environment variable.

For direct HTTP in any language, add X-UsageTap-Environment: test to the shared UsageTap request headers, including reads, POST /customers, call_begin, call_end and custom_meter. Keep that header on UsageTap requests only. Do not guess from NODE_ENV, customer names, isTest, tags, or metadata. Only production / test are valid. Automatic customers without a header stay unlabelled. In a customer panel, admins can save Traffic group as Production or Dev / test to override request labels for new usage. Automatic removes this pin. Existing usage, active calls and retries keep their recorded group.

Guard behavioral checks always compare Production + unlabelled usage with matching retained history. Dev/Test is excluded from those checks. A separate environment-mix check uses all recorded traffic to detect substantial Dev/Test growth and share increases; it follows existing Guard notification opt-ins. There is no Dev/Test inclusion setting. Viewing filters do not change detection, and eligible retained history is reused without restarting learning.

Verify the outgoing header on customer creation, begin/end and API meter reports, then inspect stored labels and filtered totals. An older SDK accepting an option in local types is not proof of delivery; use a supporting release or direct HTTP. See the API contract.

For outbound PostHog customer lifecycle and allowance signals, see PostHog customer signal delivery.

The live Code Integration workspace is the source of truth for executable examples:

Open Code Integration

It generates the smallest current example for your selected features and model provider, shows the exact server-side keys, and links to maintained playgrounds for specialized workflows.

API requests and MCP tool calls

Enable More > Agentic & API on the usage plan and set its included allocation and single or graduated overage pricing. Report completed requests with the existing custom-meter method:

const result = await usageTap.incrementCustomMeter(
  { customerId, meterSlot: "AGENTIC_API", amount: 1, feature: "mcp.tools.call" },
  { idempotencyKey: requestId },
);

The direct HTTP equivalent is POST /custom_meter with the same body and a stable Idempotency-Key header. Use a server key with usage:write. No provider, model, token count, or standard/premium tier is involved. See the API contract and SDK reference.

This meter behaves like Custom Meter 1 and 2: it spends allocation first, then eligible meter credits and/or PAYG credit. Unfunded usage is recorded with data.blocked: true; the application must enforce that flag. PAYG settlement is asynchronous. It does not reserve dollar credit or impose a rolling rate limit. Live View and meter activity display the events; use agenticApi in usage and forecast widgets.

Make usage useful for Guard

Read Instrument usage for Guard for copyable HTTP/MCP reports, LLM telemetry, and task lookup. Send a trusted customerId, stable feature, and optional customerUserId, taskId, and taskType as top-level attribution. Use the same task ID across one operation's LLM calls and meter events, with distinct event idempotency keys. HTTP metadata.method/endpoint and MCP metadata.serverName/toolName identify operations in Live View; they do not replace first-class attribution.

Guard monitors application behavior automatically from reported usage. Stable feature attribution, detector readiness, notification opt-in and task pagination are separate from successful ingestion; retired feature-mix settings are not a setup step. Organization admins inspect live evidence; offline preflight remains platform-admin-only. Task lookup reports usage so far, not task completion or complete cost. Independent customer monitoring remains future work.

Paste the one-prompt setup from the LLM Integration Reference into the coding agent that has access to your repository. The agent is instructed to inspect the existing provider calls and authentication, choose the UsageTap SDK or direct HTTPS, implement the complete Meter lifecycle, add tests, and return the exact key and verification steps.

The initial onboarding target is UsageTap Meter only. Do not add Gateway, Compress, prompt compression, or Stripe synchronization until the first metered call is working and the user explicitly chooses the next product.

Start here

  1. Keep UsageTap Meter selected for the first integration.
  2. Pick the provider already used by the application: OpenAI, Anthropic, or OpenRouter. Keep the existing provider and model. @usagetap/sdk 1.6.0 and newer preserve both Anthropic streaming forms: messages.create({ stream: true }) and synchronous messages.stream() with its event, cancellation, and final-message helpers.
  3. Pick the implementation: UsageTap SDK, Without UsageTap SDK, or Python.
  4. Start with Minimal diff, then open Complete example when you need the surrounding request lifecycle.
  5. Create the keys shown beside the example and place them in your server environment.
  6. Copy the generated code, run one request, and confirm the call appears in the connection verification step.
  7. Open UsageTap Quickstart to define the default customer plan, allowances, limits, and limit behavior.

Which key do I need?

What you are using Environment variable Key
UsageTap Meter USAGETAP_API_KEY utk-… with usage:write
UsageTap Compress USAGETAP_API_KEY utk-… with compression:invoke
Meter + Compress USAGETAP_API_KEY utk-… with usage:write and compression:invoke
UsageTap Gateway USAGETAP_API_KEY utk-… with gateway:invoke
OpenAI OPENAI_API_KEY Your OpenAI server key
Anthropic ANTHROPIC_API_KEY Your Anthropic server key

Add usage:read for usage reports, customers:read for customer and plan listings, and customers:write for provisioning, plan changes, and replenishment. Existing prefixed keys continue to use their historical permissions. | OpenRouter | OPENROUTER_API_KEY | Your OpenRouter server key |

Put these variables in the server environment. For local Next.js development, that is commonly .env.local. Never use a NEXT_PUBLIC_ name and never send a provider or UsageTap key to browser code.

Create or manage UsageTap keys

MCP for AI agents

UsageTap exposes a hosted, read-only MCP server for ChatGPT, Claude, Codex, and other clients that support Streamable HTTP and OAuth:

https://mcp.usagetap.com/mcp

Add this as a custom MCP server, choose OAuth or automatic authentication, sign in to UsageTap, select one organization, and approve the requested customers:read and/or usage:read access. Do not put an API key in the URL. OAuth uses short-lived access tokens and rotating refresh tokens. Existing scoped UsageTap API keys remain available for service clients that can send an Authorization or x-api-key header.

The server exposes nine read-only tools:

Tool Use it for
list_customers List customers by status, with cursor pagination (default 50, maximum 100).
find_customers Find a customer by friendly name, exact contact email, or external customer ID. Email is masked unless explicitly requested.
list_calls Read paged calls with date/customer filters, feature, model, tokens, and itemized cost (default 20, maximum 50).
summarize_calls Get complete usage totals and top customers, features, models, providers, UTC days, or statuses.
get_customer_usage Read current plan, meters, allowances, balances, and limit policy for one customer.
get_customer_usage_context Prepare an exact-customer account brief with recent movement, signals, replenishment-bounded forecast status, deterministic review reasons, and completeness warnings. Requires both read scopes.
get_usage_overview Summarize the last seven UTC days of calls, tokens, cost, and active customers.
get_usage_anomalies Retrieve UsageTap's bounded daily and hourly anomaly results.
get_customer_signals Find current limit, usage-movement, and new-customer activation signals with status, customer, plan, type, and severity filters.

Example: prepare an account usage brief

Resolve a friendly name with find_customers, then pass the returned exact external ID to get_customer_usage_context:

{
  "customerId": "customer-123",
  "timezone": "UTC",
  "forecastMetric": "agenticApi",
  "includeForecast": true,
  "includeSignals": true
}

The result keeps calls, tokens, USD provider cost, and custom-meter quantities in separate series. Check evidence.complete, every component status, and forecast quality before using the review state. An included allowance runout is not a guarantee that service stops: purchased meter credit, PAYG funding, or another configured policy may allow usage to continue. The tool recommends human review but cannot contact customers or change a plan.

Example: inspect calls and compare usage

Both list_calls and summarize_calls require usage:read. Supply ISO timestamps with a timezone: startTime is inclusive and endTime is exclusive, with a maximum range of 31 days. Optionally filter by one exact external customerId; omit it for the authenticated organization.

Example list_calls arguments (replace the example dates and customer):

{
  "startTime": "2026-09-01T00:00:00Z",
  "endTime": "2026-09-02T00:00:00Z",
  "customerId": "customer-123",
  "limit": 20
}

The result contains calls, count, nextCursor, and hasMore, with newest calls first. Default page size is 20, maximum 50. Reuse identical dates and customer filters with cursor: nextCursor until it is null, even when a page is empty. Each call includes identity, customer ID/stored name, feature, up to 20 tags, provider/model, reasoning effort, status, timestamps/duration, token breakdown, recorded USD cost, itemized costs, and compression savings. Prompts, contact emails, raw errors, and billing identifiers are excluded. Ask the agent to filter returned rows by feature, model, status, or cost.

Example summarize_calls arguments:

{
  "startTime": "2026-09-01T00:00:00Z",
  "endTime": "2026-09-02T00:00:00Z",
  "groupBy": "customer",
  "sortBy": "costUsd",
  "limit": 5
}

groupBy supports customer (default), feature, model, provider, day (UTC), and status. sortBy supports calls (default), costUsd, and totalTokens, descending. The group limit is 20 by default, maximum 50. It only controls returned groups: totals still covers the full range, groupCount counts all groups, and groupsTruncated indicates omitted groups.

Always check complete. The summary reads at most 5,000 calls, 20 database pages, or 10 seconds. If the full range cannot be read, complete is false, limitReason explains the bound, totals is null, and groups is empty. Narrow the dates or select one customer; use list_calls for manual paging. No partial leaderboard is represented as the top customers overall.

Calls and summaries include all statuses. Unknown metering stays null on calls; summary totals sum known values and report missingCostCalls and missingTokenCalls. completedCalls reports the completed subset. Total tokens are input plus output; cached and reasoning tokens are not added a second time. Recorded model cost is separate from customer/platform billing charges. Itemized reasoning cost can overlap completion cost, so use costUsd for the total. These tools read retained, live, eventually consistent call records. Fixed date bounds do not freeze records against late completion, updates, or deletion.

Example agent prompt:

Show the five customers with the highest recorded call cost yesterday (UTC).
Use summarize_calls, group by customer, and sort by costUsd. Check complete
and missing metering before drawing conclusions. Then use list_calls to show
20 recent calls for the highest-cost customer, including feature, model,
input/output tokens, and cost. Label these calls as one page.

Example: prioritize customers

Paste this into the connected agent:

Use the UsageTap MCP server to find ACTIVE and ON_HOLD customers with
critical, warning, or opportunity signals.

For each customer, return the external customer ID, friendly name, plan,
signal type, evidence, observedAt, and analysisDate. Keep contact email
masked. Do not contact customers or change CRM records.

An agent may translate that request into bounded get_customer_signals arguments like:

{
  "statuses": ["ACTIVE", "ON_HOLD"],
  "severities": ["critical", "warning", "opportunity"],
  "types": [
    "limit_exhausted",
    "limit_approaching",
    "usage_collapsed"
  ],
  "includeContactEmail": false,
  "limit": 20
}

Use nextCursor to request another page. sourceCapped: true means UsageTap hit a defensive source-read bound, so the result should not be treated as a complete organization-wide scan.

Example: prepare a CRM handoff

Use UsageTap to find active Growth-plan customers with customer_activated,
usage_growing, or limit_approaching signals.

Return signalKey and analysisKey so my CRM workflow can deduplicate the
results. Include the stored contact email only for this authorized CRM
export. UsageTap must remain read-only.

Contact email is masked by default, such as a***@example.com. includeContactEmail: true returns the stored address only after the authenticated caller explicitly requests it. Use this option only when the downstream workflow is authorized to handle that contact data.

A Customer Signals result includes current customer context and deterministic integration identifiers:

{
  "analysisDate": "2026-09-01",
  "analysisTimezone": "UTC",
  "totalCustomers": 1,
  "countsByType": { "limit_approaching": 1 },
  "customers": [{
    "customerId": "customer-123",
    "friendlyName": "Acme, Inc.",
    "customerEmailMasked": "a***@example.com",
    "status": "ACTIVE",
    "planName": "Growth",
    "customerSince": "2026-08-12T00:00:00.000Z",
    "nextReplenishAt": "2026-09-12T00:00:00.000Z",
    "signals": [{
      "type": "limit_approaching",
      "observedAt": "2026-09-01T18:20:00.000Z",
      "signalKey": "signal:customer-123:limit_approaching:standard%20calls",
      "analysisKey": "signal:customer-123:limit_approaching:standard%20calls:2026-09-01",
      "evidence": {
        "metric": "standard calls",
        "percentRemaining": 12
      }
    }]
  }],
  "nextCursor": null,
  "sourceCapped": false
}

signalKey identifies the customer, signal type, and applicable meter within the connected organization. analysisKey adds the UTC analysis date, which a CRM can use for daily idempotency. These are computed identifiers, not stored workflow state.

UsageTap supplies current evidence. The consuming CRM or agent owns first-seen history, assignments, acknowledgements, contact attempts, resolution, campaigns, and CRM records. The MCP server cannot send outreach, change a customer, or write CRM state.

Customer attribution

When an example uses customerId, populate it from your trusted server-side session or database. Use the same stable ID for the same customer across calls. Do not accept an unverified customer ID directly from browser input.

End-user attribution is optional. When a customer account has multiple users, send customerUserId from the same trusted server-side identity. Prefer a stable, non-PII application user ID. customerUserName and customerUserEmail are optional display metadata for Live activity and should be omitted when they are unnecessary.

const usageContext = {
  customerId: currentCustomer.id,       // Required customer/account ID
  customerUserId: currentUser.id,       // Optional stable end-user ID
  customerUserName: currentUser.name,   // Optional display metadata
  customerUserEmail: currentUser.email, // Optional display metadata
  feature: "chat.reply",
};

Direct HTTP requirements

UsageTap public API requests use:

  • Authorization: Bearer <USAGETAP_API_KEY>
  • Accept: application/vnd.usagetap.v1+json
  • Content-Type: application/json when a request has a JSON body
  • Idempotency-Key for retried create, begin, and state-changing requests

The Without UsageTap SDK and Python examples in Code Integration include these headers. See the API reference for request and response contracts.

Specialized workflows

Keep specialized code beside the product workflow that owns it:

The Examples & playgrounds tab in Code Integration also covers customer management, Gateway batches, compression, summarization, sampling, and profiling without duplicating each product’s local setup.

Integration checklist

  • At least one UsageTap feature is selected.
  • Every key is server-side.
  • The provider key and UsageTap key are separate variables.
  • customerId comes from a trusted server-side identity.
  • Optional customerUserId comes from trusted application identity, not request input.
  • Direct API calls send the required versioned Accept header.
  • Failed provider calls still finish metering with an error.
  • The integration has been tested with a real customer and a realistic prompt.

If you need help, use the support option in the application after opening the relevant product workspace.