# Reading Subscription Data

When the `MonetizationInboundPolicy` authenticates a request, it looks up the
caller's subscription — their plan, entitlements, payment status, and billing
dates — and stores it on the request context. Read that data from your own code
with the static `MonetizationInboundPolicy.getSubscriptionData` method to make
decisions, personalize responses, or log which plan a request ran on.

## Where you can call it

`getSubscriptionData` returns data that the monetization policy puts on the
context, so it only returns a value **after** the `monetization-inbound` policy
has run. Call it from:

- A **custom code inbound policy** placed _after_ `monetization-inbound` in the
  route's inbound pipeline.
- The **route handler**, which always runs after inbound policies.

On a route without the monetization policy — or in a policy that runs before it
— the method returns `undefined`. Always handle that case.

:::note

The `monetization-inbound` policy must come before any policy that reads the
subscription. If your policy runs first, the subscription data isn't on the
context yet. See
[pipeline ordering](./monetization-policy.md#pipeline-ordering).

:::

## Basic usage

```ts
import {
  MonetizationInboundPolicy,
  HttpProblems,
  ZuploContext,
  ZuploRequest,
} from "@zuplo/runtime";

export default async function (request: ZuploRequest, context: ZuploContext) {
  const subscription = MonetizationInboundPolicy.getSubscriptionData(context);

  if (!subscription) {
    return HttpProblems.forbidden(request, context, {
      detail: "No active subscription",
    });
  }

  context.log.info(`Request on plan: ${subscription.plan.key}`);
  return request;
}
```

## The subscription object

`getSubscriptionData` returns a `MonetizationSubscription`. The fields you reach
for most are the plan and the entitlements map:

```ts
interface MonetizationSubscription {
  id: string;
  customerId: string;
  name: string;
  status: string;
  currency: string;

  plan: {
    id: string;
    name: string;
    key: string; // Stable identifier — switch on this in code
    version: number;
    description?: string;
  };

  // Keyed by meter or feature key
  entitlements: Record<
    string,
    {
      balance: number; // Remaining allowance this period
      hasAccess: boolean; // false when no access or quota spent
      overage: number; // Usage beyond the included allowance
      usage: number; // Consumed this period
    }
  >;

  paymentStatus?: {
    status: "paid" | "not_required" | "pending" | "failed" | "uncollectible";
    isFirstPayment: boolean;
    lastPaymentFailedAt?: string;
    lastPaymentSucceededAt?: string;
  };

  billingCadence: string; // ISO 8601 duration, e.g. "P1M" for monthly
  billingAnchor: string;
  nextBillingDate: string;
  activeFrom: string;
  activeTo?: string;

  maxPaymentOverdueDays: number;
  accessBlocked?: boolean;
  createdAt: string;
  updatedAt: string;
}
```

Switch on `plan.key` rather than `plan.name` in your logic — the key is a stable
identifier, while the name is a display label that can change.

## Reading entitlements

Each entry in `entitlements` describes one metered feature or static feature on
the subscription. The key is the meter or feature key; the value reports the
caller's standing against it:

```ts
const subscription = MonetizationInboundPolicy.getSubscriptionData(context);

const apiCalls = subscription?.entitlements["api_requests"];
if (apiCalls) {
  context.log.info(
    `api_requests — used ${apiCalls.usage}, ${apiCalls.balance} remaining`,
  );
}
```

- `hasAccess` is the quickest check for "can this caller use this feature" —
  it's `false` when the plan doesn't include the feature or the quota has run
  out.
- `balance` is the remaining allowance. A balance of `0` or less means no
  allowance remains.
- `usage` and `overage` report consumption this billing period.

## Caveats

- **Returns `undefined`** when the monetization policy hasn't run. Guard every
  call.
- **The policy caches the data.** Subscription and entitlement data is cached
  for up to `cacheTtlSeconds` (60 seconds minimum), so `balance`, `usage`, and
  `overage` can lag real-time consumption by the length of the cache window.
  Treat them as recent, not exact.

## Next steps

- [Programmatic Monetization](./programmatic-monetization.md) — gate operations
  by plan and meter requests based on the response.
- [Dynamic Metering](./dynamic-metering.md) — set meter values at runtime from
  code.
- [Monetization Policy Reference](./monetization-policy.md) — every policy
  configuration option.
