# Build a usage-based subscription model

**Meter what your customers consume, report it through the API, and bill for actual usage at cycle end.**

Usage-based pricing bills for consumption rather than a flat fee: API calls, gigabytes stored, messages sent. Your integration meters the consumption, reports it to Revolut as it happens, and we aggregate the reports and charges the saved payment method after each cycle closes.

Unlike [per-seat pricing](/docs/guides/merchant/billing-subscriptions/api/per-seat), where a `latest`-aggregated usage item tracks a single gauge, a metered plan typically accumulates many small reports per cycle - and both the pricing and the reporting need a bit more configuration.

In this guide, you sell an **API usage plan** - a £29.00 monthly platform fee plus metered API calls, billed after each cycle closes. Customers pick how the calls are priced: £0.10 per call, or a tier ladder that starts free and gets cheaper as volume grows.

The example demonstrates how to model metered pricing on a single plan: a `flat` item carries the platform fee, and each **variation** prices the same `api_calls` meter differently - per-unit on one, graduated on the other. Map your own pricing the same way - every consumption metric becomes a usage item, and each pricing style becomes a variation.

## How it works

This guide walks the full flow: all API calls run between your backend and the Merchant API, and only [step 3](#3-collect-first-payment) - collecting the first payment - reaches the customer's browser.

1. **Create a usage-based plan** - model each meter as a `usage` item with an aggregation method and a price - per-unit or a tier ladder - sharing its phase with a flat platform fee.
2. **Subscribe the customer** - create the subscription, which starts in `pending` state until the first payment is collected.
3. **Collect the first payment** - retrieve the setup order, hand the customer a payment widget or Revolut's Hosted Checkout Page, and confirm the subscription is `active`.
4. **Report and settle usage** - report consumption through the cycle, correct records while it's open, and Revolut charges the aggregated total after the cycle closes.

The aggregation method on each usage item decides how reports become a charge:

| Method | Charges | Business example |
|--------|---------|------------------|
| `sum` | The total of all reported values in the cycle | API calls, data transferred, messages sent |
| `latest` | The most recently reported value | Active seats - see [Per-seat subscription](/docs/guides/merchant/billing-subscriptions/api/per-seat) |
| `max` | The highest reported value | Peak concurrent connections |

Because consumption accrues through the cycle, the charge lands at the end of it: the cycle closes at its `end_date`, reports and corrections stay open for another 12 hours - the `usage_cutoff_date` - and once it passes, Revolut aggregates each item's records, applies the per-unit price or tier ladder, and charges the saved payment method through a post-billing order.

The API usage plan you'll build in this guide looks like this:

```mermaid
flowchart TD
    subgraph plan ["API usage plan"]
        subgraph payg ["Pay-as-you-go variation"]
            subgraph phase1 ["Phase 1 - monthly cycle"]
                subgraph items1 ["Subscription items"]
                    fee1["Platform fee<br>flat item - £29.00"]
                    calls1["API calls<br>usage item - sum x £0.10 per call"]
                end
            end
        end
        subgraph tiers ["Volume-tiers variation"]
            subgraph phase2 ["Phase 1 - monthly cycle"]
                subgraph items2 ["Subscription items"]
                    fee2["Platform fee<br>flat item - £29.00"]
                    calls2["API calls<br>usage item - sum x tier ladder<br>first 1,000 calls free"]
                end
            end
        end
    end
```

### Before you begin

Before you start, make sure you have the following:

- [ ] You've completed **[Get started with the Subscriptions API](/docs/guides/merchant/billing-subscriptions/api/get-started)** - it introduces the universal subscription flow this guide builds on
- [ ] An **existing customer** - see [Create a customer](/docs/api/merchant#create-customer)
- [ ] A way to **meter consumption** on your side - the API records what you report, it doesn't measure anything itself
- [ ] Familiarity with the **core subscription concepts** - see [Subscription plans](/docs/guides/merchant/billing-subscriptions/subscription-plans) and [Subscription lifecycle](/docs/guides/merchant/billing-subscriptions/subscription-lifecycle)

---

## Implement usage-based subscription

The steps below follow the API usage plan narrative: you create the plan with its two pricing styles, subscribe a customer to the pay-as-you-go variation, collect the first payment, and confirm the subscription is `active`.

### 1. Create plan for usage-based pricing

Your API usage plan sells the same meter two ways. Each variation is a single phase, and the charges live on items in the phase: a `flat` item carries the £29.00 platform fee, and a `usage` item prices the `api_calls` meter.

The usage item differs per variation - £0.10 per call on pay-as-you-go, a graduated ladder on volume tiers. Every usage item needs an aggregation method and a price, and the per-unit `amount` and `tiers` are mutually exclusive - the schema rejects both.

:::info
For the plan, variation, and phase hierarchy these items sit in, see [Get started with the Subscriptions API](/docs/guides/merchant/billing-subscriptions/api/get-started).
:::

Call [Create a subscription plan](/docs/api/merchant#create-subscription-plan), turning your metered pricing into a plan your customers can subscribe to:

- ![Request]
  ```http [Request example]
  POST /api/subscription-plans HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  Content-Type: application/json

  {
    "name": "API usage plan",
    "variations": [
      {
        "phases": [
          {
            "ordinal": 1,
            "cycle_duration": "P1M",
            "subscription_items": [
              {
                "type": "flat",
                "name": "Platform fee",
                "unit": "subscription",
                "quantity": 1,
                "amount": 2900,
                "currency": "GBP"
              },
              {
                "type": "usage",
                "name": "API calls",
                "unit": "call",
                "code": "api_calls",
                "usage_aggregation_method": "sum",
                "amount": 10,
                "currency": "GBP"
              }
            ]
          }
        ]
      },
      {
        "phases": [
          {
            "ordinal": 1,
            "cycle_duration": "P1M",
            "subscription_items": [
              {
                "type": "flat",
                "name": "Platform fee",
                "unit": "subscription",
                "quantity": 1,
                "amount": 2900,
                "currency": "GBP"
              },
              {
                "type": "usage",
                "name": "API calls",
                "unit": "call",
                "code": "api_calls",
                "usage_aggregation_method": "sum",
                "currency": "GBP",
                "tiers": [
                  {
                    "upper_quantity_threshold": 1000,
                    "amount": 0
                  },
                  {
                    "upper_quantity_threshold": 10000,
                    "amount": 10
                  },
                  {
                    "amount": 5
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `name` | The plan name - what your customers subscribe to |
  | `variations` | Purchasing options for the plan - here, two pricing styles for the same meter |
  | `phases` | Sequential pricing stages within a variation, executed in `ordinal` order |
  | `ordinal` | Execution order of the phase, starting at `1` |
  | `cycle_duration` | Length of the billing cycle, as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) duration, e.g., `P1M` |
  | `subscription_items` | The charges on the phase - here, a platform fee plus a metered item per variation |
  | `subscription_items[].type` | `flat` bills a fixed amount every cycle; `usage` bills consumption you report |
  | `subscription_items[].quantity` | Units billed on a `flat` item - the platform fee bills `amount * quantity`, here 1 x £29.00 |
  | `subscription_items[].unit` | What the item bills - a free-form label, `call` for the meter here |
  | `subscription_items[].package_size` | Units grouped into one billable package - optional, defaults to `1`; leave it at `1` to price every unit |
  | `subscription_items[].code` | Merchant-defined identifier for a `usage` item - unique within the phase; you supply it when reporting usage |
  | `subscription_items[].usage_aggregation_method` | How reported values become the charge - `sum` adds them all up at cycle end |
  | `subscription_items[].amount` | Per-unit price on a `usage` item, in minor units - `10` is £0.10 per call. Mutually exclusive with `tiers` |
  | `subscription_items[].currency` | Charge currency, as an [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code |
  | `subscription_items[].tiers` | Graduated pricing ladder - mutually exclusive with `amount` |
  | `subscription_items[].tiers[].upper_quantity_threshold` | Exclusive upper bound of the tier - omit it or set `null` for an unlimited final tier |
  | `subscription_items[].tiers[].amount` | Per-unit price within the tier's range, in minor units |

- ![Response]
  The response returns the plan with a system-generated `id`, an `id` for each variation and phase, and an `id` for each item.

  Save the `id` of the variation the customer subscribes to - here, the pay-as-you-go variation for the walkthrough.

  ```json [Response example] {9}
  {
    "id": "d71d7498-ba17-4413-b973-f6a1bf55eb62",
    "name": "API usage plan",
    "state": "active",
    "created_at": "2026-01-26T08:59:09.433527Z",
    "updated_at": "2026-01-26T08:59:09.433527Z",
    "variations": [
      {
        "id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
        "phases": [
          {
            "id": "f8581f2b-2e00-4938-a527-7112ae35a50e",
            "ordinal": 1,
            "cycle_duration": "P1M",
            "subscription_items": [
              {
                "id": "823c7f33-5a7e-4eda-b079-5b5b468150da",
                "type": "flat",
                "name": "Platform fee",
                "unit": "subscription",
                "quantity": 1,
                "amount": 2900,
                "currency": "GBP"
              },
              {
                "id": "9cb9cfe9-173d-4ad2-9581-8b2705e03fcc",
                "type": "usage",
                "name": "API calls",
                "unit": "call",
                "code": "api_calls",
                "usage_aggregation_method": "sum",
                "amount": 10,
                "currency": "GBP"
              }
            ]
          }
        ]
      },
      {
        "id": "8c90d4de-1e79-4eb7-a5ff-158cdfba18fa",
        "phases": [
          {
            "id": "b726da3a-392a-417a-9c5c-fcf0ad6cbfd8",
            "ordinal": 1,
            "cycle_duration": "P1M",
            "subscription_items": [
              {
                "id": "39b46449-e46b-4da4-89b7-d3b7945a19b2",
                "type": "flat",
                "name": "Platform fee",
                "unit": "subscription",
                "quantity": 1,
                "amount": 2900,
                "currency": "GBP"
              },
              {
                "id": "d82da951-608f-4666-8ba4-c72e946527b6",
                "type": "usage",
                "name": "API calls",
                "unit": "call",
                "code": "api_calls",
                "usage_aggregation_method": "sum",
                "currency": "GBP",
                "tiers": [
                  {
                    "upper_quantity_threshold": 1000,
                    "amount": 0
                  },
                  {
                    "upper_quantity_threshold": 10000,
                    "amount": 10
                  },
                  {
                    "amount": 5
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `id` | The plan ID - the plan's identifier in the API |
  | `state` | `active` - the plan is ready to accept subscriptions |
  | `variations[].id` | The variation IDs - save the one the customer subscribes to; you'll use it in [step 2](#2-subscribe-customer) |
  | `phases[].id` | The phase IDs - each billing cycle is created under one of them |
  | `subscription_items[].id` | The item IDs - generated for each item you configured |

In terms of the API's fields and types, a usage item and its optional tier ladder look like this - the delta this model adds to the plan structure:

```mermaid
erDiagram
    subscription_item {
        id uuid PK
        type string "usage"
        name string
        unit string "what the item meters - call"
        code string "merchant-defined - targets usage reports"
        usage_aggregation_method string "sum | latest | max"
        amount integer "per-unit price - exclusive with tiers"
        currency string
        package_size number "units per billable package - optional, defaults to 1"
        tiers array "usage items only - exclusive with amount"
    }
    tier {
        upper_quantity_threshold number "exclusive upper bound - omitted or null means unlimited"
        amount integer "per-unit price within the tier's range"
    }
    subscription_item ||--o{ tier : "tiers"
```

The usage flavour carries fields a flat item never has - `code`, `usage_aggregation_method`, and `tiers` - while `quantity` exists only on flat items.

How the same meter prices under the two styles - say the customer makes 4,500 calls in a cycle:

| Pricing style | Usage charge for a 4,500-call cycle |
|---------------|-------------------------------------|
| Pay-as-you-go | 4,500 * £0.10 = £450.00 |
| Volume tiers | First 1,000 calls free, next 3,500 * £0.10 = £350.00 |

Both sit on top of the cycle's £29.00 platform fee.

A phase can carry several usage items - `api_calls` and `storage_gb`, say - each with its own aggregation method and price. The `code` you set on each item is what targets the right meter when you report usage.

### 2. Subscribe customer

When a customer wants to subscribe to your **API usage plan**, you create a subscription. The subscription connects the customer - and their consent to use their payment details for recurring charges - to the variation they chose: the pay-as-you-go variation in this guide.

Subscription creation is the same for every pricing model - the pricing lives entirely in the plan you built. Once created, the subscription starts in `pending` state - it becomes `active` once the customer completes the first payment.

Call [Create a subscription](/docs/api/merchant#create-subscription) to connect the customer to the pay-as-you-go variation. Send an idempotency key, so you can safely retry the request without creating duplicate subscriptions:

- ![Request]
  ```http [Request example] {9}
  POST /api/subscriptions HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  Content-Type: application/json
  Idempotency-Key: f5e4ba66-6465-4015-b3b4-966598ee41a5

  {
    "plan_variation_id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
    "customer_id": "650e8400-e29b-41d4-a716-446655440001",
    "external_reference": "api_7c5d21",
    "setup_order_redirect_url": "https://example.com/subscription/complete"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `plan_variation_id` | The variation the customer subscribes to - the pay-as-you-go variation `id` you saved in [step 1](#1-create-plan-for-usage-based-pricing) |
  | `customer_id` | The customer to bill |
  | `external_reference` | Optional: your own identifier for the subscription, e.g., the customer's ID in your system - returned in responses and echoed in webhook events |
  | `setup_order_redirect_url` | Optional: where the customer lands after completing payment on the hosted checkout page |

- ![Response]
  The subscription is created in `pending` state. Save the `setup_order_id` - [step 3](#3-collect-first-payment) collects the first payment with it - and the subscription `id` - [step 4](#4-verify-subscription-state) confirms activation with it, and the [Operate section](#operate-usage-based-subscription) uses it for usage reporting, billing cycles, and cancellation.

  ```json [Response example] {2,11}
  {
    "id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
    "external_reference": "api_7c5d21",
    "state": "pending",
    "customer_id": "650e8400-e29b-41d4-a716-446655440001",
    "plan_id": "d71d7498-ba17-4413-b973-f6a1bf55eb62",
    "plan_variation_id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
    "payment_method_type": "automatic",
    "created_at": "2026-01-26T09:15:00.036001Z",
    "updated_at": "2026-01-26T09:15:00.036001Z",
    "setup_order_id": "95061a11-a396-473b-989c-3f3c27ec707a",
    "current_cycle_id": "6f68ad11-6ce5-4646-b03b-1a4b8569d991"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `id` | The subscription ID - used to report usage, retrieve the billing cycles, and cancel the subscription |
  | `state` | `pending` - the subscription becomes `active` once the customer completes the first payment |
  | `setup_order_id` | The setup order collecting the first payment - [step 3](#3-collect-first-payment) retrieves the order with it |
  | `current_cycle_id` | The cycle currently billing the subscription - retrieve it for entitlement checks in [Retrieve billing cycles](#retrieve-billing-cycles) |

:::note[The first payment precedes any usage]
The setup order collects the first payment before any usage is reported - usage items contribute nothing until your reports land in the cycle.

On this plan, the first charge is the £29.00 platform fee. Make sure your metering starts reporting as soon as the subscription is `active`, so the first cycle settles with the real consumption.
:::

### 3. Collect first payment

In the previous step you created the subscription. It is in `pending` state, and becomes `active` once the customer completes the first payment. That payment is collected through a **setup order** - an order that exists to start a subscription.

Revolut creates the setup order in the background when you create the subscription: it takes the first charge - £29.00 for the platform fee - and saves the customer's payment method for the recurring cycles that follow. You already saved its `setup_order_id`.

#### 3.1 Retrieve setup order

Retrieve the setup order with [Retrieve an order](/docs/api/merchant#retrieve-order), passing the `setup_order_id` you saved in [step 2](#2-subscribe-customer) as the `order_id`:

- ![Request]
  ```http [Request example]
  GET /api/orders/{order_id} HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `order_id` | The ID of the setup order you saved in [step 2](#2-subscribe-customer) |

- ![Response]
  The response returns the setup order. The fields that unlock the checkout approaches below are - `token` for embedding a payment widget on your site and `checkout_url` for Revolut's Hosted Checkout Page.

  ```json [Response example] {3,9}
  {
    "id": "95061a11-a396-473b-989c-3f3c27ec707a",
    "token": "12fed5eb-636a-4ca2-a1e8-ffe3bce0ced1",
    "state": "pending",
    "created_at": "2026-01-26T09:15:00.036001Z",
    "updated_at": "2026-01-26T09:15:00.036001Z",
    "amount": 2900,
    "currency": "GBP",
    "checkout_url": "https://checkout.revolut.com/payment-link/12fed5eb-636a-4ca2-a1e8-ffe3bce0ced1"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `token` | The public token for initialising a payment widget |
  | `state` | `pending` - the order completes once the customer pays the £29.00 first charge |
  | `checkout_url` | The link to Revolut's Hosted Checkout Page |

Like the calls in the previous steps, this is a server-side operation - it happens between your backend and the Merchant API.

#### 3.2 Build your payment acceptance solution

Collecting the payment reaches beyond your backend: the customer pays in their browser or mobile app, so this part of the flow has client-side work alongside your server. The goal is to hand the customer a way to pay the first charge and save their payment method. Revolut supports two approaches, and both do this for you:

- [:CodeRepository: Payment widget](# "Embed the payment flow in your site: you build the customer experience, and the customer never leaves your page")
- [:Browser: Hosted Checkout Page](# "Redirect to a payment page Revolut hosts for you: no payment UI to build, and Revolut handles the payment experience")

Choose the approach that fits your integration:

- ![Payment widget]
  Keep the customer on your site with an embedded payment widget or a card-not-present method:

  1. Build your payment acceptance solution on your frontend with the widget or payment method of your choice, initialising it with the `token` from the response.
  1. Configure it to save the customer's payment method for merchant-initiated recurring transactions.
  1. The customer completes payment without leaving your site, and Revolut saves their payment method.

- ![Hosted Checkout Page]
  Redirect the customer to Revolut's Hosted Checkout Page:

  1. Redirect the customer to the `checkout_url` from the response.
  1. The customer completes payment on the hosted page, and Revolut saves their payment method.
  1. If you set `setup_order_redirect_url` in [step 2](#2-subscribe-customer), the customer is redirected there after payment; otherwise they see Revolut's default completion screen.

:::info
This step assumes you're familiar with a standard payment method integration, see [Introduction to online payments](/docs/guides/merchant/accept-payments/online-payments/introduction).
:::

### 4. Verify subscription state

When the customer completes the first payment in [step 3](#3-collect-first-payment), the subscription becomes `active`. There's no webhook event for activation, so retrieve the subscription to confirm it's live.

Check the subscription details with [Retrieve a subscription](/docs/api/merchant#retrieve-subscription), passing the subscription `id` from [step 2](#2-subscribe-customer) in the request path:

- ![Request]
  ```http [Request example]
  GET /api/subscriptions/{subscription_id} HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `subscription_id` | The ID of the subscription you saved in [step 2](#2-subscribe-customer) |

- ![Response]
  When the first payment succeeds, the subscription moves to `active` with a saved `payment_method_id` and a `start_date`.

  ```json [Response example] {4,9,12,13}
  {
    "id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
    "external_reference": "api_7c5d21",
    "state": "active",
    "customer_id": "650e8400-e29b-41d4-a716-446655440001",
    "plan_id": "d71d7498-ba17-4413-b973-f6a1bf55eb62",
    "plan_variation_id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
    "payment_method_type": "automatic",
    "payment_method_id": "38e7108b-8055-4197-a9b5-507de3d09d70",
    "created_at": "2026-01-26T09:15:00.036001Z",
    "updated_at": "2026-01-26T09:20:00.036001Z",
    "start_date": "2026-01-26T09:20:00.036001Z",
    "current_cycle_id": "6f68ad11-6ce5-4646-b03b-1a4b8569d991"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `state` | `active` - the subscription is live and charges on each billing cycle |
  | `payment_method_id` | The saved payment method - Revolut charges it automatically on each cycle |
  | `start_date` | When the subscription became `active` |
  | `current_cycle_id` | The cycle currently billing the subscription - [Retrieve billing cycles](#retrieve-billing-cycles) uses it |

Once the subscription is `active`, your usage-based subscription is live. From here, the work splits between you and Revolut.

We handle the billing: charging the customer's saved payment method the platform fee for each cycle and the aggregated usage after each cycle closes, and retrying failed payments automatically - see [Failed payments and retries](/docs/guides/merchant/billing-subscriptions/subscription-lifecycle#failed-payments-and-retries).

Your part is the metering: report consumption through every cycle, because an unreported cycle still bills the platform fee but no usage. How to report is covered in [Report usage](#report-usage).

:::tip
**You've completed the usage-based subscription flow!** The API usage plan is live with both pricing styles, and Revolut bills the customer automatically from here.

Next, operate the subscription: report consumption through each cycle, correct or delete records while cycles are open, audit the usage records and billing cycles as they accrue, and cancel the subscription when the customer leaves.
:::

---

## Operate usage-based subscription

With the subscription active, Revolut bills the saved payment method automatically - the platform fee at each cycle boundary, the aggregated usage after each cycle closes.

What remains yours while it runs is the metering: report consumption through every cycle, keep the records accurate while cycles are open, audit the usage records and billing cycles - for reconciliation, billing history, and entitlement checks - and cancel the subscription when the customer leaves.

### Report usage

Reporting usage is a recurring operation: meter consumption on your side, then send records to Revolut as it happens - one per consumption event, or batched totals if you report in intervals. The example below follows the guide's subscription in its second cycle, reporting a day's batch of 1,200 API calls.

:::warning
The `Idempotency-Key` header is **required** for this endpoint - it prevents retries from creating duplicate usage records.
:::

Call [Create a subscription usage](/docs/api/merchant#create-subscription-usage), passing the subscription `id` and the `subscription_item_code` you configured in [step 1](#1-create-plan-for-usage-based-pricing), to tell Revolut what was consumed:

- ![Request]
  ```http [Request example] {9-11}
  POST /api/subscription-usages HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  Content-Type: application/json
  Idempotency-Key: e991efb1-8e92-40a6-a1e9-e8f6ad49b0ad

  {
    "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
    "subscription_item_code": "api_calls",
    "quantity": 1200,
    "usage_date": "2026-03-05T14:22:00Z",
    "metadata": {
      "user_id": "12345",
      "api_version": "v2"
    }
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `subscription_id` | The ID of the subscription being metered |
  | `subscription_item_code` | The `code` you gave the usage item in [step 1](#1-create-plan-for-usage-based-pricing) - here `api_calls` |
  | `quantity` | The consumed amount - for `sum` and `max` items report increments or event counts; for `latest` items report the current total |
  | `usage_date` | When the consumption happened - Revolut resolves the billing cycle from this date |
  | `metadata` | Optional: your own data on the record - useful for reconciliation and audit trails |

- ![Response]
  The response confirms the report and shows the billing cycle it's connected to.

  ```json [Response example] {4}
  {
    "id": "028ffabc-c47e-458c-b978-9f122146ba97",
    "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
    "subscription_cycle_id": "264f26a7-bfb7-410a-8a67-dccd927d8d72",
    "subscription_item_code": "api_calls",
    "quantity": 1200,
    "usage_date": "2026-03-05T14:22:00Z",
    "metadata": {
      "user_id": "12345",
      "api_version": "v2"
    },
    "created_at": "2026-03-05T14:22:05Z",
    "updated_at": "2026-03-05T14:22:05Z"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `id` | The record's identifier - save it to correct the record later |
  | `subscription_cycle_id` | The cycle the report landed in - Revolut identified it from `usage_date` |
  | `quantity` | The consumed amount - echoed back as reported |

#### Where reports land

Revolut resolves every usage report to a billing cycle based on the `usage_date`. Where that date falls decides which cycle the record settles:

| Reporting window | What happens |
|-----------------|--------------|
| Active cycle | `usage_date` falls within the current cycle - the record counts towards its settlement |
| Past-cycle correction | `usage_date` falls within a recently ended cycle, and the request arrives before that cycle's `usage_cutoff_date` - 12 hours after `end_date` by default |
| Next upcoming cycle | `usage_date` falls within the upcoming cycle - Revolut holds the record in a `pending` cycle until it starts |
| Any other date | Rejected - before the subscription's start date, after the last cycle, or too far in the future |

#### Handle reporting errors

When a report falls outside the accepted windows - or targets an item the plan doesn't carry - the API rejects it with a typed error code. Handle these in your integration:

| Error code | Description |
|------------|-------------|
| `subscription_item_code_not_found` | The item `code` doesn't exist on the subscription's current plan phase |
| `subscription_usage_locked` | The cycle is past its `usage_cutoff_date` - records can no longer be modified |
| `subscription_usage_future_cycle_limit_exceeded` | The `usage_date` is too far in the future |
| `subscription_usage_before_subscription_start` | The `usage_date` precedes the subscription's start date |
| `subscription_usage_after_last_cycle` | The `usage_date` falls after the last cycle - the subscription is finished |
| `subscription_finished` | Usage can't be added to a finished subscription |

### Update usage records

Reports can be wrong - double-counted batches, late-arriving events, meter drift. Records stay editable while the cycle is open. After the `usage_cutoff_date`, the cycle locks and modifications return `422 subscription_usage_locked`.

Correct a record with [Update a subscription usage](/docs/api/merchant#update-subscription-usage) to keep the cycle's bill accurate - it updates the `quantity` or `metadata` of a single record. The `{usage_id}` in the request path is the record's `id` from the [Report usage](#report-usage) response.

:::tip
If you didn't save the usage record's `id`, see [Retrieve usage records](#retrieve-usage-records) on how to find the record to be corrected.
:::

The guide's example batch double-counted 50 calls, so correct the day's report from 1,200 to 1,150:

- ![Request]
  ```http [Request example] {8}
  PATCH /api/subscription-usages/{usage_id} HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  Content-Type: application/json

  {
    "quantity": 1150
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `usage_id` | The ID of the record you saved from the report response |
  | `quantity` | The corrected amount - replaces the recorded value |

- ![Response]
  The response returns the updated record - the `quantity` is corrected and `updated_at` reflects the change.

  ```json [Response example] {6}
  {
    "id": "028ffabc-c47e-458c-b978-9f122146ba97",
    "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
    "subscription_cycle_id": "264f26a7-bfb7-410a-8a67-dccd927d8d72",
    "subscription_item_code": "api_calls",
    "quantity": 1150,
    "usage_date": "2026-03-05T14:22:00Z",
    "metadata": {
      "user_id": "12345",
      "api_version": "v2"
    },
    "created_at": "2026-03-05T14:22:05Z",
    "updated_at": "2026-03-06T10:05:12Z"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `quantity` | The corrected amount - the value the cycle will aggregate |
  | `updated_at` | When the record was last modified |

### Delete usage records

Some records shouldn't exist at all - a duplicate report, a metering glitch, usage logged for a test run. Where an update corrects a record's quantity, deletion removes the record entirely, so it never reaches the cycle's settlement.

Call [Delete a subscription usage](/docs/api/merchant#delete-subscription-usage) to remove the wrong record from the cycle's settlement, passing the record's `id` in the path:

- ![Request]
  ```http [Request example]
  DELETE /api/subscription-usages/{usage_id} HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `usage_id` | The ID of the record being removed - the same `id` you'd use to [update](#update-usage-records) |

- ![Response]
  The record is removed from the cycle - nothing of it reaches the settlement aggregate.

  ```http [Response example]
  HTTP/1.1 204 No Content
  ```


:::warning
The same cycle lock applies: after the `usage_cutoff_date`, deletions return `422 subscription_usage_locked`.

If you need the record's history, [correct its quantity](#update-usage-records) instead - deletion leaves nothing behind.
:::

### Retrieve usage records

Every report you send becomes a stored record - its `quantity`, `usage_date`, cycle, and `metadata` - so a complete metering history builds up as the subscription runs.

Retrieving that history powers the audit side of your billing: check what a cycle will bill before its cutoff, reconcile settled cycles against your own metering, build customer-facing usage views, and find records to update when you didn't save their `id`s.

The list call gives you that history in one request. Call [Retrieve a subscription usage list](/docs/api/merchant#retrieve-subscription-usage-list), filtering to the subscription and the cycle you want to audit - here, our current guide's cycle 2 before its cutoff:

- ![Request]
  ```http [Request example]
  GET /api/subscription-usages?subscription_id={subscription_id}&subscription_cycle_id={subscription_cycle_id} HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `subscription_id` | Filter to one subscription - here, the walkthrough subscription's `id` |
  | `subscription_cycle_id` | Filter to one billing cycle - here, the current cycle's `id` |
  | `from_usage_date` / `to_usage_date` | Optional: filter by when the consumption happened - records come back ordered by `usage_date` |

- ![Response]
  The response lists the cycle's records so far - two batches at this point in the walkthrough, including the corrected one.

  ```json [Response example] {8,22}
  {
    "subscription_usages": [
      {
        "id": "d1d85443-2d6a-46f3-939a-412c1e394936",
        "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
        "subscription_cycle_id": "264f26a7-bfb7-410a-8a67-dccd927d8d72",
        "subscription_item_code": "api_calls",
        "quantity": 900,
        "usage_date": "2026-02-27T16:40:00Z",
        "metadata": {
          "user_id": "12345",
          "api_version": "v2"
        },
        "created_at": "2026-02-27T16:40:05Z",
        "updated_at": "2026-02-27T16:40:05Z"
      },
      {
        "id": "028ffabc-c47e-458c-b978-9f122146ba97",
        "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
        "subscription_cycle_id": "264f26a7-bfb7-410a-8a67-dccd927d8d72",
        "subscription_item_code": "api_calls",
        "quantity": 1150,
        "usage_date": "2026-03-05T14:22:00Z",
        "metadata": {
          "user_id": "12345",
          "api_version": "v2"
        },
        "created_at": "2026-03-05T14:22:05Z",
        "updated_at": "2026-03-06T10:05:12Z"
      }
    ],
    "next_page_token": "2f3b8c1e-9a4d-4c5f-8e7b-1d2a3c9f0e88"
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `subscription_usages[]` | The cycle's records so far - each the same shape as the [report response](#report-usage) |
  | `next_page_token` | Pass as `page_token` to fetch the next page, when more records exist |

The response shows two usage records so far, on the subscription's current billing cycle: 900 calls on 27 February, and the corrected 1,150 from 5 March - 2,050 calls on the meter, £205.00 of usage at the pay-as-you-go price.

To retrieve a single record instead, call [Retrieve a subscription usage](/docs/api/merchant#retrieve-subscription-usage) with the record's `id` in the path - it returns the same object as the report response.

### Retrieve billing cycles

While the subscription is active, Revolut creates a billing cycle for every billing period - one a month on the walkthrough variation. Cycles with usage items carry two extra fields: `usage_cutoff_date`, the deadline for reports and corrections, and `post_billing_order_id`, the order that settles the cycle's usage once the cutoff passes.

The subscription's runtime resources look like this:

```mermaid
flowchart TD
    subgraph subscription ["Subscription f0eb534f - state: active"]
        subgraph cycle2 ["Cycle 264f26a7 - current, state: active"]
            reports["Usage reports - settle after the cutoff"]
        end
        subgraph cycle1 ["Cycle 6f68ad11 - state: finished"]
            order1["Setup order 95061a11 <br>pre-billed the £29.00 platform fee"]
            order2["Post-billing order 710ed088 <br>charged £479.00: cycle 1 usage + cycle 2 platform fee"]
        end
    end
```

The examples below cover two common use cases - a billing-history view and an entitlement check - but they're just a starting point. Explore how the same calls can serve your own business cases: the cycle data can power reconciliation, customer-facing billing pages, dunning workflows, and anything else your product needs.

#### Retrieve cycle list

The cycle list gives you the subscription's billing history in one call - every cycle so far, its state, and the orders that charged it. Use it for reconciliation and billing-history views, and to discover the current cycle's `id` if you don't have it saved.

Call [Retrieve a subscription cycle list](/docs/api/merchant#retrieve-subscription-cycle-list), passing the `id` of the subscription you created in [step 2](#2-subscribe-customer) as the `subscription_id`:

- ![Request]
  ```http [Request example]
  GET /api/subscriptions/{subscription_id}/cycles HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

- ![Response]
  The response lists the subscription's cycles - each carries its `id`, period, `state`, and the `order_id` that charged it, plus the two usage-lifecycle fields.

  ```json [Response example] {14,28}
  {
    "cycles": [
      {
        "id": "264f26a7-bfb7-410a-8a67-dccd927d8d72",
        "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
        "plan_variation_id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
        "plan_variation_phase_id": "f8581f2b-2e00-4938-a527-7112ae35a50e",
        "number": 2,
        "previous_cycle_id": "6f68ad11-6ce5-4646-b03b-1a4b8569d991",
        "state": "active",
        "start_date": "2026-02-26T09:20:00.036001Z",
        "end_date": "2026-03-26T09:20:00.036001Z",
        "usage_cutoff_date": "2026-03-26T21:20:00.036001Z",
        "order_id": "710ed088-5b4e-48c6-a877-4dbdbc0352ad",
        "trial": false
      },
      {
        "id": "6f68ad11-6ce5-4646-b03b-1a4b8569d991",
        "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
        "plan_variation_id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
        "plan_variation_phase_id": "f8581f2b-2e00-4938-a527-7112ae35a50e",
        "number": 1,
        "state": "finished",
        "start_date": "2026-01-26T09:20:00.036001Z",
        "end_date": "2026-02-26T09:20:00.036001Z",
        "usage_cutoff_date": "2026-02-26T21:20:00.036001Z",
        "order_id": "95061a11-a396-473b-989c-3f3c27ec707a",
        "post_billing_order_id": "710ed088-5b4e-48c6-a877-4dbdbc0352ad",
        "trial": false
      }
    ]
  }
  ```

  | Parameter | Description |
  |-----------|-------------|
  | `id` | The cycle's identifier - retrieve a single cycle with it |
  | `number` | Cycle sequence number, starting at `1` for the first cycle |
  | `state` | `active` while the cycle runs, `finished` once complete |
  | `start_date` / `end_date` | The cycle's billing period - `end_date` is when the next cycle starts |
  | `usage_cutoff_date` | The deadline for reports and corrections for this cycle - 12 hours after `end_date` by default |
  | `order_id` | The order that charged the cycle - cycle 1's is the setup order with the platform fee upfront; cycle 2's is the post-billing order that settled cycle 1 |
  | `post_billing_order_id` | The order created after the cutoff that charged the cycle's usage - present only on cycles with usage items |
  | `trial` | Whether the cycle is a trial cycle - always `false` for plans without trials |

When a cycle closes, settlement runs after its `usage_cutoff_date` - 12 hours after `end_date` by default. Revolut aggregates each usage item's records with its `usage_aggregation_method`, applies the per-unit price or tier ladder, and charges the saved payment method through a post-billing order. The walkthrough's first cycle reported 4,500 calls: the post-billing order charged £450.00 of usage together with the next cycle's £29.00 platform fee - £479.00 in total - and also became cycle 2's `order_id`.

| Scenario | Post-billing order behaviour |
|----------|------------------------------|
| Ongoing subscription | The order combines usage settlement for the closing cycle with pre-billing for the next - it also acts as the next cycle's `order_id` |
| Last cycle | A standalone finishing order charges all the cycle's usage |
| Subscription ended with unpaid cycles | The finishing order consolidates all outstanding usage charges, including failed ones from earlier cycles |

If a settlement charge fails, the subscription moves to `overdue` and Revolut retries it automatically - see [Failed payments and retries](/docs/guides/merchant/billing-subscriptions/subscription-lifecycle#failed-payments-and-retries). Reconcile each settled cycle by matching its `post_billing_order_id` against your own metering.

#### Run an entitlement check

A common pattern is gating platform access on the subscription's state: each time the customer logs in, retrieve the current cycle - its `id` is the `current_cycle_id` from the subscription response - and grant access while its `state` is `active`.

The check runs on every login, so keep it lightweight: retrieve just the current cycle instead of pulling the whole list. Call [Retrieve a subscription cycle](/docs/api/merchant#retrieve-subscription-cycle), passing the current cycle's `id` as the `cycle_id`:

- ![Request]
  ```http [Request example]
  GET /api/subscriptions/{subscription_id}/cycles/{cycle_id} HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

- ![Response]
  The response is the cycle object itself - the same shape as the list items above. For the entitlement check, `state` is the field that matters: grant access while it's `active`.

  ```json [Response example] {8}
  {
    "id": "264f26a7-bfb7-410a-8a67-dccd927d8d72",
    "subscription_id": "f0eb534f-e3e9-42a5-8e1f-3902e8ef141f",
    "plan_variation_id": "aedec485-0e72-49ed-ad5b-ea9e722f9cec",
    "plan_variation_phase_id": "f8581f2b-2e00-4938-a527-7112ae35a50e",
    "number": 2,
    "previous_cycle_id": "6f68ad11-6ce5-4646-b03b-1a4b8569d991",
    "state": "active",
    "start_date": "2026-02-26T09:20:00.036001Z",
    "end_date": "2026-03-26T09:20:00.036001Z",
    "usage_cutoff_date": "2026-03-26T21:20:00.036001Z",
    "order_id": "710ed088-5b4e-48c6-a877-4dbdbc0352ad",
    "trial": false
  }
  ```

### Cancel subscription

When the customer wants to leave, cancel the subscription - Revolut stops billing and cancels any pending orders. You can cancel in any state except `cancelled` or `finished`.

Call [Cancel a subscription](/docs/api/merchant#cancel-subscription), passing the `id` of the subscription you created in [step 2](#2-subscribe-customer) as the `subscription_id`:

- ![Request]
  ```http [Request example]
  POST /api/subscriptions/{subscription_id}/cancel HTTP/1.1
  Host: merchant.revolut.com
  Authorization: Bearer sk_abcdef12347890_...
  Revolut-Api-Version: 2026-08-17
  ```

- ![Response]
  The subscription is marked `cancelled` - no further cycles are created.

  ```http [Response example]
  HTTP/1.1 204 No Content
  ```

The subscription moves to `cancelled` - a final state: it can't be reactivated, so if the customer returns, create a new subscription for them. A `SUBSCRIPTION_CANCELLED` webhook event is sent whether the cancellation came from you or the customer - see [Subscription states](/docs/guides/merchant/billing-subscriptions/subscription-plans#subscription-states) and [Track subscriptions with webhooks](/docs/guides/merchant/billing-subscriptions/api/webhooks).

If you [run the entitlement check](#run-an-entitlement-check), this is where it stops granting access: end access immediately, or - if you let customers finish what they paid for - stop at the current cycle's `end_date`. Cancelling stops future charges only - it doesn't refund completed cycles.

---

## Implementation checklist

Confirm your usage-based integration works end to end. Run the checks in the Sandbox environment first - set the base URL of your API calls to `sandbox-merchant.revolut.com` - then repeat them in production before going live:

- [ ] Created a plan with usage items - each with an aggregation method and either a per-unit `amount` or `tiers` (never both) - and saved the variation `id` the customer subscribes to
- [ ] Subscribed a customer, collected the first payment - the platform fee upfront - and confirmed the subscription is `active`
- [ ] Usage reports send an `Idempotency-Key` on every request and target the correct `subscription_item_code`
- [ ] Corrections and deletions land before the cycle's `usage_cutoff_date` - `subscription_usage_locked` errors are handled
- [ ] Audited a cycle's usage records before its cutoff - the list call, filtered by subscription and cycle, reconciles the totals against your own metering
- [ ] Retrieved a settled cycle and reconciled its `post_billing_order_id` charge against your own metering
- [ ] Cancelled a test subscription - `204` response, no new cycles created, `SUBSCRIPTION_CANCELLED` event received

For verifying the payment solution itself - widget behaviour, test cards, webhook receipt for the order - use the implementation checklist in the payment method guide you followed.

<!--
TODO (PTTD-824)

1. RESOLVED 2026-09-08 per PO: anchor format confirmed - articles are stripped from operation-
   summary anchors (e.g. #create-subscription-plan, #retrieve-order). Historical inventory: (create-
   a-subscription-plan, create-a-subscription, create-subscription-usage, update-a-subscription-
   usage, delete-a-subscription-usage, retrieve-a-subscription-usage, retrieve-a-subscription-
   usage-list, retrieve-a-subscription-cycle).

2. Spec discrepancy (also flagged in fixed.md/per-seat.md): Subscription-Plan-Phase-Creation.yaml
   marks phase amount + currency as required, but the official examples omit them on phases carrying
   usage items (Req-Subscription-Plan-Usage-Base.yaml, Req-Subscription-Plan-Hybrid.yaml) - the
   walkthrough's phases follow the shipped examples. Confirm with the API team which is
   authoritative.

3. Tiered charging interpretation (graduated per-unit within each range) follows Subscription-Item-
   Tier.yaml's description; the worked example (first 1,000 free, next 3,500 x £0.10) assumes
   graduated - verify with the API team if volume-style (whole order at the reached tier) is
   possible.

4. Setup-order composition for hybrid plans: the £29.00 first charge = flat items only, usage
   contributes nothing until reports land - inferred from Req-Subscription-Plan-Hybrid.yaml plus the
   settlement field descriptions (Subscription-Cycle-Usage-Cutoff-Date.yaml, Subscription-Cycle-
   Post-Billing-Order-Id.yaml). Verify first-charge composition with the API team.

5. Plan-change deep dive deferred to the upcoming manage/ category - add cross-links here when those
   pages land.
-->

---

## What's next

- [:Repayment: Build a fixed-rate subscription](/docs/guides/merchant/billing-subscriptions/api/fixed 'Charge a fixed recurring amount - the simplest subscription flow')
- [:PlaneSeat: Build a per-seat subscription](/docs/guides/merchant/billing-subscriptions/api/per-seat 'Bill per seat, with a stable or changing seat count')
- [:Voucher: Build a subscription with trials and introductory pricing](/docs/guides/merchant/billing-subscriptions/api/trials 'Offer free trials and promotional pricing phases')
- [:Webhook: Track subscriptions with webhooks](/docs/guides/merchant/billing-subscriptions/api/webhooks 'Receive real-time events when subscription states change')