Skip to main content
Metered entitlements are in beta. The API and configuration options may change.
Metered entitlements give customers a credit grant when they subscribe. As they consume resources—API calls, compute minutes, storage operations—usage events deplete the grant. At any point, you can query the balance to see how many credits remain and whether the customer still has access. Unlike static entitlements (which grant a fixed configuration value), metered entitlements track real-time consumption against a replenishing credit pool.

How It Works

  1. Create a metered feature with a billable metric that measures consumption
  2. Attach the feature to a plan price with an entitlement template defining the grant amount, reset period, and rollover rules
  3. Customer subscribes → a grant is provisioned automatically for the current period
  4. Your app sends usage events as the customer consumes
  5. Call the balance endpoint to check remaining credits and gate access in real time

Setup

Creating a Metered Feature

curl -X POST "https://api.paygentic.io/v0/features" \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "prod_abc123",
    "merchantId": "mer_abc123",
    "name": "API Calls",
    "key": "api-calls",
    "type": "metered"
  }'

Linking to a Price (Entitlement Template)

When creating or updating a price, include an entitlementTemplate to configure how grants are provisioned for subscribers:
cURL
curl -X POST "https://api.paygentic.io/v0/prices" \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "planId": "plan_def456",
    "unitPrice": "0",
    "billableMetricId": "metric_xyz789",
    "feature": {
      "id": "feat_abc123",
      "entitlementTemplate": {
        "usagePeriod": {
          "interval": "P1M"
        },
        "issueAfterReset": 1000,
        "isSoftLimit": false,
        "resetMaxRollover": 0,
        "resetMinRollover": 0,
        "preserveOverageAtReset": false
      }
    }
  }'
Setting unitPrice: "0" grants customers access without charging for individual events. This is the typical setup when you want to use metered entitlements for quota enforcement rather than per-event billing.

Linking the Price to a Plan

cURL
curl -X PATCH "https://api.paygentic.io/v0/plans/plan_def456" \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": ["price_ghi789"]
  }'

Entitlement Template Reference

FieldTypeDefaultDescription
usagePeriod.intervalISO 8601 durationrequiredHow often the grant resets. "P1M" monthly, "P1D" daily, "P1W" weekly
usagePeriod.anchordatetimeactiveFromReference point for computing period boundaries
issueAfterResetnumberCredits granted at the start of each period. If omitted, no grant is provisioned
issueAfterResetPriorityinteger0Burn order when multiple grants exist (lower = burned first)
isSoftLimitbooleanfalseIf true, customer retains access even when balance reaches 0
resetMaxRollovernumber0Max unused credits carried into the next period (0 = use-it-or-lose-it)
resetMinRollovernumber0Minimum credits guaranteed to roll over
preserveOverageAtResetbooleanfalseIf true, overage from the previous period is deducted from the new period’s grant

Checking the Balance

Use GET /v1/entitlements/{entitlementId} to get a real-time snapshot. For metered entitlements, the response includes live balance and usage data inline.
curl -X GET "https://api.paygentic.io/v1/entitlements/ent_abc123" \
  -H "Authorization: Bearer sk_test_YOUR_API_KEY"
Response example:
{
  "object": "entitlement",
  "id": "ent_abc123",
  "customerId": "cus_xyz789",
  "featureId": "feat_789",
  "featureKey": "api-calls",
  "featureType": "metered",
  "subscriptionId": "sub_abc",
  "status": "active",
  "activeFrom": "2026-01-01T00:00:00Z",
  "activeTo": null,
  "hasAccess": true,
  "metadata": {},
  "balance": 750,
  "usageInPeriod": 250,
  "overage": 0,
  "currentPeriodStart": "2026-02-01T00:00:00Z",
  "currentPeriodEnd": "2026-03-01T00:00:00Z"
}
Response fields:
FieldDescription
idThe entitlement ID
featureKeyThe unique identifier for the feature
featureTypeAlways "metered" for metered features
statusEntitlement status: active, expired, or canceled
hasAccesstrue if balance > 0, or if isSoftLimit is true
balanceRemaining credits across all active grants
usageInPeriodTotal usage consumed in the current period
overageUsage beyond available credits
currentPeriodStart / currentPeriodEndCurrent usage period boundaries

