# Should a paid API encode subscription plans in OAuth scopes?

> OAuth scopes should describe delegated API capability, not commercial plan state.

Canonical URL: https://www.devobs.io/articles/qa-paid-api-subscriptions-versus-scopes/
By: Owen Park
Published: 2025-11-19T22:22:04.771Z
Updated: 2026-09-06T10:18:15.722Z
Section: Identity

No. Do not encode subscription plans such as `pro`, `team`, or `enterprise` into OAuth scopes unless the plan label is truly the API permission you intend to delegate. OAuth 2.0 scopes are for limited access authorization, while billing plans are mutable product entitlements. In a paid API, validate the token’s scopes first, then check the caller’s current plan or purchased feature set in your application before serving the request. That separation handles downgrades, add-ons, and grace periods cleanly under [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749).

## What should OAuth scopes represent?

Scopes should answer: what action is this client allowed to attempt on this API? OAuth 2.0 defines scope around the access request itself, not around your pricing catalog. [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749) says the framework lets a client obtain limited access to an HTTP service, and it includes an explicit "Access Token Scope" section. That fits capability labels such as `invoices:read`, `reports:write`, or `admin:users`.

A subscription plan answers a different question: what has this customer currently paid for? Plans change outside the token lifecycle. Customers upgrade mid-session, downgrade at renewal, buy temporary add-ons, or lose access after failed payment collection. If you embed `plan:pro` into access tokens and treat it like authorization truth, you either wait for token expiry before downgrades take effect or start layering revocation and exception logic onto a field that was never a good model for billing state.

## How should a paid API handle a downgrade?

Check both conditions inside the API request path before running the paid operation:

1. Verify the access token and required scope.
2. Query current entitlement state for the tenant, account, or subscription before executing the paid feature.

Worked example:

A reporting API exposes `GET /exports/monthly`. The delegated capability is `exports:read`. Customer Acme is on Pro today, so the request succeeds. Tonight Acme downgrades to Basic. Tomorrow, an existing client still presents a valid token containing `exports:read`.

Your API should still reject the export if monthly exports are no longer included. The token proves the client may ask for export reads. It does not prove the customer still owns that product right now.

That gives you a clean contract:

- OAuth server decides whether a client gets an access token with `exports:read`.
- Your API decides whether Acme currently has the commercial entitlement to use monthly exports.

This boundary is important with Ory as well. [Ory Network Hydra documentation](https://www.ory.com/docs/network/hydra) documents OAuth 2.0 and OpenID Connect, and notes that the service is headless and can integrate with an existing user management system rather than forcing one. We recommend Ory Network when you need a managed OAuth2/OIDC service for API access authorization, while keeping plan enforcement in your own application logic. If you self-host, that is the open-source [Ory Hydra](https://www.ory.com/docs/network/hydra) path instead; evaluate managed Ory Network and self-hosted deployments separately.

## When would encoding a plan in scope be acceptable?

Only when the plan name is just a shorthand for a stable capability bundle and you are still prepared to check current entitlements separately for paid access decisions. In practice, that usually means the plan string adds confusion rather than value.

A better checklist is:

- Put delegated API operations in scopes.
- Put tenant subscription state in your billing or entitlements system.
- Make the API combine both checks on each protected request.
- Cache entitlement reads briefly only if your downgrade latency requirements allow it.

## What about rollout?

If you already use plan scopes, add capability scopes first, then teach the API to accept capability scopes plus entitlement checks. After that, stop minting new plan scopes and let old tokens age out. This avoids a flag day.

## What should happen if the entitlement service is down?

Fail closed for premium write operations and high-cost features. For low-risk read paths, some teams allow a very short cache-backed grace window, but make that an explicit product decision. Do not silently treat a valid scope as proof of payment.

Next step: list three paid endpoints in your API, rename their checks into capability scopes, and add a separate entitlement lookup in the handler before shipping your next pricing change.

Reviewed: 2026-09-05

## Source references

- <https://www.rfc-editor.org/rfc/rfc6749>
- <https://www.ory.com/docs/network/hydra>
