---
name: metronome-token-billing
description: Set up and verify Metronome Token Billing for an AI application through public APIs. Use when an agent needs to turn selected AI models and provider prices into a managed Metronome rate card, configure token metrics and products, apply markup or custom pricing units, provision a customer, integrate token usage reporting, validate the end-to-end Stripe and Metronome billing flow, and submit a sanitized end-of-run integration friction report to Stripe when there is concrete feedback. Do not use for unrelated Metronome pricing models.
---

# Set up Metronome Token Billing through APIs

Build one working integration in the merchant's chosen environment. Use public APIs for API-addressable configuration and use the Metronome UI only for user-authenticated flows such as OAuth. Treat this skill as the orchestration layer over the current [Token Billing guide](https://docs.metronome.com/guides/pricing-packaging/billing-model-guides/token-billing) and public API reference. Follow the linked schemas instead of copying generic object definitions into the integration.

Use the [agent-friendly documentation index](https://docs.metronome.com/llms.txt) to discover current Metronome documentation when the linked pages do not cover a required operation. Prefer the direct links below for known steps, and do not invent undocumented API behavior.

## Start a persistent run report

Treat a report path supplied in the invocation as an optional input. If it exists, read it first, verify its target and current state, and resume from its last checkpoint without repeating resolved questions or completed operations. If it does not exist, create it. When no path is supplied, create a uniquely named report in the operating system's temporary directory, such as `/tmp/metronome-token-billing-report.md`.

Use the report as the durable record and resumable state for the run and any follow-up promotion. Record local findings and evidence, inferred decisions, unresolved questions, the approved plan, target environment, pricing calculations, credential-binding validation without values, every operation, and the next safe action. Append corrections and later-environment results instead of overwriting earlier history.

For custom field keys, billable metrics, products, rate cards and rates, packages, customers, contracts, and webhooks, record the action, role, name, ID or stable key, alias, API result, and a verified dashboard link. If an item has no deep link or ID, link its parent or nearest dashboard list and identify it by its composite key.

Keep secrets and unnecessary customer data out of the report. Record failures, workarounds, and unclear documentation as they occur, then use the report as the source for the final friction log. State its path in the setup checkpoint and return it at handoff.

## Inspect, infer, and conduct one setup checkpoint

Inspect the application before asking setup questions. Discover its language and framework, billing ownership, identity model, persistence, existing API clients, LLM call sites, token-count sources, retries, and background-job or outbox patterns. Do not ask for information that the repository or read-only remote state can answer.

Check whether the required environment-variable or approved secret-manager bindings exist without reading their values. Use each available credential for its own read-only discovery and validation; do not require both credentials before doing the discovery that one can support.

Infer concrete defaults from repository conventions and accessible Metronome and Stripe state. Choose names, stable aliases, identity mappings, SDK or HTTP usage, event hooks, transaction IDs, delivery behavior, retries or existing job infrastructure, error handling, and object reuse instead of asking the user to choose implementation tactics. Clearly distinguish inferred choices from facts.

Fit the run to this canonical integration unless the user explicitly requested otherwise: USD pay-as-you-go token usage at current provider prices plus one markup policy, one managed rate card, Metronome invoice delivery to Stripe, one verification customer with a direct contract, and server-side usage reporting that follows the application's existing conventions. Do not solicit decisions about packages, recurring fees, included credits, prepaid balances, top-ups, thresholds, custom pricing units, ordinary products, broad customer rollout, or alternative delivery architectures. Those are separate billing-design work outside this skill's normal scope.

After discovery, send exactly one blocking setup checkpoint. Begin with: "I've finished discovery. This is the only setup checkpoint; after your reply, I'll build and verify the integration."

Keep it concise and include:

- the report path;
- the target, defaulting to sandbox unless the user clearly selected production;
- the models and providers inferred from the application;
- the canonical billing and usage-integration shape the agent will implement;
- the credential-binding and validation status; and
- the exact remote objects to create or reuse and the application changes to make.

Use these sections in order:

1. **`### Requirements`** — Use a compact table with `Requirement`, `Status`, and `How to set it up` columns. Always include the target Metronome account, intended Stripe account and mode, `METRONOME_BEARER_TOKEN`, `STRIPE_SECRET_KEY`, and the Metronome-to-Stripe account connection. Mark each as ready, missing, invalid, or requiring a browser connection. For anything not ready, give one short action and link to the relevant token, Stripe key, or Metronome integrations page. Explain that the two environment variables authenticate API work, while the browser connection separately authorizes Metronome to deliver invoices to Stripe. Never ask the user to paste a secret.
2. **`### Billing decisions`** — Use a compact table with `Decision`, `Default`, and `Alternative` columns for the target environment, inferred billable models, USD pricing, Stripe invoice delivery, and any other genuinely merchant-owned choice already surfaced by discovery. Put one decision in each row, give only one meaningful alternative, and omit irrelevant choices. Immediately below it, ask `**Token markup — Required:** What markup percentage should apply to the selected models and models added later?` Do not supply a default or recommendation.
3. **`### Integration defaults`** — Use a compact table with `Category`, `Default`, and `Alternative` columns for inferred implementation choices such as scope, rate card, contract, customer identity, and usage delivery. Bold category names, put one category in each row, and give only one short meaningful alternative. Do not enumerate every possible permutation.
4. **`### Planned changes`** — List the remote objects and application changes concisely.
5. **`### Ready to proceed`** — Tell the user they need only supply the markup, state any corrections, and say "Proceed." Make clear that the build begins after that reply.

Assume Metronome will deliver invoices to Stripe. Do not ask the user to choose an invoice destination unless they explicitly challenge that default; treat opting out as a correction rather than a required answer.

After the reply, record the answers and approval, then build without another planning round. Do not ask the user to confirm implementation details already inferred. Ask another question only when new remote state, failed validation, or a material safety blocker makes the approved plan impossible; interactive authentication or consent may still require participation at the identified browser handoff.

Markup is exclusively the merchant's choice. Never infer, prefill, exemplify, or recommend a percentage, and never describe one as typical, standard, safe, or preferred. Unless the user specifies an exception, apply their single markup answer to every selected model and to models added later. Ask another markup question only to resolve an exception the user identifies. Markups must produce a positive price. Use one USD-denominated rate card; Token Billing does not support another fiat currency.

## Bootstrap and validate credentials

Credential setup belongs in the single setup checkpoint, not in a surprise follow-up after approval. Reuse an existing approved server-side binding when possible without reading or copying its value.

- Require `METRONOME_BEARER_TOKEN` before Metronome discovery or writes. It must target the selected environment and permit the operations in the approved plan. If absent, include [create a Metronome API token](https://docs.metronome.com/api-reference/authentication) in the checkpoint.
- Require `STRIPE_SECRET_KEY` before fetching the model catalog. It must be a secret API key for the intended Stripe account and mode, never a publishable key. If absent, include [manage Stripe API keys](https://docs.stripe.com/keys) in the checkpoint.

Tell the user to expose missing credentials through the agent's environment or secret settings and respond when ready, never to paste values into chat. If the running agent cannot inherit variables added afterward, write the resolved decisions and checkpoint to the report, state its path, and tell the user how to restart from the configured environment and resume from that report without repeating discovery.

Do not print or inspect secret values, enable shell tracing, place literal keys in tool calls, extract a newly created key from the browser, or create or modify a secret file unless the user explicitly requests it and confirms that the file is excluded from source control.

Validate each credential with its first required read-only request: list Metronome custom-field keys and fetch Stripe's model catalog. When a binding was already available, perform this validation during discovery so its result can appear in the checkpoint. Reference only environment-variable names in commands. Treat `401` or `403` as a credential or permission failure and stop before writes. Record only the binding name, selected environment and mode, and validation result in the run report.

Verify that the credentials match the selected target before mutating it. Default to sandbox when the user has not clearly selected an environment. For production changes, include the exact planned writes in the setup checkpoint and obtain explicit approval; never silently switch a sandbox run to production. If the user declines or cannot provide a required credential, offer a dry-run plan and safe local application scaffolding, but do not create remote objects or claim authoritative prices.

## Connect Stripe for invoice delivery

The Stripe key used to read model prices does not connect the Stripe and Metronome accounts or route invoices. For the canonical integration, identify the key's Stripe account with a read-only Stripe account request, then call `POST /listConfiguredBillingProviders` before provisioning customers. Reuse a Stripe delivery method only when its `stripe_account_id` matches that account and the user-selected target. Skip Stripe invoice routing only when the user explicitly opted out.

If no matching connection exists, assist with the browser flow described in [Invoice with Stripe](https://docs.metronome.com/integrations/invoice-integrations/stripe):

1. Confirm the target Metronome environment and intended Stripe account and mode.
2. With browser capabilities, open `https://app.metronome.com/sandbox/developer/integrations` for sandbox or `https://app.metronome.com/developer/integrations` for production and navigate to the Stripe connection action. Without browser capabilities, give the user the corresponding link and wait.
3. Have the user authenticate, select the Stripe account, complete any two-factor challenge, review the requested access, and grant consent. Never ask for credentials, verification codes, or recovery codes.
4. Start from the Metronome integrations page; do not construct a Stripe authorization URL or capture its authorization code. Metronome generates the OAuth state, selects the environment-specific client ID and redirect URI, and handles the callback.
5. After the callback, call `POST /listConfiguredBillingProviders` again. Verify the returned `stripe_account_id`, record its non-secret `delivery_method_id` and dashboard link, and stop on a mismatch or failed connection.

OAuth establishes the account-level connection only. Complete the customer and contract routing in the customer-integration section. Do not substitute a second Stripe Subscription for an existing recurring entitlement.

## Orchestrate the managed rate card

Use `https://api.metronome.com/v1` for Metronome requests unless the user supplies another API base URL. Before every create call, list or retrieve existing objects and reuse only an exact semantic match. Persist every returned ID so a retry can resume after a partial failure.

### 1. Read and normalize Stripe's model catalog

Request `GET https://llm.stripe.com/v1/models` with the Stripe secret key as Bearer authentication and `Accept: application/json`. Use a 10-second timeout and retry only transient network failures, `429`, and `5xx` responses, with at most three attempts and backoff.

For each model in `data`:

1. Require non-empty `id`, `model`, `author`, and at least one endpoint with `provider` and a supported current price.
2. Accept only `input`, `output`, `cached_input`, and `cached_write` `usage_type` values.
3. For each `(model id, provider, usage type)`, select the price with the latest `valid_from` not later than the fetch time. Treat a missing `valid_from` as currently effective but older than a timestamped price. Ignore scheduled future prices and unknown usage types.
4. Interpret `unit_amount_major_units` as USD per individual token. Preserve it as a decimal; do not round through binary floating point.
5. Keep only the model IDs the application will expose, but calculate the snapshot watermark from the full supported catalog. Use the greatest supported `valid_from`, capped at the fetch time; use the fetch time when no supported price has a timestamp.

Stop if a selected model disappeared, has no supported current prices, or the response shape changed. Show the selected model, provider, token type, base USD price, markup, denomination, and resulting rate before writing Metronome configuration.

Use the exact catalog snapshot the user reviewed and approved for the write phase; do not silently refetch prices or add models during submission. If a refetch is necessary, recompute the proposal and reconfirm changed writes.

### 2. Ensure the managed custom-field keys

Use the [Custom fields API](https://docs.metronome.com/api-reference/custom-fields) to list keys, then create only missing keys with `enforce_uniqueness: false`. These exact keys are the Token Billing managed-sync contract:

| Entity             | Key                          | Value on creation                                   |
| ------------------ | ---------------------------- | --------------------------------------------------- |
| `rate_card`        | `ai_managed`                 | `"true"`                                            |
| `rate_card`        | `ai_managed_last_updated_at` | catalog snapshot watermark as an ISO 8601 timestamp |
| `rate_card`        | `default_ai_markup`          | default markup as a decimal fraction                |
| `contract_product` | `ai_managed`                 | `"true"`                                            |
| `contract_product` | `ai_markup`                  | that model's markup as a decimal fraction           |

Store percentages as decimal fractions by dividing the user-supplied percentage by 100. Write the common markup to every current product's `ai_markup` and to the rate card's `default_ai_markup`. The rate-card field persists the policy for products added later by managed sync; it is not a separate business decision unless the user specifies a future-model exception. A product's field applies to that model and token type. Use the exact entity, key, casing, and string value; otherwise the object will not participate correctly in managed updates.

Store `"0"` when the user intentionally leaves the optional default markup blank.

### 3. Ensure one shared billable metric per token type

For every token type present in the selected catalog rows, find an active billable metric with the exact semantic definition below. Reuse it even if its display name differs. Otherwise create it with the [Create billable metric API](https://docs.metronome.com/api-reference/billable-metrics/create-a-billable-metric).

| Token type     | Suggested name           | Aggregation key       |
| -------------- | ------------------------ | --------------------- |
| `input`        | `AI input tokens`        | `input_tokens`        |
| `output`       | `AI output tokens`       | `output_tokens`       |
| `cached_input` | `AI cached input tokens` | `cached_input_tokens` |
| `cached_write` | `AI cached write tokens` | `cached_write_tokens` |

With the public REST API, use:

- `event_type_filter: {"in_values": ["token-billing"]}`;
- `aggregation_type: "SUM"` and `aggregation_key` set to the corresponding token property;
- `property_filters` containing `{name, exists: true}` for `model`, `provider`, and the aggregation key;
- `group_keys: [["model", "provider"]]` in that order.

Do not create one metric per model. Compare the complete filter, aggregation, aggregation key, and group-key structure before reusing a metric.

### 4. Create one usage product per model and token type

For every selected `(model, token type)` with at least one provider price, create a [usage product](https://docs.metronome.com/api-reference/products/create-a-product). All providers for the same model and token type share this product.

Set the following public API fields:

- `name` to `<model display name> <token type label> tokens`;
- `type` to `USAGE`;
- `billable_metric_id` to the shared metric for that token type;
- refundable behavior to its default of `true`;
- `tags` to include `ai_managed`;
- `pricing_group_key` to `["model", "provider"]`;
- `quantity_conversion` to divide by `1000000`, named `million tokens`;
- `custom_fields` to `{"ai_managed": "true", "ai_markup": "<fraction>"}` using the model markup decimal fraction.

If resuming a partial run, recover a product mapping from an existing managed rate's model pricing-group value and the product's token-type metric. Do not reuse an unreferenced product by display name alone because names are not unique.

### 5. Create the rate card

Use the [Create rate card API](https://docs.metronome.com/api-reference/rate-cards/create-a-rate-card) with:

- the chosen name and alias;
- USD (cents), ID `2714e483-4ff1-48e4-9e25-ac732e8f24f2`, as `fiat_credit_type_id`;
- an optional conversion containing the existing custom pricing unit ID and its positive `fiat_per_custom_credit`;
- the three rate-card custom fields from step 2.

The UI asks for the value of one custom unit in major USD, but the public rate-card API expresses `fiat_per_custom_credit` in the rate card's fiat unit. Because this rate card uses USD cents, multiply the user-facing USD value by `100` for the API payload. For example, `$0.50` per credit becomes `50`. Record both values to prevent unit confusion.

Custom pricing units currently must already exist in the target account. List them with `GET /credit-types/list`. If the requested unit is absent and the current public API still has no create operation, stop and report that API gap; do not click through the UI or silently switch the billing model to USD.

### 6. Calculate and add rates

For every normalized `(model, provider, token type)` row, use the product for `(model, token type)` and calculate:

```text
marked_up_usd_per_token = unit_amount_major_units * (1 + model_markup_fraction)
usd_cents_per_million = marked_up_usd_per_token * 1_000_000 * 100
api_fiat_per_custom_credit = usd_major_per_custom_credit * 100
custom_units_per_million = usd_cents_per_million / api_fiat_per_custom_credit
```

Add the rates with the [Add rates API](https://docs.metronome.com/api-reference/rate-cards/add-a-rate). Set `rate_type: "FLAT"`, `entitled: true`, `price` to the calculated per-million amount, `pricing_group_values` to the exact catalog `model` ID and endpoint `provider`, and `credit_type_id` to USD or the selected custom pricing unit. Mirror the managed onboarding flow's initial `starting_at` value: `2025-01-01T00:00:00.000Z`.

Use decimal arithmetic and preserve precision. Rates in USD are expressed in cents; rates in a custom pricing unit are expressed in that unit. The product's divide-by-one-million conversion is why the rate is a per-million-token amount.

Before retrying `addRates`, retrieve the rate schedule and skip an existing semantic rate. Do not add duplicate `(product, model, provider, starting_at)` entries.

If the user selected ordinary usage, subscription, or composite products for this rate card, add their approved rates through the normal API in the same setup. Do not give those products Token Billing tags or managed fields. Every AI-managed rate must share one denomination; other products may use the rate card's fiat or any custom unit with a valid conversion.

## Complete the customer integration

Use existing docs for the remaining generic objects and application work:

1. For the canonical integration, skip packages and create a contract directly against the rate card. Create a [package](https://docs.metronome.com/api-reference/contracts/create-a-package) only when the user explicitly requested reusable contract terms or credits; if it should invoice through Stripe, set its billing provider to Stripe and its delivery method to direct billing-provider delivery.
2. Create or reuse a [customer](https://docs.metronome.com/api-reference/customers/create-a-customer) using a stable app-to-Metronome mapping. Do not use email as the durable identity.
3. If Metronome sends invoices to Stripe, create or reuse the Stripe Customer in the verified connected account. Set its ID and chosen collection method on the Metronome customer, either during customer creation or with the [customer billing-provider configuration API](https://docs.metronome.com/api-reference/customers/set-billing-provider-configurations-for-a-customer), using the matching `delivery_method_id`. Fetch the resulting configuration and persist its ID.
4. Create the [contract](https://docs.metronome.com/api-reference/contracts/create-a-contract). For a direct contract, explicitly attach the customer billing-provider configuration ID when Stripe invoice delivery is in scope. A package-provisioned contract cannot accept that customer-specific ID; it resolves the package's provider and delivery method to exactly one active customer configuration. Stop rather than guess if more than one matches. Preserve any existing Stripe Subscription that owns recurring entitlement; do not create a competing subscription.
5. Follow the Token Billing guide's [usage event format](https://docs.metronome.com/guides/pricing-packaging/billing-model-guides/token-billing#integrate-usage-tracking). Report the resolved model, provider, and authoritative token counts after routing or fallback. Reuse one stable `transaction_id` on retries and send through a durable outbox when possible. Follow [Send usage events](https://docs.metronome.com/guides/events/send-usage-events) for delivery behavior.
6. Configure lifecycle webhooks using [Set up webhooks](https://docs.metronome.com/guides/platform-configuration/setup-webhooks). If the current public API cannot create an endpoint, report the gap and provide the user the documented prerequisite; do not drive the UI.

## Validate before handoff

Retrieve the created objects and prove:

- every managed field key exists on the correct entity;
- each required token type has one exact shared metric;
- every selected model/token pair has one managed product with its markup;
- every selected model/provider/token row has one flat rate in a single shared rate denomination;
- the rate card uses USD as fiat and has a conversion for any custom rate denomination;
- any Stripe-routed customer points to the intended Stripe Customer and delivery method, and the contract selects that customer billing-provider configuration;
- one controlled event in the target environment matches the intended metric and appears under the intended customer and contract;
- rerunning discovery and provisioning creates no duplicate objects or usage.

The hourly managed sync discovers rate cards from `rate_card.ai_managed = "true"`. It uses `ai_managed_last_updated_at` as its catalog watermark, `default_ai_markup` (or zero when absent) for newly created managed products, and product `ai_markup` with fallback to the default for rates on an existing managed product. It considers supported catalog rows with `valid_from` later than the watermark only when the model-author prefix before `/` already appears on the rate card. It adds new model, provider, or token-type rate keys at the sync hour and advances the watermark even when no eligible rows are added; author opt-in is therefore not retroactive. Existing `(model, token type, provider)` rate keys are skipped, so current provider-price changes are not automatically applied to an existing key.

The sync expects managed product tag `ai_managed`, product field `ai_managed = "true"`, shared token-type metrics, pricing group keys in `model`, `provider` order, one rate credit type across AI products, and a rate-card conversion when that type is custom. A legacy per-model metric layout is not syncable.

Return the report path, chosen pricing decisions, non-secret object IDs, price calculations, test evidence, remaining API or OAuth prerequisites, and a concise friction log. In production, keep any broader customer rollout separate from the explicitly approved configuration and controlled verification performed by this run.

## Submit integration feedback

As the final external action before handoff, submit any concrete integration feedback or friction points from the run report to Stripe. Do not submit an empty or generic entry when the report contains no useful feedback; record that no feedback was submitted instead. Sanitize feedback before submission. Exclude secrets, customer data, account or object IDs, local paths, and other run-specific identifiers. Choose a `positive`, `neutral`, or `negative` sentiment to represent the report accurately, and submit exactly one form-encoded `POST` to `https://api.stripe.com/v1/_unstable/feedback` with:

- `Authorization: Bearer ${STRIPE_SECRET_KEY}` without resolving, printing, or copying the value;
- `Stripe-Version: 2026-08-26.dahlia`;
- `sentiment` set from the report;
- `message` beginning with the exact searchable prefix `Metronome token billing skill for <merchant id>` and containing a report that explains what the integration was trying to accomplish and any issues or friction points;
- `feature_area=skills`, `channel=cli`, and `actor=agent`.

Use only the already validated `STRIPE_SECRET_KEY`; do not create, retrieve, or request another credential solely for feedback. Before sending, store a stable, non-sensitive idempotency key in the run report and include it as the `Idempotency-Key` header. On resume, do not submit again if the report already contains a successful feedback ID.

Treat a returned `fbk_` ID and `success: true` as success and record the ID in the run report. The endpoint accepts test-mode secret keys and appropriately permissioned restricted or agent credentials; an ordinary live-mode secret key can receive a deliberate `404`. Record a sanitized status and error category for a failed submission, do not retry a `4xx`, and do not let feedback submission change the integration's validation result. Retry only an unambiguously transient network error, `429`, or `5xx`, using the same idempotency key and at most the existing three-attempt retry policy.

## Offer relevant follow-up work

At handoff, offer concise, copy-ready prompts for relevant next steps. Substitute the actual report path and omit inapplicable prompts. If this run configured sandbox, always offer this production-promotion prompt:

```text
Use the Metronome Token Billing skill to promote the sandbox integration documented in <actual-report-path> to production. Treat that report as resumable state, inspect production independently, ask me interactively for unresolved production decisions and credentials, present the exact production writes for approval, and append production object IDs, dashboard links, validation evidence, and friction notes to the same report.
```

Do not describe promotion as copying sandbox IDs or silently reuse sandbox credentials. Reconfirm environment-specific resources, prices, integrations, and approvals before production writes. If the report is in temporary storage that may not survive the follow-up environment, offer to copy it first to a user-approved persistent path and use that path in the prompt; do not place it in the repository without approval.
