The MonetizationInboundPolicy is the gateway enforcement mechanism. It runs on
every request to a protected route, authenticates the API key, checks the
customer's subscription and payment status, enforces quota, meters the request,
and allows or blocks access.
Basic configuration
Add the policy to your policies.json:
Code
Then reference it in your route's inbound policy pipeline:
Code
The MonetizationInboundPolicy handles API key authentication internally. It
reads the API key from the Authorization header, validates it, and sets
request.user. You do not need a separate api-key-auth policy on monetized
routes — the monetization policy replaces it.
Configuration options
| Option | Type | Default | Description |
|---|---|---|---|
meters | object | (required) | Map of meter keys to increment values |
meterOnStatusCodes | string or number[] | "200-299" | Status code range to meter |
authHeader | string | "authorization" | Header to read the API key from |
authScheme | string | "Bearer" | Expected auth scheme prefix |
cacheTtlSeconds | number | 60 | How long to cache subscription data (minimum 60s) |
meters
The meters option defines which meters to increment and by how much when a
request is processed. Values must be positive numbers.
Code
meterOnStatusCodes
Controls which responses count toward metering. By default, only successful responses (2xx) are metered.
Code
The wildcard "*" is not a valid value for meterOnStatusCodes and throws a
configuration error. Use a specific range like "200-599" if you want to meter
most responses.
This is important for fairness: if your backend returns a 500 error, you probably don't want to charge the customer for that request.
authHeader
The header to read the API key from. Defaults to "authorization".
authScheme
The expected auth scheme prefix. Defaults to "Bearer". The policy expects the
header value in the format {authScheme} {apiKey}.
cacheTtlSeconds
How long to cache subscription and entitlement data, in seconds. Defaults to
60 with a minimum of 60. Increasing this value reduces calls to the gateway
service but means entitlement changes take longer to propagate.
Subscription and payment validation
The policy checks payment status on every request. Access is granted when:
- The subscription is active and not expired
- Payment status is
paidornot_required(free plans)
When payment fails, a configurable grace period (default 3 days) allows continued access while retries are attempted. After the grace period, access is blocked until payment succeeds.
The grace period is configurable via metadata on the plan or customer:
- Plan metadata key:
zuplo_max_payment_overdue_days - Customer metadata key:
zuplo_max_payment_overdue_days - Default:
3(days)
Multiple policies for different routes
Different routes can have different metering configurations. Define multiple policy instances:
Code
Apply each to the appropriate routes:
Code
Dynamic metering
For AI APIs and other variable-cost endpoints, you may need to meter based on the response — for example, counting the number of tokens returned by an LLM.
Use the setMeters and addMeters static methods to set meter values
programmatically at runtime from a custom policy or handler:
Code
You can also use addMeters to add to existing meter values rather than
replacing them:
Code
When using dynamic metering, configure the policy with the meter key but omit it
from the static meters option (or set it to 0), and set the value
dynamically in your handler.
Error responses
The policy returns 403 Forbidden for all error conditions. Responses follow
the RFC 7807 Problem Details format:
Code
Common error details:
| Condition | detail message |
|---|---|
| No auth header | "No Authorization Header" |
| Wrong auth scheme | "Invalid Authorization Scheme" |
| Invalid API key | "API Key is invalid or does not have access to the API" |
| Expired API key | "API Key has expired." |
| Expired subscription | "API Key has an expired subscription." |
| Payment not made | "Payment has not been made." |
| Payment overdue | "Payment is overdue. Please update your payment method." |
| Quota exhausted | "API Key has exceeded the allowed limit for \"X\" meter." |
| Meter not in subscription | "API Key does not have \"X\" meter provided by the subscription." |
Pipeline ordering
The monetization policy should be the first policy in the inbound pipeline since it handles authentication:
Code
If you still want per-second or per-minute rate limiting on top of monthly quotas, add a standalone rate-limiting policy after the monetization policy. These serve different purposes: monetization enforces billing quotas, while rate limiting protects against traffic spikes.