Skip to main content
Create a trial contract with an expiring credit, wire up alerts and webhooks, convert the customer to paid, and handle the variations you are most likely to need: ending a trial early, a recurring free tier, and a prepaid landing. Read Free trials first and complete its prerequisites, so that your custom-field keys are registered and your system notifications are enabled.

Set up the trial

Register custom-field keys (once)

Custom fields are how you mark a contract or credit as a trial, so that alerts, webhooks, and reports can tell trials apart from everything else a customer holds. Register each key once per environment before anything references it.

Create the customer

Create the trial contract — expiring credit

$100 of credit, valid 14 days, scoped to the products carrying the Language models product tag, and burned before anything else the customer holds (priority: 1).
To keep trial users out of specific products, add an overrides[] entry with "entitled": false for those product tags. The trial ends as soon as either limit is reached: the balance hits 0, or the access schedule reaches its ending_before date. If any balance is left when the schedule ends, Metronome expires the remainder and records an expiration entry on the credit’s ledger. That entry does not affect revenue, unlike the expiration of a prepaid commit — see revenue recognition for how each is treated. Note that the contract above has no end date, and only the credit is time-bounded. When the credit expires the contract carries on and usage is rated at your list prices, so the customer lands on pay-as-you-go with no further action from you. Create a separate paid contract with a transition when the paid terms differ from the trial contract’s, such as a different rate card, a prepaid commit, or a recurring free tier. The reference architecture covers the trade-off between the two shapes. To build one of the other patterns, start from the request above and change it as follows:
  • Skew toward time. Keep a modest amount and push ending_before further out, for products where adoption ramps slowly. Every access-schedule segment requires an ending_before, so a credit that never expires isn’t possible.
  • Effectively unlimited. Grant far more than a trial customer could plausibly consume and keep the window short. The customer never meets the cap, and you still get balance tracking, alerts, and a complete drawdown record to learn from.
  • Quantity-based instead of spend-based. Denominate the credit in a custom pricing unit rather than a currency by setting credit_type_id to that unit. The trial then grants an amount of units, such as tokens or requests, instead of dollars.
  • Time-only, uncapped. Remove the credits array and add a time-bounded zero multiplier in its place:
    Because there is no credit, this model produces no balance alerts and no credit.segment.end event. The override’s end date is your only trial-end signal. To limit exposure after the trial, consider a spend threshold on the paid contract.
  • Reverse trial. Keep the trial credit as-is, and add a recurring_credits[] item to the paid contract you renew into, so conversion lands the customer on a small permanent allowance. See recurring free tier.

Alerts

Threshold alerts are evaluated per customer, so create each one once and leave out customer_id to have it apply to everyone. Use alert_specifiers to evaluate only trial-tagged credits. Set a uniqueness_key so that retrying a request doesn’t create a duplicate alert.
A few things to keep in mind when creating these alerts:
  • evaluate_on_create: false skips only the initial evaluation. An alert that applies to all customers still fires for existing ones the next time their state changes.
  • The days-remaining alert fires as soon as you create it if the trial is shorter than its threshold.
  • alert_specifiers controls which credits are evaluated, but it is not echoed back in the webhook payload. Valid entity values are Contract, Commit, ContractCredit, and ContractCreditOrCommit. Using group_key_filter or per-key grouped specifiers requires account enablement, so contact Metronome support if you need them.

Handle the webhooks

Enable the credit.segment.start, credit.segment.end, credit.archive, and contract.end system notifications and point them at your webhook endpoint.
Use credit.segment.end as the trial-end signal. It fires at the segment boundary itself rather than after a later evaluation, and it carries credit, contract, and customer custom fields, so your handler needs no follow-up lookup.
Example payload:
The same event fires for three different reasons: the segment reached its end date, a conversion ended the trial early, or the credit was archived. Use the events that accompany it to tell them apart:
Alert payloads for reference:
Unlike the system notifications above, alert payloads do not include custom fields, contract_id, or environment_type, so look the credit up through the API when you need those. Of the three alerts, only the days-remaining one includes a credit_id, and only it has no timestamp. triggered_by names the evaluation trigger rather than the reason the threshold was crossed, so don’t branch on it.

