> For the complete documentation index, see [llms.txt](https://docs.kangal.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kangal.dev/metering-and-billing/entitlements-metering.md).

# Entitlements, quotas, and usage

Kangal enforces commercial feature access on the server. Hiding a button in the console is never the security or billing boundary.

## Entitlement catalog

The catalog version is `2026-07-15.1`. Every tenant must have an explicit, currently active plan assignment. Without a usable assignment, protected operations fail with `402 Payment Required` and the stable code `plan_required`.

An assignment is unusable when it has not started, has expired, references an unknown plan, or uses an unavailable catalog version.

### Feature keys and periods

| Feature             | Key                             | Period                  |
| ------------------- | ------------------------------- | ----------------------- |
| Gateways            | `gateway.manage`                | Lifetime resource count |
| Routes              | `route.manage`                  | Lifetime resource count |
| Plugins             | `plugin.manage`                 | Lifetime resource count |
| AI configuration    | `ai_gateway.manage`             | Lifetime resource count |
| AI requests         | `ai_gateway.runtime`            | Calendar month, UTC     |
| Integrations        | `integration.manage`            | Lifetime resource count |
| Developer portal    | `developer_portal.manage`       | Lifetime resource count |
| Portal applications | `developer_portal.applications` | Calendar month, UTC     |

### Plan limits

`Unlimited` means enabled with no numeric limit.

| Feature                     |     Starter |           Team | Dedicated Cloud | Self-managed Hybrid |
| --------------------------- | ----------: | -------------: | --------------: | ------------------: |
| Gateways                    |      2 hard |        10 hard |        100 soft |           Unlimited |
| Routes                      |    100 soft |     1,000 soft |       Unlimited |           Unlimited |
| Plugins                     |     25 hard |       250 hard |       Unlimited |           Unlimited |
| AI configurations           |     10 hard |       100 hard |       Unlimited |           Unlimited |
| AI requests / month         | 10,000 soft | 1,000,000 soft | 10,000,000 soft |           Unlimited |
| Integrations                |      5 hard |        50 hard |       Unlimited |           Unlimited |
| Developer portal            |      1 hard |         1 hard |       Unlimited |           Unlimited |
| Portal applications / month |    100 soft |    10,000 soft |       Unlimited |           Unlimited |

Plan codes are `serverless_starter`, `serverless_team`, `dedicated_cloud`, and `self_managed_hybrid`.

## Enforcement results

| HTTP status             | Stable code            | Meaning                                    |
| ----------------------- | ---------------------- | ------------------------------------------ |
| `402 Payment Required`  | `plan_required`        | No currently usable assignment.            |
| `403 Forbidden`         | `feature_not_entitled` | The plan or override disables the feature. |
| `429 Too Many Requests` | `quota_exceeded`       | A hard quota has no remaining capacity.    |

Day- or month-based hard quota responses include `Retry-After`. Lifetime quotas do not have an automatic reset date. Soft quotas allow the operation and expose `warning: true` in status.

Limit-bearing operations reserve capacity before the write, commit it after a successful write, and release it after failure. Reservations expire after five minutes by default. Concurrent writes cannot consume the same final hard-quota slot. `used + reserved` therefore represents current availability.

Deleting a covered lifetime resource releases its unit. Monthly request and application meters are not decremented. When a limited plan is assigned, existing gateways, routes, plugins, AI configurations, and integrations are reconciled into lifetime usage before a new reservation is accepted.

## Admin workflow

Tenant admins can read catalog, status, and their tenant's history. Only super-admins can assign plans, set or delete overrides, or reset usage.

```bash
export KANGAL_URL='https://api.example.com'
export KANGAL_SUPER_ADMIN_TOKEN='<super-admin-bearer>'

curl --request PUT "$KANGAL_URL/api/v1/admin/entitlements/assignments/acme" \
  --header "Authorization: Bearer $KANGAL_SUPER_ADMIN_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
    "plan_code": "serverless_team",
    "reason": "Approved order form"
  }'
```

Assignments can include `starts_at` and `expires_at`; expiry must be after the start. Set a temporary feature override with a documented reason:

```bash
curl --request PUT "$KANGAL_URL/api/v1/admin/entitlements/overrides/acme/plugin.manage" \
  --header "Authorization: Bearer $KANGAL_SUPER_ADMIN_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
    "limit": 50,
    "mode": "hard",
    "reason": "Temporary migration capacity",
    "expires_at": "2026-08-01T00:00:00Z"
  }'
```

Use `unlimited: true` instead of `limit` to remove a numeric limit. Every assignment, override, deletion, and usage reset is audit logged.

## Enforced operations

The entitlement catalog applies to gateway creation, route creation, plugin creation and package publish/install, AI configuration creation, AI request runtime, integration install, developer portal management, and portal application registration. Delete operations for covered lifetime resources release their usage unit.

## Metering data quality

| Quality     | How it is produced                                                                                                  | Can be finalized? |
| ----------- | ------------------------------------------------------------------------------------------------------------------- | ----------------- |
| Exact       | Explicit period traffic logs plus exact successful AI events with billed cost, read from a proven database snapshot | Yes               |
| Estimated   | Seven-day aggregates scaled to the requested window, or AI events with estimated rather than billed cost            | No                |
| Unavailable | Required source cannot be read or exact AI evidence is missing                                                      | No                |

For explicit periods, requests come from traffic logs. AI cache hits, failures, disconnects, and responses with error status do not add tokens or cost. A successful AI event needs token usage and non-negative billed cost to be exact.

## Durable billing workflows

### Ledger snapshot

1. `POST /api/v1/admin/metering-billing/ledger/snapshots` with tenant, optional gateway, and either `days` or explicit period bounds.
2. Inspect `data_quality`, `billing_eligible`, summary, digest, and the server-generated `source_watermark`.
3. Finalize only an exact eligible draft with `POST /api/v1/admin/metering-billing/ledger/snapshots/{ledger_id}/finalize`.
4. Export by finalized `ledger_id`.

### Close a billing period

`POST /api/v1/admin/metering-billing/periods/close` atomically persists the exact usage period, finalized invoice, and reconciliation evidence. The request requires explicit `period_start` and `period_end`.

```bash
curl --request POST "$KANGAL_URL/api/v1/admin/metering-billing/periods/close" \
  --header "Authorization: Bearer $KANGAL_SUPER_ADMIN_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
    "tenant_id": "acme",
    "period_start": "2026-06-01T00:00:00Z",
    "period_end": "2026-07-01T00:00:00Z"
  }'
```

The server captures the usage boundary. Clients should normally omit `source_watermark`; if supplied to validate a retry, it must exactly match the server-derived value from the prior response. The same scope, period, and watermark are idempotent. A retry can finish a partial persisted close without using a second monthly slot.

### Billing-close entitlement

Configure billing enablement, `period_close_enabled`, and `monthly_period_close_quota` for each tenant before closing a period. The operations status endpoint reports whether closing is enabled and shows closed and remaining slots. Concurrent distinct closes cannot consume the same quota slot.

## Integrity requirements

Exact snapshot and period close require database snapshot-transaction support. If Kangal cannot prove a consistent usage boundary, it fails with `409` instead of finalizing an uncertain invoice. Finalized snapshots are immutable and are exported from persisted content rather than recalculated live. Digest or watermark tampering also fails closed.

## API reference

| Operation                | Endpoint                                                                    |
| ------------------------ | --------------------------------------------------------------------------- |
| Catalog                  | `GET /api/v1/admin/entitlements/catalog`                                    |
| Tenant status            | `GET /api/v1/admin/entitlements/status?tenant_id={tenant_id}`               |
| Entitlement history      | `GET /api/v1/admin/entitlements/history?tenant_id={tenant_id}`              |
| Assign plan              | `PUT /api/v1/admin/entitlements/assignments/{tenant_id}`                    |
| Set override             | `PUT /api/v1/admin/entitlements/overrides/{tenant_id}/{feature_key}`        |
| Delete override          | `DELETE /api/v1/admin/entitlements/overrides/{tenant_id}/{feature_key}`     |
| Reset usage              | `POST /api/v1/admin/entitlements/usage/{tenant_id}/{feature_key}/reset`     |
| Metering preview         | `GET /api/v1/admin/metering-billing/summary`                                |
| Create ledger snapshot   | `POST /api/v1/admin/metering-billing/ledger/snapshots`                      |
| Get ledger snapshot      | `GET /api/v1/admin/metering-billing/ledger/snapshots/{ledger_id}`           |
| Finalize ledger snapshot | `POST /api/v1/admin/metering-billing/ledger/snapshots/{ledger_id}/finalize` |
| Close billing period     | `POST /api/v1/admin/metering-billing/periods/close`                         |
| Get billing period       | `GET /api/v1/admin/metering-billing/periods/{period_id}`                    |
| Operations status        | `GET /api/v1/admin/metering-billing/operations/status`                      |
| Export invoice           | `GET /api/v1/admin/metering-billing/invoice/export`                         |

## Troubleshooting

| Symptom                                       | Check                                                                                                            |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `402 plan_required`                           | Assign a valid plan, then verify its catalog version and start/expiry.                                           |
| `403 feature_not_entitled`                    | Inspect effective feature and active override.                                                                   |
| `429 quota_exceeded`                          | Check used plus reserved units and the reset period.                                                             |
| Status is already over limit after assignment | Existing lifetime resources were reconciled; remove resources or change the contract limit.                      |
| Soft quota did not block                      | Soft mode warns and allows overage by design.                                                                    |
| Ledger finalization returns `409`             | Data is estimated/unavailable, snapshot proof is absent, the draft was tampered with, or the watermark is wrong. |
| Period close returns `403`                    | Billing or period-close entitlement is disabled.                                                                 |
| Period close says monthly quota is exhausted  | Wait for the next UTC month or have an operator change the tenant billing entitlement.                           |
| Preview and entitlement plans differ          | Entitlement assignment is tenant-level; billing reads each gateway's `plan_code`. Reconcile both configurations. |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kangal.dev/metering-and-billing/entitlements-metering.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
