> ## Documentation Index
> Fetch the complete documentation index at: https://api.fanvue.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Read a creator's app subscription

> Check whether a creator subscribes to your app with GET /apps/{appUuid}/subscription/me, read plan lifecycle with subscription-status, and handle each status.

Two read-only endpoints report App Store billing state. `subscription-status` gives the lifecycle of your app's pricing plans for your own screens, and `subscription/me` tells you whether the signed-in user is entitled to your app, which is what you gate paid features on server-side. Both require the `read:self` scope.

## Available endpoints

| Endpoint | Returns | Call it |
| - | - | - |
| `GET /apps/{appUuid}/subscription-status` | Each pricing plan's status and `checkoutUrl`, plus an app-level `overallStatus` | As the app owner, to show plan lifecycle in a developer-facing surface |
| `GET /apps/{appUuid}/subscription/me` | Whether the authenticated user, or the creators an agency team member manages, subscribes to the app | From your server, to decide whether the signed-in user has an active, pending, cancelled or absent subscription |

## Get app pricing lifecycle

`GET /apps/{appUuid}/subscription-status` returns a lifecycle summary for an app you own and includes each pricing plan's status.

### Response fields

| Field | Value |
| - | - |
| `availability` | `complete` when every plan's lifecycle was read |
| `overallStatus` | `notConfigured`, `pendingSetup`, `active`, `withdrawn`, `mixed` or `unavailable` |
| `pricingPlans[].uuid`, `pricingPlans[].name` | Plan identity |
| `pricingPlans[].billingType` | `free`, `one_time` or `recurring` |
| `pricingPlans[].interval` | `monthly`, `yearly` or `null` |
| `pricingPlans[].price`, `pricingPlans[].currencyCode` | Price in USD minor units (cents) and its ISO 4217 code |
| `pricingPlans[].status` | `pending_setup`, `active` or `withdrawn` |
| `pricingPlans[].checkoutUrl` | Fanvue-hosted checkout URL, or `null` |

`checkoutUrl` is the Fanvue-hosted checkout page for the plan or one-time item. It is `null` for free, pending setup and withdrawn plans, and for a plan whose checkout link you disabled. Send buyers to this URL rather than building one by hand.

### Example request

```bash theme={null}
curl "https://api.fanvue.com/apps/00000000-0000-4000-8000-000000000001/subscription-status" \
  -H "Authorization: Bearer <token>" \
  -H "X-Fanvue-API-Version: 2025-06-26"
```

### Example response

```json theme={null}
{
  "appUuid": "00000000-0000-4000-8000-000000000001",
  "appName": "Example App",
  "availability": "complete",
  "overallStatus": "active",
  "pricingPlans": [
    {
      "uuid": "00000000-0000-4000-8000-000000000002",
      "name": "Pro Monthly",
      "billingType": "recurring",
      "interval": "monthly",
      "price": 999,
      "currencyCode": "USD",
      "status": "active",
      "checkoutUrl": "https://www.fanvue.com/checkout/app_000000001VgEh72lXvTXkI"
    }
  ]
}
```

### Error behaviour

* `403`: the authenticated user does not have access to this app.
* `404`: the app was not found or is not owned by the authenticated user.
* `503`: the environment is not configured to serve developer app subscription data.

## Get current user subscription for an app

`GET /apps/{appUuid}/subscription/me` returns the authenticated user's entitlement for the app, plus a `managedCreators` array with one record per creator the user is assigned to manage. The array is populated for agency team members and empty for everyone else.

<Note>
  The access token must come from the app identified by `appUuid`. If any other OAuth client issued the token, the call returns `403`.
</Note>

### Response fields

Top-level fields describe the authenticated user's own subscription state.

| Field | Value |
| - | - |
| `userUuid` | The user's UUID. This can be a creator or an agency team member |
| `hasActiveSubscription` | `true` when the user holds an active subscription |
| `status` | `active`, `pending`, `cancelled` or `none` |
| `planUuid`, `planName` | The matched pricing plan, or `null` |
| `currentPeriodEnd` | End of the current billing period, or `null` |
| `cancelAtPeriodEnd` | `true` when the subscription ends after the current period |
| `managedCreators` | Per-creator records for creators this user is assigned to manage. Always present; `[]` for non-agency users |

