---
title: Commercial Plans & Application Feature Entitlements
category: Plans & Monetization
order: 9
badge: Plans & Quotas
description: Architectural specification for user subscription plans, feature flags, application limits, and client SDK integration across the Ternis ecosystem.
---

# Commercial Plans & Feature Entitlements

The **Ternis Auth & SSO Platform** provides a centralized plan and entitlement management system. Rather than having each distributed application (e.g. `ternisdomains.de`, `thosted.de`, analytics portals) build separate subscription tables, applications consume the user's active plan, feature toggles, and numerical quotas directly via standard OAuth tokens and REST API endpoints.

---

## 1. Platform Plan Matrix

Ternis Auth ships with 4 standard subscription tiers:

| Plan | Slug | Price | Domains Limit | API Rate Limit | Dedicated IP | 24/7 SLA | SSO SAML |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| **Community Free** | `free` | Free | 2 | 1,000 / mo | No | No | No |
| **Developer Starter** | `starter` | €9.00 / mo | 10 | 10,000 / mo | No | No | No |
| **Developer Pro** | `pro` | €29.00 / mo | 50 | 50,000 / mo | Yes | Yes | No |
| **Enterprise Cloud** | `enterprise` | €99.00 / mo | 500 | 500,000 / mo | Yes | Yes | Yes |

---

## 2. How Applications Read User Plans

Ternis ecosystem applications can inspect plan entitlements via two primary mechanisms:

### Mechanism A: Standard OpenID Connect Claims
When requesting the standard OAuth scopes `openid`, `profile`, or `ternis:customer`, the `/oauth/userinfo` response automatically includes the active `plan` object and list of authorized `entitlements`:

```json
{
  "sub": "b2f6b897-4001-460d-8386-db93f1d8c1c4",
  "name": "Elena Rostova",
  "email": "member@ternis.org",
  "plan": {
    "id": "9dc721a0-128f-4318-912b-31ca78198f12",
    "name": "Developer Pro",
    "slug": "pro",
    "tier": "pro",
    "price_cents": 2900,
    "currency": "EUR",
    "features": {
      "domains_limit": 50,
      "api_rate_limit": 50000,
      "custom_avatar_cdn": true,
      "priority_support": true,
      "dns_management": true,
      "ssl_wildcards": true,
      "dedicated_ip": true,
      "balance_auto_topup": true,
      "sso_saml": false
    }
  },
  "entitlements": [
    "domains_limit",
    "api_rate_limit",
    "custom_avatar_cdn",
    "priority_support",
    "dns_management",
    "ssl_wildcards",
    "dedicated_ip",
    "balance_auto_topup"
  ]
}
```

### Mechanism B: Dedicated Plan & Entitlement Endpoint
Ternis backend services can query the dedicated plan endpoint with an authorized Bearer token:

```http
GET /api/v1/user/plan HTTP/1.1
Host: auth.ternis.net
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGci...
```

#### Response (200 OK):
```json
{
  "user_id": "b2f6b897-4001-460d-8386-db93f1d8c1c4",
  "plan": {
    "id": "9dc721a0-128f-4318-912b-31ca78198f12",
    "name": "Developer Pro",
    "slug": "pro",
    "price_cents": 2900,
    "formatted_price": "€29.00 / mo",
    "currency": "EUR",
    "billing_interval": "month"
  },
  "features": {
    "domains_limit": 50,
    "api_rate_limit": 50000,
    "custom_avatar_cdn": true,
    "priority_support": true,
    "dns_management": true,
    "ssl_wildcards": true,
    "dedicated_ip": true,
    "balance_auto_topup": true,
    "sso_saml": false
  },
  "entitlements": [
    "domains_limit",
    "api_rate_limit",
    "custom_avatar_cdn",
    "priority_support",
    "dns_management",
    "ssl_wildcards",
    "dedicated_ip",
    "balance_auto_topup"
  ],
  "status": "active"
}
```

---

## 3. Implementation Example in `ternisdomains.de`

Here is an example of how `ternisdomains.de` verifies a user's domain registration quota before creating a new DNS zone:

```php
namespace App\Services;

use Illuminate\Support\Facades\Http;

class DomainEntitlementService
{
    public function canRegisterDomain(string $accessToken, int $currentDomainCount): bool
    {
        $response = Http::withToken($accessToken)
            ->get('https://auth.ternis.net/api/v1/user/plan');

        if (! $response->successful()) {
            return false;
        }

        $features = $response->json('features', []);
        $maxDomains = $features['domains_limit'] ?? 2;

        return $currentDomainCount < $maxDomains;
    }
}
```
