Get started with Entitlement Enforcement

TL;DR

The Entitlement Enforcement plugin enforces per-customer, per-feature access by polling the Metering & Billing Entitlement Access API and blocking requests when a customer has no access or has exhausted a usage limit.

In this tutorial you’ll set up the full Metering & Billing product catalog needed for enforcement: a meter, a metered feature, a plan with an entitlement, a customer, and a subscription. Then, enable the Entitlement Enforcement plugin on a Kong Gateway Route to enforce that entitlement.

Prerequisites

This is a Konnect tutorial and requires a Konnect personal access token.

  1. Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.

  2. Export your token to an environment variable:

    export KONNECT_TOKEN='YOUR_KONNECT_PAT'
  3. Run the quickstart script to automatically provision a Control Plane and Data Plane, and configure your environment:

    curl -Ls https://get.konghq.com/quickstart | bash -s -- -k $KONNECT_TOKEN \
         --deck-output

    This sets up a Konnect Control Plane named quickstart, provisions a local Data Plane, and prints out the following environment variable exports:

    export DECK_KONNECT_TOKEN=$KONNECT_TOKEN
    export DECK_KONNECT_CONTROL_PLANE_NAME=quickstart
    export DECK_KONNECT_ADDR=https://us.api.konghq.com
    export KONNECT_CONTROL_PLANE_URL=https://us.api.konghq.com
    export KONNECT_PROXY_URL='http://localhost:8000'

    Copy and paste these into your terminal to configure your session.

To complete this tutorial, install decK. We recommend keeping decK up to date with the latest version (1.66.1).

decK is a CLI tool for managing Kong Gateway declaratively with state files. This guide uses deck gateway apply, which directly applies entity configuration to your Gateway instance.

You can check your current decK version with deck version.

For this tutorial, you’ll need Kong Gateway entities, like Gateway Services and Routes, pre-configured. These entities are essential for Kong Gateway to function but installing them isn’t the focus of this guide. Follow these steps to pre-configure them:

  1. Run the following command:

    echo '
    _format_version: "3.0"
    services:
      - name: example-service
        url: http://httpbin.konghq.com/anything
    routes:
      - name: example-route
        paths:
        - "/anything"
        service:
          name: example-service
        protocols:
        - http
        - https
    ' | deck gateway apply -

To learn more about entities, you can read our entities documentation.

You need the Metering & Billing Admin role in Konnect to configure Metering & Billing.

You need a Konnect system account token (spat_) with the Ingest role for Metering. This token authenticates the Metering & Billing plugin when it sends events to the Konnect ingest endpoint.

Export your system account token:

export DECK_AUTH_TOKEN='YOUR SPAT TOKEN'

For more information, see system accounts and access tokens.

You need a Konnect system account token (spat_) with the Entitlement Access role for Metering. This role grants the permission to query entitlement access information for the Metering & Billing Entitlement Access API. This token authenticates the Entitlement Enforcement plugin when it checks customer access.

This is a different token from the Ingest token used by the Metering & Billing plugin: ingesting events and querying entitlement access are separate permissions.

Export your system account token:

export DECK_ENTITLEMENT_ACCESS_TOKEN='YOUR SPAT'

For more information, see system accounts and access tokens.

To complete this tutorial, make sure you have the following:

  • A Redis Stack running and accessible from the environment where Kong is deployed.
  • Port 6379, or your custom Redis port is open and reachable from Kong.
  • Redis host set as an environment variable so the plugin can connect:

    export DECK_REDIS_HOST='YOUR-REDIS-HOST'

If you’re testing locally with Docker, use host.docker.internal as the host value.

This guide shows how to enforce Metering & Billing entitlements on Kong Gateway API traffic with the Entitlement Enforcement plugin. Unlike the Metering & Billing plugin, which only meters usage, the Entitlement Enforcement plugin actively blocks requests: it polls the Metering & Billing Entitlement Access API for each customer and returns an error when the customer has no access to a feature or has exhausted a usage limit.

In this guide, you’ll:

  • Create a Kong Gateway Consumer that you’ll map to a customer
  • Set up a meter for Kong Gateway API requests
  • Create a metered feature
  • Create a plan that grants that feature as an entitlement, and publish it
  • Create a customer and start a subscription so the entitlement is active
  • Enable the Metering & Billing plugin to report usage, and the Entitlement Enforcement plugin to enforce the entitlements
  • Verify that traffic is allowed within the limit and blocked once the limit is reached

