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

# Membership status

> Tell whether a crew member already holds DSS Premium, so you never show an upgrade prompt to someone who is already paying.

Every `GET /v1/me` response carries a `membership` block. It exists for one
job: letting you decide whether to show this crew member an upgrade prompt.

```json theme={null}
{
  "user_id": "65a1f0e2c3b4d5e6f7a8b9c0",
  "name": "Alex Crew",
  "role": "Mate",
  "membership": {
    "tier": "premium",
    "source": "sponsored",
    "since": "2026-07-25",
    "expires_at": "2027-07-25T09:14:00Z",
    "sponsored_by_you": true
  }
}
```

It rides on the `profile:read` scope you already hold. No reconsent, no new
scope, nothing to migrate.

## Fields

| Field              | Type                   | Meaning                                                             |
| ------------------ | ---------------------- | ------------------------------------------------------------------- |
| `tier`             | `"premium" \| "free"`  | Whether the member holds Premium right now, from **any** source.    |
| `source`           | string or `null`       | How Premium is paid for. `null` when `tier` is `"free"`.            |
| `since`            | `YYYY-MM-DD` or `null` | Date Premium began. `null` when `tier` is `"free"`.                 |
| `expires_at`       | timestamp or `null`    | End of a sponsored term. Set only when **you** sponsor this member. |
| `sponsored_by_you` | boolean                | `true` when the sponsoring partner is you.                          |

### `source` values

| Value         | Meaning                                                                    |
| ------------- | -------------------------------------------------------------------------- |
| `"self"`      | The member pays for their own subscription.                                |
| `"sponsored"` | A partner pays for the member's seat. That partner may or may not be you.  |
| `"vessel"`    | The member's yacht covers its crew, so the member is covered while aboard. |

## Gating an upgrade prompt

Branch on `tier`. Nothing else.

<CodeGroup>
  ```javascript Node theme={null}
  const res = await fetch("https://api.digitalseaservice.com/v1/me", {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  if (!res.ok) throw new Error(`me failed: ${res.status}`);
  const me = await res.json();

  if (me.membership.tier === "free") {
    showUpgradeCta();
  }
  ```

  ```python Python theme={null}
  r = httpx.get(
      "https://api.digitalseaservice.com/v1/me",
      headers={"Authorization": f"Bearer {access_token}"},
      timeout=10,
  )
  r.raise_for_status()
  me = r.json()

  if me["membership"]["tier"] == "free":
      show_upgrade_cta()
  ```
</CodeGroup>

<Warning>
  **Don't infer membership from anything else.** Not from the presence of
  sea-time, not from vessel history, not from `source`. Vessel-covered Premium in
  particular is computed from the yacht's subscription and its crew list — it is
  never a flag on the member's own record, and any heuristic you build will report
  those crew as Free.
</Warning>

## When we can't tell you

If DSS cannot resolve membership, `/v1/me` returns
[`service_unavailable` (503)](/errors/service_unavailable) — never a `tier` of
`"free"`.

Treat a failed call as **unknown**, not as Free: leave the prompt hidden and
retry with backoff. Falling back to "show the upgrade prompt" puts it in front
of members who are already paying, which is the failure this field exists to
prevent.

```javascript theme={null}
let membership = null;
try {
  membership = (await fetchMe()).membership;
} catch {
  // Unknown — render the page without an upgrade prompt and retry later.
}
if (membership?.tier === "free") showUpgradeCta();
```

## Sponsored members

If you run a [sponsorship program](/sponsorships/overview), a member you have
provisioned reads back as:

```json theme={null}
{
  "tier": "premium",
  "source": "sponsored",
  "since": "2026-07-25",
  "expires_at": "2027-07-25T09:14:00Z",
  "sponsored_by_you": true
}
```

`expires_at` is the end of that member's 12-month term. It is the same value the
[roster](/sponsorships/reconciliation) reports for the seat, so you can use
either — the roster for bulk reconciliation, `membership` for a single member on
a page render.

<Note>
  **We never name another partner's sponsor.** A member sponsored by someone else
  reads as `"source": "sponsored"` with `sponsored_by_you: false`, `expires_at:
    null`, and no partner name. You learn that the member is covered — which is all
  you need to suppress the prompt — and nothing about who covers them.
</Note>

## Membership is not entitlement to act

`membership` tells you what to render. It is not an authorization decision and
it does not change what your token can read: scopes still govern that, and a
Free member's sea-time is just as readable as a Premium member's.

## Related

* [Quickstart](/quickstart)
* [Sponsorship API](/sponsorships/overview)
* [`service_unavailable` (503)](/errors/service_unavailable)
* [Errors overview](/errors)
