Hosting: usage reports for billing#
This documentation applies to Piler enterprise edition 2.2.5+
Revision #4
Publication date: 2026-OCT-28
Piler's license doesn't depend on your tenants' size, but your prices may: by the stored size, the number of mailboxes or the emails archived. Three ways to get the numbers per tenant.
The quota dashboard#
On the master, as the admin: Admin > Tenant > Quotas lists every tenant with its storage, emails and active users, and Export CSV downloads them. The same page sets per-tenant quotas with warning emails. On a multi-node deployment the master collects the numbers from the workers.
From 2.2.5 the numbers are collected every 15 minutes (QUOTA_SNAPSHOT_INTERVAL), and the page shows when. The admin pages show a banner when a tenant is at or above the warning threshold of a quota.
A tenant's own admin sees nothing of this by default. To warn the tenant too, turn on Show the quota banner to the tenant's admin in the quota editor of the tenant (the pencil button): the tenant's admin pages then show which of its quotas is at or above the warning threshold, with the usage and the limit, and ask to contact the service provider. Leave it off if you handle the quotas with your customers yourself.
The billing API#
For a script that feeds your billing system. From 2.2.5 it uses an API token, not the admin's password: the script can read the quotas and nothing else, the admin can use TOTP, and changing the admin's password doesn't break billing.
Create a token#
On the master, as the admin: Admin > Tenant > API tokens > New API token.
- Name: what uses the token, e.g.
billing script. - Expires in (days): 0 for a token that doesn't expire.
- Allowed addresses (optional): comma separated IP addresses or CIDRs
of the billing server, e.g.
192.0.2.10, 198.51.100.0/24. Behind a reverse proxy the client address is taken from the proxy's header only if the proxy is inTRUSTED_PROXIES, as for/metrics.
The token (plr_...) is shown once: copy it to the script's secret store.
Piler keeps only its hash. The page lists the tokens with their last use;
Revoke stops one at once. To rotate a token, create the new one,
switch the script to it, then revoke the old one. Creating and revoking a
token are in the audit log.
The token is read-only: it works on the two endpoints below, with GET, and nowhere else, not even on the admin API. It's refused with the browser's session cookie.
Call the API#
All tenants, with their usage and quotas:
TOKEN='plr_...'
curl -s -H "Authorization: Bearer $TOKEN" \
https://archive.example.com/api/v1/billing/quota
The same as a CSV, one line per tenant:
curl -s -H "Authorization: Bearer $TOKEN" \
https://archive.example.com/api/v1/billing/quota \
| jq -r '.[] | [.tenant_id, .storage_used_gb, .email_count, .user_count, .collected_at] | @csv'
"acme",45.67,123456,25,"2026-10-28T08:00:00Z"
"example",3.2,8800,4,"2026-10-28T08:00:00Z"
The emails received and the bytes stored per month of a tenant
(months: default 12, at most 36):
curl -s -H "Authorization: Bearer $TOKEN" \
"https://archive.example.com/api/v1/billing/quota/monthly/acme?months=3"
{
"tenant_id": "acme",
"months": 3,
"stats": [
{"month": "202610", "emails_received": 4120, "storage_bytes": 1342177280, "storage_gb": 1.25},
{"month": "202609", "emails_received": 3980, "storage_bytes": 1288490188, "storage_gb": 1.2},
{"month": "202608", "emails_received": 3655, "storage_bytes": 1181116006, "storage_gb": 1.1}
]
}
/api/v1/billing/quota returns the collected numbers, at most 15 minutes
old (QUOTA_SNAPSHOT_INTERVAL),
with collected_at per tenant: polling it is cheap, it doesn't ask the
workers. Check status too: pending is a tenant added since the last
collection, unknown one whose usage couldn't be collected, and
failed_workers the number of workers that didn't answer, when the usage
is a lower bound.
| Field | Meaning |
|---|---|
storage_used_gb |
Stored (compressed, encrypted) size of everything the tenant has archived, including email purged since |
email_count |
Emails received, all time |
user_count |
Active users: addresses of the tenant's domains that received archived email in the last 90 days |
archived_user_count |
Every address of the tenant's domains that ever received archived email, former employees included (from 2.2.5) |
If a call fails:
| Status | Reason |
|---|---|
| 401 | No token, an unknown, revoked or expired one, or sent with a session cookie |
| 403 | Not from an allowed address |
| 405 | Not a GET request |
| 400 | Not a valid tenant id (monthly usage) |
The API tokens need the api_token table: run the SQL migrations on the
master's piler database when upgrading to 2.2.5, see
Run the migrations.
Before 2.2.5 the same data was at /api/v1/admin/quota/all and
/api/v1/admin/quota/monthly/<tenant id>, with the token of the master
admin's login. These still work for the admin, but a billing script
should use an API token. See the REST API for the
details.
Two things to know:
- For tenants with a retention period,
storage_used_gbgrows past what is stored now, as purged email is still counted. For the current size, use the metrics below. - From 2.2.5,
user_countis the active users, with the user quota applying to them; before, it was every address that ever received email. An archive keeps the mail of the people who left: their mail is in the storage, it's not a user. A mailbox counts as active as long as it receives email, spam and newsletters included, so have the MTA reject email to deleted mailboxes. pilerimport counts the date of each email, not the time of the import, so importing a customer's old mail doesn't make its former employees active (before 2.2.5 the import time counted). - On a multi-node deployment, from 2.2.5 the master counts a mailbox that received email on several workers once, and active if it's active on any of them. Before, it added up the workers' counts.
Metrics#
From 2.2.5, piler-ui's /metrics has the current numbers per tenant, for
your monitoring system to keep a history of (see monitoring):
| Metric | Meaning |
|---|---|
piler_tenant_stored_bytes |
Stored size of what the tenant has archived now, purged email excluded |
piler_tenant_size_bytes |
Original size of the same |
piler_tenant_messages_received |
Emails received, all time |
On a multi-node deployment they come from the workers: sum them by tenant,
e.g. sum by (tenant) (piler_tenant_stored_bytes). A monthly maximum or
average of the stored size is a fair base for a storage price.