Enforcement needs configuration on both sides, and the pieces reference each other. The following table lists what you’ll create, why each piece is needed, and what it connects to:

What you configure

Where

Why it’s needed

example-service and example-route Prerequisite The entities the plugins attach to. The Entitlement Enforcement plugin is enabled on the Route, so it runs for traffic matching that Route only.
Two Konnect system account tokens Prerequisite One with the Ingest role for reporting usage, one with the Entitlement Access role for reading entitlements. The roles are separate, so a single token can’t do both unless it has the Admin role.
Redis Prerequisite Where the plugin caches enforcement state: a background timer writes the state it fetches from Metering & Billing, and each worker syncs its local cache from Redis. The plugin can’t enforce without a reachable Redis.
Consumer with a pinned id Kong Gateway Identifies the client. The pinned id fixes the consumer:<id> subject key that both plugins use, so the customer you create later can be matched to it.
Key Auth plugin Globally Authenticates the request so Kong Gateway can resolve a Consumer. Without an authenticated Consumer, there’s no customer to enforce against.
Meter Metering & Billing Counts raw API requests. This is the usage the entitlement limit is measured against.
Metered feature Metering & Billing Makes the meter’s usage enforceable. Its key is what you set as the plugin’s feature.key.
Plan with a metered entitlement, published Metering & Billing Defines the allowance, for example 5 requests per month, on a rate card that references the feature.
Customer with a matching subject key Metering & Billing The entity whose access is enforced. Its usage_attribution.subject_keys must contain the Consumer’s subject key, or usage and enforcement won’t resolve to this customer.
Subscription to the plan Metering & Billing Materializes the entitlement onto the customer. Until the subscription starts, the customer has no entitlement to enforce.
Metering & Billing plugin, with the Ingest token example-service Reports usage events. Nothing counts against the limit unless usage is reported, and allow_status_codes keeps failed responses out of that usage.
Entitlement Enforcement plugin, with the Entitlement Access token example-route Reads the customer’s entitlement for the configured feature and blocks the request when they’re over the limit.

