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:
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.
Recommended: let your coding agent implement Meter
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
- Keep UsageTap Meter selected for the first integration.
- Pick the provider already used by the application:
OpenAI, Anthropic, or OpenRouter. Keep the existing provider and
model.
@usagetap/sdk1.6.0 and newer preserve both Anthropic streaming forms:messages.create({ stream: true })and synchronousmessages.stream()with its event, cancellation, and final-message helpers. - Pick the implementation: UsageTap SDK, Without UsageTap SDK, or Python.
- Start with Minimal diff, then open Complete example when you need the surrounding request lifecycle.
- Create the keys shown beside the example and place them in your server environment.
- Copy the generated code, run one request, and confirm the call appears in the connection verification step.
- 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+jsonContent-Type: application/jsonwhen a request has a JSON bodyIdempotency-Keyfor 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:
- Prompt Compression playground
- Context Summarization Add to Code
- Sampling Add to Code
- Call Profiler
- UsageTap Gateway playground
- UsageTap Gateway setup
- SDK reference
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.
customerIdcomes from a trusted server-side identity.- Optional
customerUserIdcomes from trusted application identity, not request input. - Direct API calls send the required versioned
Acceptheader. - 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.