> ## Documentation Index
> Fetch the complete documentation index at: https://help.the-meridian.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Feature gating

A feature is a capability a plan grants. You define features in the [plan builder](/introduction-13) and attach them to plans; the SDK answers "does this shop's plan grant it?" at runtime, with no network call in the render path.

```tsx theme={null}
const { isEnabled, getLimit } = useMeridian()

if (!isEnabled("advanced_reports")) return <UpgradePrompt />

const seats = getLimit("team_seats")   // 5, or undefined
```

## Boolean and numeric features

A feature is either on/off or carries a number.

| Kind | Read with | Example |
| - | - | - |
| Boolean | `isEnabled(key)` | Advanced reports, priority support |
| Numeric | `isEnabled(key)` for access, `getLimit(key)` for the amount | 5 team seats, 200 products |

A numeric feature is still enabled or not. The limit is the amount the plan sets on top of that. Check both: an enabled feature with no limit configured returns `undefined` from `getLimit`, not `0`.

## Keys are the contract

The string you pass is the feature's **key**: the app-local stable identifier from the plan builder. A key survives a rename in the dashboard.

<Warning>
  A key that does not exist reads as `false`, and a missing limit reads as `undefined`. A typo therefore gates nothing and reports no error. Read the real keys from the plan builder, or from Meridian MCP's `features_list`, rather than retyping them.
</Warning>

## Gating on the server

The same gates exist without React, for resource routes, Shopify extensions and background jobs:

```ts theme={null}
const gates = await meridian.gatesForShop(session.shop)

if (gates.can("advanced_reports")) { /* … */ }
const seats = gates.limitOf("team_seats")
```

`gatesForShop` resolves the shop's entitlements in one call and never throws. An unconfigured, unreachable or unsubscribed shop comes back **ungated**. If you already hold a shop token, `createMeridianServerClient(...).getGates()` does the same, and `checkFeature(key)` reads a single feature. See [Server client](/sdk-server-client).

Entitlements resolve through one path everywhere, so a gate behaves identically in your React code, on your server, and in [Meridian MCP](/mcp-server)'s `entitlements_explain`.

## Fail open or fail closed

The SDK fails **open**: a null snapshot, a failed load, or an unreachable Meridian all read as ungated, so a billing outage cannot lock a paying merchant out.

That suits a paid capability and does not suit anything that costs you money per call. Where a wrong `false` is expensive, check `loading` and `error` from [`useMeridian`](/sdk-use-meridian) and hold the action.

## Gating on usage

A limit a shop consumes over time is an [event](/events) rather than a feature. `getUsage(eventKey)` returns what the plan includes and what has been used this period.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.