Metered billing
Charge customers by usage instead of a flat seat count: report events from the SDK, and let Stripe auto-charge based on that usage.
Metered Billing
Metered billing lets you charge customers based on actual usage rather than a flat seat count. Your software reports a usage event every time a customer performs a billable action — calling an API, exporting a file, generating a report. PermitCore records every event and, with Stripe connected, automatically charges customers at the end of each billing period. No spreadsheets, no manual invoicing.
Recording a usage event — SDK call
POST /api/v1/meter — no authentication required. Rate limited to 60 requests/minute per IP.
curl -X POST https://api.permitcore.dev/api/v1/meter \
-H "Content-Type: application/json" \
-d '{"licenseKey":"PERMIT-XXXX-XXXX-XXXX-XXXX","eventName":"api_call","quantity":1}'await fetch('https://api.permitcore.dev/api/v1/meter', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ licenseKey: 'PERMIT-XXXX-XXXX-XXXX-XXXX', eventName: 'api_call', quantity: 1 }) });
import requests requests.post('https://api.permitcore.dev/api/v1/meter', json={ 'licenseKey': 'PERMIT-XXXX-XXXX-XXXX-XXXX', 'eventName': 'api_call', 'quantity': 1, })
Request fields
| Field | Required | Description |
|---|---|---|
licenseKey | Yes | Plaintext license key — hashed and resolved via HMAC lookup server-side, never stored |
eventName | Yes | Billable action slug, e.g. api_call, export, report_generated. Normalised to lowercase. Must be consistent — this same name is used to configure pricing. |
quantity | No | Units consumed. Defaults to 1. Use for batch operations (e.g. 5 if processing 5 items at once). |
meta | No | Arbitrary JSON stored with the event (e.g. file format, region). Visible in the admin event list. |
Response
{ "recorded": true }Choosing event names
Event names are normalised to lowercase and matched exactly. Choose them carefully up front — they appear in your Stripe Dashboard, invoices, and customer billing pages.
| Good | Why |
|---|---|
api_call | Machine-readable, consistent, clear unit |
export | One word, noun — matches what the customer did |
report_generated | Verb past-tense, unambiguous |
sms_sent | Describes the unit being billed |
Meter Usage dashboard
Go to Meter Usage in the admin sidebar. The dashboard shows:
- Total events — sum of all events in the selected period (7 / 30 / 90 days)
- Unique event types — count of distinct event names
- Average events / day — total ÷ days in period
- Daily bar chart — volume per calendar day, useful for identifying traffic spikes
- Event breakdown table — each event type, total count, and share percentage
Buttons at the top of the page:
- Pricing — configure per-event Stripe pricing (see below)
- Stripe Setup — retroactive backfill wizard for existing customers
Stripe Auto-Charge for Metered Usage
Connect PermitCore to Stripe to have customers billed automatically based on their usage — no manual invoicing, no exports, no scripts. When a Store customer triggers their first meter event, PermitCore creates the Stripe mapping and subscription item automatically. You configure prices once and everything else runs itself.
Prerequisites
- A Stripe account (live or test mode)
- Your Stripe Secret Key added to PermitCore under Settings → Company → Payment Settings
- Customers who bought via your PermitCore Store — their Stripe
cus_xxxID is captured automatically on checkout
Step 1 — Configure pricing for each event name
Go to Meter Usage → Pricing. This page shows all event names that have been recorded, plus any you configure in advance.
Click Add Event Price and fill in:
| Field | Description | Example |
|---|---|---|
| Event Name | Must exactly match the eventName your SDK sends. Normalised to lowercase automatically. | api_call |
| Display Name | Human-readable name shown in Stripe Dashboard and customer invoices. | API Calls |
| Unit Label | The noun Stripe uses on invoices — e.g. "per API call" on the invoice line. | api calls |
| Price per Unit | Cost in the selected currency per single event unit. Supports up to 6 decimal places for micro-pricing. | 0.0100 ($0.01) |
| Currency | USD, EUR, or GBP. Must match the currency your Stripe subscription uses. | USD |
Click Save & Create in Stripe. PermitCore immediately:
- Calls the Stripe Billing Meters API to create a new Billing Meter with your event name, aggregation formula (
sum), and value key (value). - Creates a recurring metered Price linked to that meter (monthly interval).
- Stores the Stripe Price ID (
price_xxx) in your database.
You will see a green Stripe status badge next to the event name showing the Price ID. This confirms Stripe is ready.
Step 2 — What happens on the first meter event (fully automatic)
After Step 1, everything else is automatic. Here is exactly what happens when a customer's software calls POST /api/v1/meter for the first time for a given event:
{ "recorded": true } is returned to your SDK. Everything else happens in the background.Order record from when the customer bought via your Store — which contains their Stripe cus_xxx ID (captured automatically from the Stripe Checkout session). A LicenseMeterStripeConfig record is created linking the license key, event name, and Stripe customer ID.SubscriptionItemService.CreateAsync to add the price to the subscription. From this moment, Stripe will aggregate all future meter events for this customer.MeterEvent.create) with the event name, the customer ID, and the quantity. Stripe records this event against the meter.POST /api/v1/meter call for the same license key + event name skips directly to pushing the event to Stripe. The customer is invoiced at the end of each billing period by Stripe automatically.For customers who didn't buy via the Store
If a customer's license was not sold through the PermitCore Store (e.g. sold offline, via a reseller, or via a custom flow),
their Stripe customer ID (cus_xxx) is not automatically available. You need to configure the mapping manually.
Go to Licenses → View for the relevant license key. Scroll down to the Stripe Metered Billing card.
cus_xxx.cus_xxx ID is correct before saving.You can add multiple mappings per license — one per event name if different events should go to different Stripe customers (rare, but supported).
Retroactive backfill — customers who used meter before setup
If you had customers using POST /api/v1/meter before you configured pricing, those events were already
recorded in PermitCore but never forwarded to Stripe. To backfill:
- Go to Meter Usage → Stripe Setup.
- The wizard shows all event names with existing meter events, how many licenses are auto-mappable (have Store orders with
cus_xxx), and how many need manual setup. - Click Backfill Now to auto-create
LicenseMeterStripeConfigrecords for all historical Store-purchased licenses. - Any remaining licenses with no
cus_xxxare listed in Step 3 of the wizard with direct links to their license detail pages for manual mapping.
End-to-end example — $0.01 per API call
A vendor sells an API gateway product. Customers pay a base subscription plus $0.01 per API call.
api_call, Display Name "API Calls", Unit Label "api calls", Price $0.01, Currency USD. Click Save & Create in Stripe. Done — Stripe Meter + Price created automatically.cus_xxx ID is captured and saved on the order automatically.client.meter(license_key, "api_call", quantity=1)
api_call event from this customer: PermitCore creates the customer mapping, adds the metered price to their Stripe subscription, and pushes the event. The response confirms that the event was recorded locally; it does not confirm successful delivery to Stripe.How to confirm it actually worked
After sending a test POST /api/v1/meter call, check these three places — in order, since the first one confirms the fastest and the mapping/Stripe steps only run once per customer:
| Where | What confirms success |
|---|---|
| The API response | { "recorded": true } only confirms PermitCore accepted the event locally — not that Stripe received it. |
| License detail page → Stripe Metered Billing card | The mapping row now shows the Stripe Customer ID this event was routed to. |
| Stripe Dashboard → Billing → Meters → your meter → Event summaries | The definitive proof — an aggregated usage value against that customer for the current period. If this shows the expected total, the full pipeline worked end to end. |
What you see in Stripe Dashboard
After setup, in your Stripe account you can see:
- Billing → Meters — your meter(s) with event name, current period usage, and aggregated totals
- Customers → [customer] → Subscriptions — the subscription now has a metered line item showing usage
- Billing → Invoices — at period end, a draft invoice is generated then auto-paid
Stripe auto-charge API reference
| Endpoint | Auth | Description |
|---|---|---|
GET /api/meter/pricing | CompanyAdmin | List all configured event prices. Also returns known unconfigured event names from recorded usage. |
PUT /api/meter/pricing | CompanyAdmin | Create or update a price config. Immediately creates the Stripe Billing Meter + metered Price. |
DELETE /api/meter/pricing/{eventName} | CompanyAdmin | Remove the local price config. Does not delete the Stripe Meter or Price (Stripe resources are kept). |
GET /api/meter/stripe/{licenseKeyId} | CompanyAdmin | List manual customer mappings for a license key. |
PUT /api/meter/stripe/{licenseKeyId} | CompanyAdmin | Create or update a manual customer mapping (eventName + stripeCustomerId). |
DELETE /api/meter/stripe/{licenseKeyId}/{eventName} | CompanyAdmin | Remove a manual customer mapping. |
POST /api/meter/stripe/{licenseKeyId}/test | CompanyAdmin | Verify a cus_xxx exists in Stripe. Returns { valid, email, name }. |