Convert to paid

Create the paid contract with a transition from the trial contract. starting_at must be hour-aligned, and it is the instant the trial ends.
For a pay-as-you-go landing with a card on file, add a spend threshold to the paid contract to control post-trial risk. The transition has these effects:
  • The trial contract’s ending_before is set to the paid contract’s starting_at, leaving no gap and no overlap.
  • The trial credit is truncated to that instant and any unused balance expires. The ledger records a deduction for consumed usage and an expiration entry for the remainder, both stamped at the transition time. Nothing rolls over unless you set rollover_fraction.
  • Accrued trial usage is re-dated from the billing-period boundary to the transition instant, then invoiced once the grace period ends and the invoice finalizes.
  • You receive contract.edit when the paid contract is created, followed by credit.segment.end and contract.end at the transition instant.
  • Both contracts return the same transitions[] entry, holding type, from_contract_id, and to_contract_id. It has no timestamp, so read the effective time from contracts_transitions.date in Data Export.

End a trial early

You might need to end a trial before its window closes: the customer converts ahead of schedule and you don’t want free credit burning alongside paid usage, sales renegotiates the evaluation period, or a trial is being misused and you want to shut off the allowance. The cleanest way to do it is to truncate the credit’s access schedule. The credit keeps its ledger, Metronome emits credit.segment.end, and the $0 balance alert fires shortly afterward. Set ending_before to an hour boundary at or after the segment’s starting_at; a zero-length segment is accepted and expires the full balance immediately.
Note that update_credits[] identifies the credit with credit_id while the nested schedule item uses id. Both values come from POST /v1/contracts/customerBalances/list.
Avoid archiving a trial credit. Archiving zeroes the balance and deletes the credit’s ledger from both the API and Data Export, hides the credit unless you pass include_archived: true, and leaves it out of the in-app Commits and credits report. You are left with a grant that has no consumption history to reconcile against. Reserve archiving for cleanup where you want the grant to disappear entirely.

Recurring free tier

A recurring credit grants an allowance that refreshes every period, which suits both a permanent free tier and the landing state of a reverse trial. This example grants $10 of usage a month on a perpetual contract, with no rollover. recurrence_frequency is an enum string, and recurring credits do not accept custom_fields.
Metronome pre-generates two child credit segments, and the upcoming one contributes nothing to the balance until its period begins. Each child carries a recurring_credit_id, which webhook payloads call parent_recurring_credit_id. Setting recurrence_frequency explicitly anchors each period to the credit’s own starting_at, which means a period can reset partway through a billing period. Omit it to follow the contract’s usage statement schedule instead. Alerts mean something different on this model. A $0 or percentage-remaining alert now tells you that this period’s allowance is used up, which is a monthly throttling signal rather than the end of a trial, so keep a separate alert configuration for each model.

Prepaid landing with a payment gate

For a “buy $20 before you continue” conversion, add a payment-gated prepaid commit to the paid contract. Metronome creates the invoice immediately and releases the commit once payment succeeds. See that guide for the billing-provider setup and the rest of the requirements.
For auto-recharge once the customer is paying, add a prepaid balance threshold to the contract.

Behavior reference

How each trial-end path shows up

The $0 balance alert is a usage signal. Threshold notifications are evaluated when usage is ingested and when customer metadata changes, so a credit that reaches its end date with unused balance does not produce one. Use credit.segment.end to detect a trial that ends on schedule, and keep the $0 alert for trials exhausted by usage.

Payload differences between the two notification families

Things that are easy to get backwards

  • The percentage alert’s threshold is the percentage remaining, so a threshold of 25 fires when 25% is left.
  • Ledger entry types are named differently in each surface: CREDIT_EXPIRATION in the API is credit_segment_expiration in Data Export and in-app reports. See the database reference for the full mapping.
  • Usage drawdown appears as a single deduction entry per invoice, dated at the end of the service period and rewritten in place, so don’t use the ledger to date individual usage.
  • An invoice’s issued_at is its nominal date, while finalization happens after the grace period.