MeterFlow

Usage-based billing with Lemon Squeezy

Lemon Squeezy takes the payment and handles the tax. Counting what each customer uses, keeping their credit balance and deciding whether they may do the next thing is left to your app. MeterFlow does that part, and it follows your Lemon Squeezy subscriptions without code.

Who does what

The two sit side by side. Lemon Squeezy stays your merchant of record; MeterFlow never moves money.

Lemon SqueezyMeterFlow
Checkout, payment methods, sales tax and invoicesUsage events, counted per customer and per meter
The subscription and its renewalsThe same subscription, mirrored, on the plan you mapped
The recurring chargeWhat that charge includes: allowances, limits and credits
Refunds and chargebacksThe answer to “may this customer do this now?”

Set it up no code

Five steps in two dashboards. Your app is not involved yet.

  • 1. Create a plan

    In MeterFlow, define what you measure (a meter, such as images.generated) and a plan with its limits, then publish the plan. A draft plan is not on sale: an event for it creates no subscription.

  • 2. Create the webhook in Lemon Squeezy

    In Lemon Squeezy, create a webhook for the subscription events, subscription_payment_success included, and keep its signing secret. It asks for a URL: enter a placeholder for now. Lemon Squeezy’s own guide has the clicks: Webhooks.

  • 3. Add the connector

    In MeterFlow, open your project → Connectors and add one for Lemon Squeezy. Choose the live account or the test one and paste the signing secret. It is checked on every call and never shown back.

  • 4. Give Lemon Squeezy the real URL

    The connector now shows its webhook URL. Edit the webhook in Lemon Squeezy and replace the placeholder with it.

  • 5. Map each variant to a plan

    On the connector, map each Lemon Squeezy variant (its numeric variant id) to one of your plans. From then on, a customer who subscribes in Lemon Squeezy appears in MeterFlow on that plan.

Field by field, with the other four providers: the guide’s connector section.

What each Lemon Squeezy event does

Every delivery is checked against Lemon Squeezy’s signature, then listed in the connector’s delivery log with what it did, in a sentence.

Lemon Squeezy eventWhat happens in MeterFlow
subscription_createdThe customer gets a subscription on the plan mapped to the Lemon Squeezy variant, with Lemon Squeezy’s status.
subscription_updated, subscription_resumed, subscription_unpausedThe same subscription moves to the new plan or status.
subscription_pausedThe subscription takes that status.
subscription_cancelledThe subscription keeps running and records when it will end: in Lemon Squeezy, cancelled means “will not renew”.
subscription_expiredThe subscription is cancelled.
subscription_payment_successThe customer is granted the plan’s price in credits, once per payment.
Anything else, order_created includedRecorded in the delivery log and changes nothing.

The code in your app two calls

Ask before the customer does the billable thing, then report that it happened. The customer id is Lemon Squeezy’s own. A recorded event is always recorded; it is the question beforehand that enforces a limit.

Node.js

npm install meterflow

import { MeterFlow } from "meterflow";

const client = new MeterFlow({ apiKey: process.env.METERFLOW_API_KEY! });
const customerId = "4821973"; // the customer's id in Lemon Squeezy

const gate = await client.entitlements.check(customerId, "images.generated");
if (!gate.allowed) {
  return showUpgradePrompt(gate); // gate.reason says why: "hard_limit_reached", "insufficient_credits", ...
}

await generateImage();
await client.usage.record({ event_name: "images.generated", customer_external_id: customerId, value: 1, properties: {} });

Python

pip install meterflow

import os
from meterflow import MeterFlow

client = MeterFlow(api_key=os.environ["METERFLOW_API_KEY"])
customer_id = "4821973"  # the customer's id in Lemon Squeezy

gate = client.entitlements.check(customer_id, "images.generated")
if not gate["allowed"]:
    return show_upgrade_prompt(gate)  # gate["reason"] says why

generate_image()
client.usage.record({"event_name": "images.generated", "customer_external_id": customer_id, "value": 1, "properties": {}})

The full reference is in the SDK READMEs: Node.js and Python.

Questions

Does MeterFlow charge my customers through Lemon Squeezy?

No. MeterFlow never moves money and creates nothing in your Lemon Squeezy account. Lemon Squeezy charges your customers; MeterFlow reads what Lemon Squeezy tells it and keeps the usage, the credits and the limits.

Which id do I use for a customer?

Lemon Squeezy’s own customer id, the number Lemon Squeezy gives each customer. The connector creates each subscription under that id, so your app passes the same id when it records usage or asks whether a customer may do something.

What happens if Lemon Squeezy sends an event twice, or out of order?

A repeated event is ignored, and a repeated payment never grants credits twice. An older event that arrives after a newer one is ignored too, so a late update cannot bring a cancelled subscription back.

Can I try it without touching live data?

Yes. A connector is created for your live Lemon Squeezy account or for its test mode. The second kind writes test data only, which your app reads with an mf_test_ key; live data is never touched.

What if a Lemon Squeezy variant has no plan mapped to it?

The event is recorded in the delivery log with the reason, and nothing changes. Add the mapping and the next event for that variant applies.

Start

The free plan is enough to connect a test Lemon Squeezy account and send test events. Paid plans start at $29 a month, a flat price that is never a percentage of your revenue.

Create an account · See the plans · Compare with Stripe Billing and Autumn

↑