6 steps to build a usage-based billing system with Lago (YC S21)
Tags: billing, opensource
Published: 2022-09-08 13:13:41

---
canonical_url: https://www.getlago.com/blog/6-steps-to-build-a-usage-based-billing-system
---

[Lago](https://getlago.com) is an open-source billing API that helps engineers implement their billing systems faster. We do think the future of pricing is hybrid, which includes subscription fees and usage-based charges. Over the past few years, engineers have struggled to implement exotic billing systems but this is over!

Lago also provides a cloud-hosted application for those who don’t want to run it on their own environment.

We are going to build an entire hybrid billing system, including plans and usage-based features. Let’s try to replicate [Segment’s pricing](https://segment.com/pricing/) for instance!

## Prerequisites
You can find the main Lago repository on GitHub.

Before we get started, you need to:
1. Install [Docker](https://docs.docker.com/get-docker/) on your machine;
2. Make sure [Docker Compose](https://docs.docker.com/compose/install/) is installed and available (it should be the case if you’ve chosen to install Docker via Docker Desktop);
3. Make sure [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) is installed on your machine; and
4. A cup of tea (or coffee)! ☕️

This demo is made through basic CURL API calls, but Lago also provides API clients in order to make the implementation process easier.

## 1. Setting up your Lago billing engine
Start running Lago on your own environment. You will have access to **Lago API** and **Lago UI** (frontend admin panel to perform no-code tasks).

```bash
# Get the code
git clone https://github.com/getlago/lago.git

# Go to Lago folder
cd lago

# Set up environment configuration
echo "LAGO_RSA_PRIVATE_KEY=\"`openssl genrsa 2048 | base64`\"" >> .env
source .env

# Start (make sure docker is started)
docker-compose up
```

You can now open your browser and go to [http://localhost/](http://localhost) to connect to the application. Lago's API is exposed at [http://localhost:3000/](http://localhost:3000).

If needed, you can override your environment variables ([learn more](https://doc.getlago.com/docs/guide/self-hosting/docker#environment-variables)).

## 2. Defining your billable metrics
The usage-based feature billed by Segment is the number of **monthly tracked users (MTUs)**, which is related to the total number of unique users tracked monthly.

Lago’s billable metrics allow you to track and automatically aggregate usage to determine the number of **units to be charged** at the end of a billing period.

Here we’re going to define MTUs as a `metered billable metric`:
```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/billable_metrics" \
  --header "Authorization: Bearer $API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "billable_metric": {
      "name": "Monthly Tracked Users",
      "code": "mtu",
      "description": "Number of unique users tracked by Segment",
      "aggregation_type": "unique_count_agg",
      "field_name": "user_id"
    }
  }'
```

For this billable metric, the aggregation type is `unique_count_agg`, which is used to count the number of unique `user_id` values recorded during the billing period.

## 3. Creating your first plan
Now that we have our billable metric, we can create our first plan. The plan defines the billing period and all fees (i.e. subscription and usage-based charges).

Let’s take the example of **Segment’s Team plan**. The base subscription costs $120 per month month, including 10,000 free MTUs. If you track more users, you will be charged for the overage, according to Segment’s graduated pricing model.

```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/plans" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
  "plan": {
    "name": "Team",
    "code": "team",
    "interval": "monthly",
    "description": "Team plan, ideal for startups",
    "amount_cents": 12000,
    "amount_currency": "USD",
    "trial_period": 0,
    "pay_in_advance": true,
    "bill_charges_monthly": true,
    "charges": [
      {
        "billable_metric_id": "__BILLABLE_METRIC_ID__",
        "charge_model": "graduated",
        "properties": [
          {
            "to_value": 10000,
            "from_value": 0,
            "flat_amount": "0",
            "per_unit_amount": "0.00"
          },
          {
            "to_value": 25000,
            "from_value": 10001,
            "flat_amount": "0",
            "per_unit_amount": "0.012"
          },
          {
            "to_value": 100000,
            "from_value": 25001,
            "flat_amount": "0",
            "per_unit_amount": "0.011"
          },
          {
            "to_value": null,
            "from_value": 100001,
            "flat_amount": "0",
            "per_unit_amount": "0.010"
          }
        ]
      }
    ]
  }
}'
```
Let’s take a closer look at the request above.

### Basic plan model
The request creates a basic plan at $120/month that is paid in advance (beginning of period), with no free trial. If can customise it using the following fields:

**• interval:** can also be set to yearly or weekly;
**• amount_currency:** all currencies are available;
**• trial_period:** set a number of trial days; and
**• pay_in_advance:** if set to false, the subscription fee will be paid in arrears (end of period).

### Usage-based charges
In this example, we selected a **graduated charge model** for our usage-based feature (i.e. MTUs). Here’s what we can see in the user interface.


![Usage based graduated charge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i830vm5v0ycqxj1zzvc.png)

You can create as many charges as you want and there are many charge models available (e.g. volume pricing, standard pricing, percentage pricing, package pricing).

## 4. Creating your first customer
Congratulations, your pricing is ready! You can now create a customer, assign them a plan and start monitoring usage.

```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/customers" \
  --header "Authorization: Bearer $API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "customer": {
      "external_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
      "address_line1": "5230 Penfield Ave",
      "address_line2": null,
      "city": "Woodland Hills",
      "country": "US",
      "email": "dinesh@piedpiper.test",
      "legal_name": "Coleman-Blair",
      "legal_number": "49-008-2965",
      "logo_url": "http://hooli.com/logo.png",
      "name": "Gavin Belson",
      "phone": "1-171-883-3711 x245",
      "state": "CA",
      "url": "http://hooli.com",
      "vat_rate": 12.5,
      "zipcode": "91364",
      "billing_configuration": {
        "payment_provider": "stripe",
        "provider_customer_id": "cus_12345",
        "sync": true
      }
    }
  }'
```

This request allows you to create a customer and also to define the default payment provider (i.e. the payment service provider that will be used to collect payments for this customer). We also set a tax rate of 12.5% for this customer.


![Creating a customer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/712ukabsvi1itf3ji1gd.png)

## 5. Assigning a plan to a customer

To start billing a customer, you need to assign them a plan. This action will create a **subscription**.

```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/subscriptions" \
  --header "Authorization: Bearer $API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "subscription": {
			"external_id": "my_subscription_1",
      "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
      "plan_code": "team",
      "billing_time": "anniversary"
    }
  }'
```

![Create a subscription](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7w2z1ixvnwflz5l6mkbr.png)

When assigning a subscription, you can:

• Define your own `external_id`;
• Choose between `anniversary` or `calendar` for the billing time - a subscription based on calendar dates is billed on the first day of each week/month/year, while a subscription based on the anniversary date is billed on the day it was created (e.g. every 4 of the month); and
• Enter a subscription name that will be displayed on the invoice and will allow you to differentiate subscriptions that share the same plan (e.g. Project 1 on the Free plan and Project 2 also on the Free plan).

As the base amount of the Team plan has to be paid in advance, Lago automatically generates a first invoice. The usage-based charge related to MTUs will be billed at the end of the period.


![Emitting the invoice](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mlg4haeuua5ldbhvqbzh.png)

This invoice can be customized with information about your organization, your logo and a custom footer. For compliance purposes, invoices have an incremental `sequential_id` and incremental `invoice_number` as well.

## 6. Ingesting usage-based events
Lago has an event-based architecture. When a subscription is associated with a customer, you can start pushing events related to your billable metrics. By using a unique `transaction_id`, we make sure that an event is not ingested twice. It’s what we call an idempotency key.

Below are three events ingested by Lago:

**Event #1**
```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/events" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
      "event": {
          "transaction_id": "transaction_1",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662462118,
          "properties": {
            "user_id": "1234_5678_9012_3456"
          }
      }
  }'
```
**Event #2**
```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/events" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
      "event": {
          "transaction_id": "transaction_2",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662548611,
          "properties": {
            "user_id": "1234_5678_9012_3456"
          }
      }
  }'
```
**Event #3**
```bash
LAGO_URL="https://api.getlago.com"
API_KEY="__YOUR_API_KEY__"

curl --location --request POST "$LAGO_URL/api/v1/events" \
--header "Authorization: Bearer $API_KEY" \
--header 'Content-Type: application/json' \
--data-raw '{
      "event": {
          "transaction_id": "transaction_3",
          "external_customer_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
          "code": "mtu",
          "timestamp": 1662721411,
          "properties": {
            "user_id": "0000-1111-2222-3333"
          }
      }
  }'
```


![Lago events debugger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtj5428ajerkfr257bdy.png)

We have three different events, however, two of them are sending the same `user_id`. As we use a unique count aggregation type for the billable metric MTUs, **the number of units to be charged is 2.**

Lago then automatically calculates the fees depending on the charge model of the plan. With Segment’s pricing, the first 10,000 MTUs are free of charge. Therefore, these two units cost $0.

![Current billing usage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mps63mwpoy7v7aecsalb.png)

This is how Lago can help you implement a usage-based billing system in just a few hours (not months). It’s easy to build but also easy to maintain.

In addition to this, Lago manages upgrades/downgrades, taxes, discounts, prepaid credits and prorated fees. [Ready to give it a try?](https://www.getlago.com/get-started)