Amba
SDKsFeatures

Entitlements

Check if a user has access to a feature — `has()` for one, `list()` for all. Safe for paywall gating.

Entitlements are named permissions a user holds — typically tied to a subscription or one-time purchase. The SDK exposes two operations: has(name) to gate a specific feature, and list() to enumerate everything the user has.

Entitlement state is sourced from your billing system (RevenueCat, Stripe, App Store, Play Store) via webhooks; the SDK reads the resolved state for the signed-in user.

Quick start

import { Amba } from '@layers/amba-web';
 
if (await Amba.entitlements.has('pro')) {
  showProFeature();
} else {
  showUpgradePrompt();
}

Operations

has(name) — single check

Returns true if the user has the named entitlement and it's currently active. Defaults to false during loading or on error — safe for paywall gating (no flash-of-pro-content).

const isPro = await Amba.entitlements.has('pro');

list() — all entitlements

Returns every entitlement the user holds, with the purchase that backs it (store, product, period, dates) and its active status. Each entitlement mirrors GET /v1/client/entitlements field-for-field:

FieldTypeNotes
entitlement_idstringProject-defined entitlement key (e.g. "pro") — what has() checks.
is_activebooleanWhether it's currently active.
product_idstring | nullStore product / SKU the grant resolved from, if any.
store"app_store" | "play_store" | "web" | nullSource store, or null for a grant not tied to a store purchase.
purchase_datestring | nullISO-8601 purchase time, or null.
expiration_datestring | nullISO-8601 expiry, or null for lifetime.
period_type"trial" | "intro" | "normal" | nullSubscription period phase, or null.
const all = await Amba.entitlements.list();
// [
//   {
//     entitlement_id: 'pro',
//     is_active: true,
//     product_id: 'com.app.pro.monthly',
//     store: 'app_store',          // 'app_store' | 'play_store' | 'web'
//     period_type: 'normal',       // 'trial' | 'intro' | 'normal'
//     purchase_date: '2026-01-01T00:00:00Z',
//     expiration_date: '2026-12-31T00:00:00Z',
//   },
//   ...
// ]

React hook

import { useEntitlement } from '@layers/amba-react';
 
function ProGate({ children }: { children: ReactNode }) {
  const isPro = useEntitlement('pro');
  return isPro ? <>{children}</> : <Paywall />;
}

The hook defaults to false while the initial fetch is in flight. Safe to use in render without an explicit loading guard for paywall logic.

restore() — Restore Purchases

Re-syncs the user's owned entitlements from the configured subscription service and returns the active set. App Store review (3.1.1) requires a visible Restore Purchases action for any app selling subscriptions — wire this to a button.

try {
  const { restored_count, entitlements } = await Amba.entitlements.restore();
} catch {
  // Fail-closed: the server couldn't reach the subscription service, so the
  // user's access is UNCHANGED. Show a retry message — never lock them out.
}

restore() is fail-closed: a transport failure rejects and leaves access exactly as it was (no silent unlock or revoke). A user with no prior purchases succeeds with restored_count: 0.

offerings() — the paywall read

Returns the offerings your paywall renders, each with its packages and the product behind each (display fields, trial info, per-store identifiers). Provider-neutral.

const { offerings, current_offering_id } = await Amba.offerings();
const current = offerings.find((o) => o.current);
// current.packages[].product → { product_id, display_name, store_product_refs, … }
 
// Or fetch one offering by id:
const { offerings: holiday } = await Amba.offerings('holiday_2026');

Declare products and offerings with the amba_products_create / amba_offerings_create MCP tools (or the /admin/projects/:projectId/subscriptions/* API). Render the packages, let the user purchase through the platform store, then gate features with has().

Patterns

Paywall gating

function NewFeature() {
  const isPro = useEntitlement('pro');
  if (!isPro) {
    return <UpgradePrompt feature="new-feature" />;
  }
  return <FullFeature />;
}

Hybrid client + server check

For revenue-critical actions, double-check on the server. Client-side checks are great UX (instant gating) but the server is source-of-truth:

// Client gates the UI
if (await Amba.entitlements.has('pro')) {
  await callProOnlyFunction();
}
 
// Server function also checks — defense in depth.
// Amba functions are ES modules with the `{ async fetch(req, env, ctx) }`
// shape; bindings are injected into `env`. The X-Amba-User-Id header
// is the signed user identity from the edge router. AMBA_INTERNAL_TOKEN
// is a project-scoped Bearer for /admin/projects/${env.AMBA_PROJECT_ID}/*.
export default {
  async fetch(req, env, ctx) {
    const userId = req.headers.get('X-Amba-User-Id');
    const res = await fetch(
      `${env.AMBA_API_URL}/v1/admin/projects/${env.AMBA_PROJECT_ID}/users/${userId}/entitlements`,
      { headers: { Authorization: `Bearer ${env.AMBA_INTERNAL_TOKEN}` } },
    );
    const { data } = await res.json();
    if (!data.some((e) => e.entitlement_id === 'pro' && e.is_active)) {
      return Response.json({ error: 'pro_required' }, { status: 403 });
    }
    // ... do the pro-only thing
    return Response.json({ ok: true });
  },
};

Multi-tier products

Many apps ship pro / team / enterprise tiers. Model each as its own entitlement and check the highest-applicable:

const list = await Amba.entitlements.list();
const tier = list.find((e) => e.is_active)?.entitlement_id; // 'enterprise' | 'team' | 'pro' | undefined

Limits

  • Entitlement names: lowercase, alphanumeric + underscore + dash, up to 64 characters.
  • Refresh cadence: the SDK caches results for ~60 seconds per user; subsequent has() calls within that window are served from cache. Force-refresh via list() (which always hits the server).
  • Source-of-truth: entitlement state is set by your billing webhooks. The SDK reads it; it cannot grant or revoke.
  • Paywall safety: has() returns false on network failure or unknown entitlement name. Plan UI to fail-closed.

Reference

On this page