Each `managedCreators` entry mirrors the top-level shape for one creator: `userUuid` (the creator's UUID), `hasActiveSubscription`, `status`, `planUuid`, `planName`, `currentPeriodEnd`, `cancelAtPeriodEnd`.

### Status values

| `status` | `hasActiveSubscription` | Meaning |
| - | - | - |
| `active` | `true` | The user is entitled to `planUuid` until `currentPeriodEnd` |
| `pending` with `currentPeriodEnd: null` | `false` | A checkout awaiting payment. `planUuid` and `planName` are `null` until the payment settles |
| `pending` with `currentPeriodEnd` set | `false` | A paid subscription with a cancellation in flight. Plan fields are kept |
| `cancelled` | `false` | A past subscription that has ended |
| `none` | `false` | No subscription record for this user and app |

### Example request

```bash theme={null}
curl "https://api.fanvue.com/apps/00000000-0000-4000-8000-000000000001/subscription/me" \
  -H "Authorization: Bearer <token>" \
  -H "X-Fanvue-API-Version: 2025-06-26"
```

### Example response, creator with an active subscription

```json theme={null}
{
  "appUuid": "00000000-0000-4000-8000-000000000001",
  "userUuid": "00000000-0000-4000-8000-000000000003",
  "hasActiveSubscription": true,
  "status": "active",
  "planUuid": "00000000-0000-4000-8000-000000000002",
  "planName": "Pro Monthly",
  "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "managedCreators": []
}
```

### Example response, agency team member with two managed creators

```json theme={null}
{
  "appUuid": "00000000-0000-4000-8000-000000000001",
  "userUuid": "00000000-0000-4000-8000-000000000010",
  "hasActiveSubscription": false,
  "status": "none",
  "planUuid": null,
  "planName": null,
  "currentPeriodEnd": null,
  "cancelAtPeriodEnd": false,
  "managedCreators": [
    {
      "userUuid": "00000000-0000-4000-8000-000000000011",
      "hasActiveSubscription": true,
      "status": "active",
      "planUuid": "00000000-0000-4000-8000-000000000002",
      "planName": "Pro Monthly",
      "currentPeriodEnd": "2026-11-01T00:00:00.000Z",
      "cancelAtPeriodEnd": false
    },
    {
      "userUuid": "00000000-0000-4000-8000-000000000012",
      "hasActiveSubscription": false,
      "status": "none",
      "planUuid": null,
      "planName": null,
      "currentPeriodEnd": null,
      "cancelAtPeriodEnd": false
    }
  ]
}
```

### Agency team members

Agency team members belong to one or more agencies and manage a set of creators. Acting as themselves they install nothing; an install or purchase made while switched into a managed creator's profile belongs to that creator's account. Two things follow from that.

1. **Top-level fields describe the team member's own subscription**, which is always empty: `status: "none"`, `hasActiveSubscription: false`, `planUuid: null`. Render entitlement from `managedCreators` when you detect an agency context.
2. **Agency users receive `200`, not `404`.** A non-agency user whose app lookup fails receives `404`; an agency team member receives `200` with the empty top-level shape and a populated `managedCreators` array.

A team member sees only the creators they are assigned to manage. Chatters see their assigned subset, and admins see the agency's creators. The `read:self` scope covers the managed-creator records, so you need no additional scope.

### Iterating over `managedCreators`

For agency-aware surfaces, branch on `managedCreators` rather than the top-level subscription:

```typescript theme={null}
type SubscriptionRecord = {
  userUuid: string;
  hasActiveSubscription: boolean;
  status: "active" | "pending" | "cancelled" | "none";
  planUuid: string | null;
  planName: string | null;
  currentPeriodEnd: string | null;
  cancelAtPeriodEnd: boolean;
};

type AppSubscriptionMeResponse = SubscriptionRecord & {
  appUuid: string;
  managedCreators: SubscriptionRecord[];
};

async function renderEntitlements(appUuid: string, accessToken: string) {
  const res = await fetch(
    `https://api.fanvue.com/apps/${appUuid}/subscription/me`,
    {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "X-Fanvue-API-Version": "2025-06-26",
      },
    },
  );

  if (res.status === 404) {
    return { self: null, managed: [] };
  }
  if (!res.ok) throw new Error(`subscription/me failed: ${res.status}`);

  const body = (await res.json()) as AppSubscriptionMeResponse;

  const self: SubscriptionRecord | null = body.hasActiveSubscription
    ? {
        userUuid: body.userUuid,
        hasActiveSubscription: body.hasActiveSubscription,
        status: body.status,
        planUuid: body.planUuid,
        planName: body.planName,
        currentPeriodEnd: body.currentPeriodEnd,
        cancelAtPeriodEnd: body.cancelAtPeriodEnd,
      }
    : null;

  const managed = body.managedCreators.map((c) => ({
    creatorUuid: c.userUuid,
    isPaid: c.hasActiveSubscription,
    plan: c.planName,
    renewsOrEndsAt: c.currentPeriodEnd,
  }));

  return { self, managed };
}
```

<Warning>
  `managedCreators` holds at most 100 records. An agency with more than 100 assigned creators receives a truncated list, so do not treat the array as the full roster.

  A managed-creator lookup that fails upstream is dropped from the array rather than failing the call. Treat a missing creator as "no information", not as "no subscription", and retry rather than revoking access.
</Warning>

### Error behaviour

* `400`: `appUuid` is not a valid UUID.
* `401`: bearer token missing or invalid.
* `403`: the token lacks `read:self`, or `appUuid` does not belong to the OAuth client that issued the token.
* `404`: `App not found for this user`. The app is not visible to this non-agency user. A user with no subscription record receives `200` with `status: "none"` instead.
* `410`: API version sunset.
* `502`: the upstream billing service returned an error.
* `503`: the environment is not configured to serve developer app subscription data.

## Typical usage pattern

1. Create your app in the Developer Area and configure it with an [App Manifest](/docs/app-store/app-manifest).
2. Authenticate a user with OAuth and request `read:self`.
3. Use `subscription-status` in developer-facing surfaces to show plan lifecycle and to read each plan's `checkoutUrl`.
4. Use `subscription/me` in server-side logic to gate paid features. For agency-aware surfaces, also iterate `managedCreators`.

Testing guidance is in [Test your app](/docs/get-started/test-your-app); pricing constraints are in [App Store listing requirements](/docs/app-store/listing-requirements).

## Getting notified instead of polling

Both endpoints read current state. To hear about purchases, subscription activations, refunds and disputes as they happen, subscribe to `app.payment.*`, `app.subscription.*`, `app.refund.created` and `app.dispute.*`. [App events](/docs/webhooks/app/overview) has the payloads.

## See also

* [App events: subscriptions](/docs/webhooks/app/subscriptions)
* [API errors](/docs/api-reference/errors)


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