The following diagram shows how those pieces relate:

 
flowchart TB
  client(["Client (API key)"])
  subgraph gateway["Kong Gateway"]
        route["example-route"]
        service["example-service"]
        consumer1["Consumer (Kong Air)"]
        enforcement["Entitlement Enforcement plugin"]
        metering["Metering & Billing plugin"]
  end
  redis[("Redis
enforcement cache")] subgraph mb["Konnect Metering & Billing"] events["Events API"] access["Entitlement Access API"] meter["Meter"] subgraph plan["Premium Plan"] feature2["Metered feature + entitlement (limit)"] end subgraph subscription["Premium Subscription"] customer1["Customer (Kong Air)"] end end client -->|request| route route -.->|enforced by| enforcement route -->|allowed| service service -.->|metered by| metering client -.->|authenticates as| consumer1 consumer1 -->|subject key| customer1 metering -->|usage events, Ingest token| events events -->|aggregated by| meter meter -->|referenced by| feature2 subscription -->|activates| plan enforcement -->|queries, Entitlement Access token| access access -->|reads entitlement| customer1 enforcement <-->|cached state| redis feature2 -.->|feature.key| enforcement

Create a Consumer

Before you configure Metering & Billing, set up a Consumer (in this example, kong-air. Consumers let you identify the client that’s interacting with Kong Gateway. Later in this guide, you’ll map this Consumer to a customer in Metering & Billing.

The Entitlement Enforcement plugin identifies the customer from the request’s Consumer and sends the subject key consumer:<consumer-id> to the Entitlement Access API. To keep that subject key predictable, this guide sets an explicit id on the Consumer so you can reference it directly when you create the customer.

You’re going to use key authentication in this tutorial, so the Consumer needs an API key to access any Kong Gateway Services.

echo '
_format_version: "3.0"
consumers:
  - id: a3d1f5e2-1b2c-4d3e-9f80-000000000001
    username: kong-air
    keyauth_credentials:
    - key: air-key
' | deck gateway apply -

Enable authentication

Authentication lets you identify a Consumer so you can enforce their entitlements as a customer. This example uses the Key Authentication plugin, but you can use any authentication plugin that you prefer.

Enable the plugin globally, which means it applies to all Kong Gateway Services and Routes:

echo '
_format_version: "3.0"
plugins:
  - name: key-auth
    config:
      key_names:
      - apikey
' | deck gateway apply -

Create a meter

In Metering & Billing, meters track and record the consumption of a resource over time. Create a meter to count API requests. The command captures the new meter’s ID so you can reference it when you create the metered feature:

METER_ID=$(curl -X POST "https://us.api.konghq.com/v3/openmeter/meters" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "API requests",
       "key": "api_requests_total",
       "description": "Number of API requests",
       "event_type": "kong.api_request",
       "aggregation": "count",
       "dimensions": {
         "request_method": "$.request_method",
         "route_name": "$.route_name",
         "service_name": "$.service_name"
       }
     }' | jq -r ".id"
)

Create a feature

Meters collect raw usage, but features make that usage enforceable. Create a metered feature that references the meter, so its entitlement can carry the usage limit that the Entitlement Enforcement plugin enforces. The command captures the new feature’s ID so you can reference it when you create the plan:

METERED_FEATURE_ID=$(curl -X POST "https://us.api.konghq.com/v3/openmeter/features" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "Premium API access",
       "key": "premium_api_access",
       "meter": {
         "id": "'$METER_ID'"
       }
     }' | jq -r ".id"
)

Enable the Metering & Billing plugin

The Entitlement Enforcement plugin enforces a usage limit, but something has to report the usage that counts against it. Configure the Metering & Billing plugin on example-service to emit an API request event for every request. Because both plugins use consumer lookup, the usage the Metering & Billing plugin reports is attributed to the same consumer:<consumer-id> subject that the Entitlement Enforcement plugin checks.

echo '
_format_version: "3.0"
plugins:
  - name: metering-and-billing
    service: example-service
    config:
      ingest_endpoint: https://us.api.konghq.com/v3/openmeter/events
      api_token: "${{ env "DECK_AUTH_TOKEN" }}"
      ssl_verify: true
      meter_api_requests: true
      meter_ai_token_usage: false
      subject:
        look_up_value_in: consumer
      allow_status_codes:
      - 200-299
' | deck gateway apply -

allow_status_codes limits metering to the response codes you list, which is 200-299 in this example. The two plugins work independently, so without allow_status_codes, the Metering & Billing plugin also reports usage for failed requests, and a customer who already reached their usage limit keeps spending it on requests the Entitlement Enforcement plugin rejects.

For alternatives, see Excluding blocked requests from usage.

Create a plan with entitlements

Plans are the core building blocks of your product catalog. A plan is a collection of rate cards, where each rate card ties a feature to a price and an optional entitlement. The entitlement is what the Entitlement Enforcement plugin reads to decide access.

Create a Premium plan with one rate card: the metered feature, granted a limit of 5 requests per month. The rate card uses a free price, because enforcement depends only on the entitlement, not on price:

PLAN_ID=$(curl -X POST "https://us.api.konghq.com/v3/openmeter/plans" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "Premium",
       "key": "premium",
       "currency": "USD",
       "billing_cadence": "P1M",
       "phases": [
         {
           "name": "Main",
           "key": "main",
           "rate_cards": [
             {
               "name": "Premium API access",
               "key": "premium_api_access",
               "feature": {
                 "id": "'$METERED_FEATURE_ID'"
               },
               "billing_cadence": "P1M",
               "price": {
                 "type": "free"
               },
               "entitlement": {
                 "type": "metered",
                 "is_soft_limit": false,
                 "usage_period": "P1M",
                 "limit": 5
               }
             }
           ]
         }
       ]
     }' | jq -r ".id"
)

Publish the plan so you can subscribe customers to it:

curl -X POST "https://us.api.konghq.com/v3/openmeter/plans/$PLAN_ID/publish" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN"

Create a customer and subscription

A customer is the entity whose access is enforced. The customer’s usage-attribution subject key must match the subject the Entitlement Enforcement plugin sends, which is consumer: followed by the Consumer ID you set earlier.

This guide creates the customer through the API so that the subject key can be set to the exact consumer:<consumer-id> value. In the Metering & Billing UI, the Include usage from dropdown only lists subjects that have already sent events, so a freshly created subject key isn’t selectable yet.

Create the customer with the matching subject key:

curl -X POST "https://us.api.konghq.com/v3/openmeter/customers" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "name": "Kong Air",
       "key": "kong-air",
       "currency": "USD",
       "usage_attribution": {
         "subject_keys": [
           "consumer:a3d1f5e2-1b2c-4d3e-9f80-000000000001"
         ]
       }
     }'

