Skip to content

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 in TRUSTED_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_gb grows past what is stored now, as purged email is still counted. For the current size, use the metrics below.
  • From 2.2.5, user_count is 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.