Checking Feature Access

Query a customer's entitlements on every request using the hot-path check endpoint.

Two Entitlement Query Endpoints

ABAXUS provides two endpoints for querying entitlements, optimized for different use cases:

  • GET /v1/entitlements — the full resolved entitlement set for a customer (all features)
  • GET /v1/entitlements/check — a single-feature check optimized for the hot path

GET /v1/entitlements — Full Resolved Set

Returns all entitlements for a customer, resolved across all active subscriptions. Use this for loading a customer’s access profile during session initialization, building a “your plan includes” UI, or bootstrapping a local cache.

1
2
curl "$ABAXUS_URL/v1/entitlements?customer_id=cust_acme" \
  -H "Authorization: Bearer $ABAXUS_KEY"

Response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
  "customer_id": "cust_acme",
  "resolved_at": "2026-04-03T10:30:00Z",
  "entitlements": [
    {
      "feature_key": "advanced_analytics",
      "type": "boolean",
      "granted": true,
      "source_plan": "plan_growth"
    },
    {
      "feature_key": "api_calls_per_month",
      "type": "limit",
      "limit": 600000,
      "current_usage": 47293,
      "remaining": 552707,
      "granted": true,
      "exceeded": false,
      "source_plans": ["plan_growth", "plan_addon_api"]
    },
    {
      "feature_key": "export_formats",
      "type": "boolean",
      "granted": false,
      "source_plan": "plan_growth"
    },
    {
      "feature_key": "ai_model",
      "type": "custom",
      "value": "gpt-4o",
      "source_plan": "plan_growth_v2"
    }
  ]
}

The source_plan field tells you which plan version is responsible for each entitlement value, which is useful for debugging and for displaying plan-tier-specific messaging to customers.


GET /v1/entitlements/check — Hot Path

The check endpoint answers a single yes/no question: “Does this customer have access to this feature right now?” It’s designed to be called on every request in your product’s hot path.

1
2
curl "$ABAXUS_URL/v1/entitlements/check?customer_id=cust_acme&feature_key=advanced_analytics" \
  -H "Authorization: Bearer $ABAXUS_KEY"

Response (boolean, granted):

1
2
3
4
5
{
  "customer_id": "cust_acme",
  "feature_key": "advanced_analytics",
  "granted": true
}

For limit-type entitlements:

1
2
curl "$ABAXUS_URL/v1/entitlements/check?customer_id=cust_acme&feature_key=api_calls_per_month" \
  -H "Authorization: Bearer $ABAXUS_KEY"

Response (limit, not exceeded):

1
2
3
4
5
6
7
8
9
{
  "customer_id": "cust_acme",
  "feature_key": "api_calls_per_month",
  "granted": true,
  "exceeded": false,
  "limit": 600000,
  "current_usage": 47293,
  "remaining": 552707
}

Response (limit, exceeded):

1
2
3
4
5
6
7
8
9
{
  "customer_id": "cust_acme",
  "feature_key": "api_calls_per_month",
  "granted": true,
  "exceeded": true,
  "limit": 600000,
  "current_usage": 604218,
  "remaining": -4218
}

Note the distinction between granted and exceeded. A customer can have a limit entitlement that is granted: true (they have this feature) but exceeded: true (they’ve gone over their limit). What you do with that depends on your enforcement pattern — see Enforcement Patterns.


The granted vs exceeded Distinction

For boolean entitlements, only granted matters:

  • granted: true — the customer has this feature
  • granted: false — the customer does not have this feature (not on a plan that includes it)

For limit entitlements, both fields matter:

  • granted: true, exceeded: false — the customer has the feature and is within their limit
  • granted: true, exceeded: true — the customer has the feature but has gone over their limit
  • granted: false — the customer has no limit defined (feature not on any of their plans)

Caching Recommendations

The check endpoint is fast (< 20ms typical), but calling it on every individual request in a high-throughput service can add latency. Two common caching patterns:

Session-level cache: On user authentication, call GET /v1/entitlements to load the full entitlement set and store it in the session. Check your local cache on each request. Refresh the cache when subscriptions change (use webhooks for this, or refresh on a short TTL of 60 seconds).

CDN/edge cache: The check endpoint response can be cached at the edge for short TTLs (15–60 seconds). This is appropriate for features that change infrequently. Set Cache-Control: public, max-age=30 on the client side when consuming the API.

For critical enforcement decisions (blocking a payment, preventing an action with significant consequences), always hit the live check endpoint without caching. The small latency cost is worth the correctness guarantee.


Using the Publishable Key for Client-Side Checks

For entitlement checks that run in browser or mobile clients, use the publishable pk_... key instead of the secret sk_... key. The publishable key can only call read endpoints and specifically the entitlement check endpoint — it cannot create invoices, modify customers, or access billing data. This makes it safe to embed in client-side code.

1
2
3
4
5
const response = await fetch(
  `${ABAXUS_URL}/v1/entitlements/check?customer_id=${customerId}&feature_key=advanced_analytics`,
  { headers: { 'Authorization': `Bearer ${ABAXUS_PUBLISHABLE_KEY}` } }
);
const { granted } = await response.json();