Now subscribe the customer to the Premium plan. The subscription becomes active as soon as you create it, which materializes the metered entitlement onto the customer:

curl -X POST "https://us.api.konghq.com/v3/openmeter/subscriptions" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "customer": {
         "key": "kong-air"
       },
       "plan": {
         "key": "premium"
       }
     }'

Enable the Entitlement Enforcement plugin

Enable the Entitlement Enforcement plugin on example-route. It points at the Entitlement Access API, enforces the premium_api_access feature, reads the customer from the request’s Consumer, and uses your Redis instance as its enforcement cache at config.redis. Note that api_token uses the Entitlement Access token, not the Ingest token the Metering & Billing plugin uses:

echo '
_format_version: "3.0"
plugins:
  - name: entitlement-enforcement
    route: example-route
    config:
      entitlement_access_endpoint: https://us.api.konghq.com/v3/openmeter/entitlement-access/query
      api_token: "${{ env "DECK_ENTITLEMENT_ACCESS_TOKEN" }}"
      ssl_verify: true
      feature:
        key: premium_api_access
      customer:
        look_up_value_in: consumer
      refresh_interval: 3
      redis:
        host: "${{ env "DECK_REDIS_HOST" }}"
        port: 6379
        ssl_verify: true
' | deck gateway apply -

This configuration relies on the plugin’s defaults for the rest of its behavior. For every available setting, see the Entitlement Enforcement plugin reference:

  • deny_unknown_customers defaults to true, so a request whose customer can’t be resolved is blocked.
  • fail_policy defaults to allow, so if the enforcement state can’t be retrieved, requests are allowed through.
  • response_codes defaults return 429 when a usage limit is reached, and 403 for feature or customer errors.

Each plugin instance enforces exactly one feature.key. Boolean entitlements are evaluated the same way as metered ones: if the customer doesn’t have the feature, the request is denied with 403. To enforce more than one feature, enable a plugin instance per feature on the Routes that serve it. For the full list of denial reasons and their default responses, see Enforcement decisions and response codes.

refresh_interval is set to 3 seconds here so the tutorial responds quickly. In production, use a higher interval to reduce load on the Entitlement Access API.

Validate

Send requests to your Route with the Kong Air API key:

for _ in {1..8}; do
  curl -i $KONNECT_PROXY_URL/anything \
       -H "apikey:air-key"
  echo
  sleep 1
done

Expect the following progression:

  • Cold start: The first requests may return 403 with "Customer is not found by subject." The Entitlement Enforcement plugin hasn’t polled the customer’s state yet. It records the subject and fetches its entitlements on the next poll (every refresh_interval seconds), so retry for up to a minute.
  • Within the limit: Once the state is loaded, requests return 200. The customer has access to premium_api_access and hasn’t reached the limit of 5.
  • Limit reached: As the Metering & Billing plugin reports usage and it crosses 5 requests in the period, the Entitlement Enforcement plugin blocks further requests with 429 and "Customer has reached usage limit for feature."

There’s a short delay between metering a request and the Entitlement Access API reflecting the new usage, and another delay while the plugin polls the updated state. If you don’t see 429 immediately after the sixth request, keep sending requests for a few more seconds.

Query the Entitlement Access API directly

To confirm the customer’s access independent of the plugin’s cache, you can call the same endpoint the plugin uses. This reports has_access per feature for the subject:

The Entitlement Access query endpoint is an internal, unstable API. It may change without notice. Use it for verification only, and do not use it for production integrations.

curl -X POST "https://us.api.konghq.com/v3/openmeter/entitlement-access/query" \
     --no-progress-meter --fail-with-body  \
     -H "Authorization: Bearer $KONNECT_TOKEN" \
     --json '{
       "customer": {
         "keys": [
           "consumer:a3d1f5e2-1b2c-4d3e-9f80-000000000001"
         ]
       },
       "feature": {
         "keys": [
           "premium_api_access"
         ]
       }
     }'

In the response, data[0].features.premium_api_access.has_access is true while the customer is within the limit and false once the limit is reached, with a reason.code of usage_limit_reached.

Entitlement Enforcement enforces, metering doesn’t: the Metering & Billing plugin only reports usage, but the Entitlement Enforcement plugin blocks traffic based on entitlements. Customize the response_codes in the plugin configuration to control the status code and message returned for each denial reason.

Cleanup

If you created a new control plane and want to conserve your free trial credits or avoid unnecessary charges, delete the new control plane used in this tutorial.

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!