Gating Access

Check the balance before allowing an operation, then send the usage event regardless:
async function canMakeApiCall(entitlementId) {
  const response = await fetch(
    `https://api.paygentic.io/v1/entitlements/${entitlementId}`,
    { headers: { 'Authorization': 'Bearer sk_test_YOUR_API_KEY' } }
  );
  const { hasAccess } = await response.json();
  return hasAccess;
}

// In your request handler
if (!(await canMakeApiCall(customer.entitlementId))) {
  return res.status(429).json({ error: 'API call quota exceeded' });
}

// Proceed with the request, then send a meter event
await fetch('https://api.paygentic.io/v0/events', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_test_YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'api.call',
    subject: customerId,
    data: { quantity: 1 },
  }),
});
The grant engine does not block usage events in real time. Always check hasAccess before allowing an operation—do not rely on the event pipeline to enforce limits.

Soft vs Hard Limits

Hard Limit (isSoftLimit: false)Soft Limit (isSoftLimit: true)
When balance = 0hasAccess returns falsehasAccess stays true
Best forStrict quotas, free-tier capsOverage billing, best-effort enforcement
OverageNot accrued (access denied)Tracked in the overage field

Grant Lifecycle Examples

These examples show how grants behave across billing periods under different configurations.

Example 1 — Monthly Grant, Use-It-or-Lose-It

issueAfterReset: 1000, resetMaxRollover: 0, isSoftLimit: false
DateEventBalance
Jan 1Grant issued1000
Jan (mid)600 credits consumed400
Feb 1Period reset — unused 400 forfeited, new 1000 issued1000
Feb (late)1000 credits consumed0
Feb (late)Another requesthasAccess: false

Example 2 — Monthly Grant with Rollover Cap

issueAfterReset: 1000, resetMaxRollover: 500, isSoftLimit: false
DateEventBalance
Jan 1Grant issued1000
JanOnly 200 consumed — 800 remaining800
Feb 1Reset: 800 > 500 cap, rolled over as 500; new 1000 issued1500
FebHeavy usage month
The rollover is applied before the new grant, so the customer starts the period with rollover + issueAfterReset.

Example 3 — Daily Grants on a Monthly Subscription

usagePeriod.interval: "P1D", issueAfterReset: 100, resetMaxRollover: 50
A customer’s subscription renews monthly, but their grant resets daily. Each day they receive a fresh allocation.
DayEventBalance
Day 1Daily grant issued100
Day 180 consumed20
Day 2Reset: 20 ≤ 50 cap, rolls over; new 100 issued120
Day 2110 consumed10
Day 3Reset: 10 rolls over; new 100 issued110

Example 4 — Soft Limit with Overage Carry-Forward

issueAfterReset: 1000, isSoftLimit: true, preserveOverageAtReset: true
DateEventBalanceOverage
Jan 1Grant issued10000
Jan1200 consumed0200
Jan 31Access still allowed (soft limit)200
Feb 1Reset: new 1000 issued, 200 overage deducted8000
The preserveOverageAtReset flag ensures customers who overconsume one period have a reduced allowance the next period.

Example 5 — Minimum Rollover Floor

issueAfterReset: 1000, resetMaxRollover: 100, resetMinRollover: 100
When both min and max rollover are set to the same value, customers always carry forward exactly that amount regardless of actual usage:
Jan usageRemainingRolls over as
95050100 (raised to min)
900100100 (exact match)
200800100 (capped at max)

Rollover and Overage Reference

At each period reset, the carried-over balance is computed as:
rolledOver = min(resetMaxRollover, max(resetMinRollover, currentBalance))
Ending balanceresetMaxRolloverresetMinRolloverRolls over as
80010000800 (within range)
1200100001000 (capped at max)
501000100100 (raised to min)
0000 (use-it-or-lose-it)
When preserveOverageAtReset is true, any overage accumulated in the previous period is subtracted from the new period’s starting balance after rollover and the new grant are applied.

Next Steps