Context

OEM partners host many n8n instances for their customers and need to centrally track billable executions. Querying the customer instances ad-hoc for analytics would put load on the customer's runtime and is fragile. The recommended pattern is a partner-operated usage tracking service that pulls a daily summary from each instance via the public Insights API and stores it in the partner's own database, where analytics can run independently from the customer’s instances.

It presents the relevant n8n API and gives an example service shape and DB schema to be hosted by the customer, but does not serve as a full reference implementation.


1. Multi-instance usage tracking at a glance

2. Authorizing against your self-hosted n8n instances using token exchange

Token exchange (link to OAuth 2.0 specification) lets the monitoring service authenticate to every n8n instance with a single shared RSA key pair — no per-instance API keys to provision or rotate.

The following section describes how to deploy a central usage tracking service and configuring your self-hosted n8n instances to allow this service access to them via token exchange.

⚠ As of now, the tokens generated on the usage tracking service via token exchange will require admin privileges. We are working on introducing a feature for “custom instance roles”, which will enable provisioning tokens that only have access to the insights API for example.

For a more comprehensive description of token exchange, see this page: ‣

1. Generate a key pair (one-time):

💡 If your IdP already publishes a JWKS endpoint (most OAuth2/OIDC providers do), you can skip key generation and point n8n to the JWKS URL instead.

Keep the private key secure, as it allows anyone to generate a valid authorization token to your n8n instances.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

2. On each n8n instance, set these environment variables:

N8N_TOKEN_EXCHANGE_ENABLED=true
N8N_ENV_FEAT_TOKEN_EXCHANGE=true

# When using a static key pair:
N8N_TOKEN_EXCHANGE_TRUSTED_KEYS=[{"type":"static","kid":"monitoring-service-key","key":"<contents of public.pem with newlines escaped as \n>","issuer":"<https://monitoring.yourcompany.com>","allowedRoles":["global:admin"]}]

# When using a jwks endpoint:
N8N_TOKEN_EXCHANGE_TRUSTED_KEYS=[{"type": "jwks","url": "<https://idp.example.com/.well-known/jwks.json>","issuer": "<https://idp.example.com>"}]

<aside> 📣

kid and issuer are arbitrary strings — they just need to match what the monitoring service sends.

</aside>