---
title: "Billing shouldn’t just be PDF generation"
url: https://getlago.com/blog/custom-expressions-billing
description: "Billing Systems shouldn't just generate PDFs. See how Lago lets you send raw data & automates complex billing logic, metering & invoice generation."
authors: ["Anh-Tho Chuong"]
tags: ["Pricing & Monetization"]
published: 2025-01-22
updated: 2026-07-07
reading_time_minutes: 3
---

# Billing shouldn’t just be PDF generation

I hate paying for tools that make *me*do all the work. This is one of my gripes with conventional billing vendors: Sure, it’s easy enough to bill for seat-based subscriptions and a bit of usage. The logic is simple: You tell your billing system how many seats a customer has and send over usage once a day.

But as companies grow (especially infrastructure and API-based companies), billing gets more complex: Let’s say you run a cloud storage service with a monthly fee per user and a usage-based charge for GBs per second. Now a customer’s finance department wants more specific invoices that list exactly what they’re paying for.

Now you have to build:

- A script/CRON job that fetches usage data at your pre-defined intervals.
- Logic that calculates GB per second with timestamps, differences and data consumed.
- A way to aggregate these results and send them to your billing system so they actually show up on the invoices.

This means you’re diverting engineers from building product and create complexity that makes this an even bigger headache. Because what happens if you start an EU-based cloud with different prices and a customer uses both clouds for different user segments? Or if sales tells an enterprise prospect they get a 20% discount on the first 10,000 GB each month?

If you want to know how this ends, it’ll probably look something like this:

### Why most billing tools mostly do PDF generation

In the end, you’re building most of your own metering and your billing vendor becomes a glorified PDF generator. And then you still have a system where you don’t have a real-time overview of your revenue and your customers don’t have one for their spend.

![__wf_reserved_inherit](https://cdn.prod.website-files.com/63569f390f3a7ad4c76d2bd6/6838b75b25c2663cf25f3054_6790d980e5c464767b586ead_6790d874334f6a215f4bbce8_metering-without-lago.webp)

Sucks, doesn’t it? If you pay for a billing system, you should be able to send it raw data and know your billing system has taken care of it. This means [Lago](https://getlago.com/docs/faq/about-lago), not you, calculates, aggregates, deduplicates and ultimately turns it into an invoice.

![__wf_reserved_inherit](https://cdn.prod.website-files.com/63569f390f3a7ad4c76d2bd6/6838b77ebac661ab6d0963d7_6790d980e5c464767b586ec1_6790d896b8e9194f5ae5d4a5_metering-with-lago.webp)

This is why we recently shipped custom SQL expressions in[Lago](https://getlago.com/docs/faq/about-lago), which is a step towards exactly this. Here’s how it works.

### Custom SQL expressions in Lago

Before the feature itself, let’s talk about billable metrics. A billable metric is anything you want to charge for. For [Mistral AI](https://getlago.com/customers/mistral) that’s a million tokens, for cloud providers that might be gigabytes in data storage, but it can be anything you can measure events for. If you want, you can bill a customer for every time they type the letter “b”.

Billable metrics used to be a single field like a million tokens. Now you can use them as more complex logic — a form of [advanced billing customization](https://getlago.com/blog/pricing-group-keys).

Let’s say you’re a DevTool company and you want to charge customers per user per repo. If you simply billed for users, it wouldn’t capture your pricing model. If you billed for repos only, it wouldn’t work either.

So you can now concatenate 2 properties on the event: `user_id` and `app_id`. You don’t want to pre-aggreate the calculation on your side, so you ship this:

‍

`Endpoint: /events`

`{
 "event": {
   "transaction_id": "{{$randomUUID}}",
   "external_subscription_id": "sub_expression_001",
   "code": "unique_seats",
   "properties": {
       "user_id": "user_01",
       "repo_id": "repo_02"
   }
 }
}`

‍

You can now send the row event, and [Lago](https://getlago.com/docs/faq/about-lago) applies the following SQL syntax to this metric to get the **Unique Seat per Repo:** `CONCAT(event.properties.user_id', '-' , event.properties.repo_id)`

The usage is entirely calculated by [Lago](https://getlago.com/docs/faq/about-lago). In that case, the unique seat per repo will be `user_01-repo_02`

‍

([Read our docs to find out more details about how to do this](https://getlago.com/docs/guide/billable-metrics/sql-expressions))

### Want to get started?

You can start ingesting these events now, either by building custom expressions and advanced computation directly on [Lago](https://getlago.com/docs/faq/about-lago)or by piping them in from your ingestion sources—AWS, GCP, MongoDB, etc.

[You can see the full list here](https://getlago.com/docs/guide/events/metering-source).
