I need to generate a usage report... Is this the best way?

Your question

I would like to generate a usage report - domains, domain aliases, mailboxes, number of e-mails and mailbox size.
Reading through the documentation, it seems that the sticking point is the number of e-mails which isn’t available without impersonating an account - and if I write a script which impersonates a user, that script then holds the credentials for full access to every user’s account/mailbox.
Am I misunderstanding the docs? Is there a way of getting the mailbox count without having to impersonate a user?

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

This confirms x:Account/get (a privileged admin API, not impersonation) exposes usedDiskQuota per account, no impersonation needed. Now check if there’s a per-account email/message count field or if that indeed requires impersonating via JMAP Mailbox/Email counts. Let’s check the Domain object too, and search “totalEmails” or similar.

I have enough information now to answer accurately.

Based on the docs, the Account object exposes usedDiskQuota directly to admins (via x:Account/get, gated by the sysAccountGet permission) — no impersonation needed for disk usage. That covers mailbox size per account. However, there is no equivalent server-set field on the Account object for the actual message count; that would require querying the Email/Mailbox objects within the account’s own JMAP namespace, which does require signing in as that account (impersonation).

So your understanding is correct for message counts, but not for size:

  • Domains, domain aliases, mailboxes (as principals), and disk usage can all be retrieved with an admin-privileged account using Domain/get, Account/get (which exposes usedDiskQuota), etc. No impersonation required for these.
  • Number of e-mails per mailbox is not exposed as a field on the Account object; getting exact message counts means querying Email/query or Mailbox/get inside that account’s own JMAP session, which does require the impersonate permission.

If you’re only after per-account disk usage (not exact email counts), you can build the report without impersonation at all, using the Account object’s usedDiskQuota field together with Domain and Account queries.

Your understanding is largely correct. Domains, domain aliases, and mailbox disk usage can be pulled without impersonation, but per-mailbox message counts require going through the mailbox’s own JMAP data, which does require signing in as (or impersonating) that account.

What is available without impersonation:

  • Domains and their aliases: via the Domain object (x:Domain/get / x:Domain/query, needs the relevant sysDomain* permissions).
  • Mailbox/account disk usage: the Account object has a server-set usedDiskQuota field (bytes currently consumed), retrievable with x:Account/get under the sysAccountGet permission, no impersonation needed.

What is not exposed at the account-registry level:

  • The number of emails per mailbox is JMAP Mailbox/Email data that lives inside the individual account’s own namespace, so reading it requires acting as that account, which today means using the impersonate permission and the composite target%impersonator login (docs: Administrators / Impersonation).

If you want to limit exposure, you can scope a dedicated role that only holds impersonate plus the minimal JMAP read permissions needed (e.g. jmapMailboxGet), rather than granting it through a full admin role, and use a service account whose password is only used by the reporting script. But there is currently no separate “read-only usage stats” endpoint that avoids impersonation for message counts.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

I was under the impression that in an impersonated session, the target’s permissions are those that apply and not those of the impersonator’s. For example, if I place an IP restriction on a user (i.e. set an allowed IP under Directory → Accounts → [USER] → Authentication) and then impersonate that user, that IP restriction is applied and not ignored which is what you’re implying should happen.

That being the case, if I have only the permission jmapMailboxGet and I impersonate a user, then don’t I then have exactly the same access to that user that they have themselves (e.g. reading their e-mail etc.)?

Yes, a % login runs with the target’s own permissions, whatever the impersonator holds, so a script using it can do anything that user can.

You don’t need the % login for this. An account holding the impersonate permission can address any accountId directly over JMAP while keeping its own permissions, so a credential with jmapMailboxGet but no email or blob permissions can read mailbox counts and nothing else. Create a reporting account and give it an app password restricted to the host that runs the script, with its permissions replaced by just what the report needs (app passwords can’t be used for % logins at all):

"permissions": {"@type": "Replace", "permissions": {"authenticate": true, "impersonate": true, "jmapMailboxGet": true, "sysAccountQuery": true, "sysAccountGet": true, "sysDomainQuery": true, "sysDomainGet": true}}

Everything then comes from /jmap: x:Domain/get for names and aliases, x:Account/get with name, domainId, aliases and usedDiskQuota, and Mailbox/get with totalEmails for each account id. Summing totalEmails counts a message twice when it’s in two mailboxes; Email/query with calculateTotal and limit 1 gives the exact per-account number and only adds jmapEmailQuery, which returns ids, not content. The account’s own password can still do % logins, so make it long and random and IP-restrict it.