# Subscription Plans

The Subscription Plans API manages tenant-defined plans that control an account's listing allowance, expiry period, and order fees or commissions. It is separate from the listing-based **Memberships** API (`/v1/memberships`): use Memberships for a business's own customer/member programs.

All examples use Bearer authentication. Tenant and user identity are resolved from the authenticated request by the gateway; do not send separate identity headers.

```bash
export API="https://api.tradly.app"
export TOKEN="<bearer-token>"
```

## Manage subscription plans

### List plans

`GET /v1/subscription_products`

Returns plans visible to the authenticated tenant. Optional query parameters include `currency_id`, `account_id`, and `include_stats=true`. Non-admin callers receive active plans only; currency context is required by the API for non-admin listing.

```bash
curl "$API/v1/subscription_products?currency_id=13&include_stats=true" \
  -H "Authorization: Bearer $TOKEN"
```

### Get a plan

`GET /v1/subscription_products/{id}`

Returns one plan. Non-admin callers can retrieve active plans only and must provide currency context as required by the API.

### Create a plan

`POST /v1/subscription_products`

The JSON body is wrapped in `subscription_product`. Required fields are `title`, `allowed_listings` (positive integer), `currency_id`, `expiry_days` (positive integer), and `type`.

Supported create `type` values: `free_subscription`, `stripe`, `android`, `ios`, `mangopay`.

```bash
curl -X POST "$API/v1/subscription_products" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data-raw '{
    "subscription_product": {
      "title": "Starter",
      "description": "Starter plan",
      "amount": 49.00,
      "currency_id": 13,
      "expiry_days": 30,
      "allowed_listings": 25,
      "type": "stripe",
      "sku": "starter-monthly",
      "active": true,
      "fee_fixed_per_order": 0,
      "commission_percent_per_order": 0,
      "metadata": {}
    }
  }'
```

The response contains the new `subscription_product.id`.

### Update a plan

`PUT /v1/subscription_products/{id}`

Uses the same `subscription_product` wrapper and requires `title`, `allowed_listings`, `currency_id`, `expiry_days`, and `type`. Update accepts `stripe`, `android`, `ios`, or `mangopay`; `free_subscription` is not accepted by this handler's update validator.

### Archive a plan

`DELETE /v1/subscription_products/{id}`

Soft-archives the plan and marks it inactive; it does not physically delete its subscription history.

## Plan analytics

All analytics routes are tenant-scoped by the authenticated request.

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/v1/subscription_products/stats` | Aggregate plan statistics for the tenant. |
| `GET` | `/v1/subscription_products/{id}/stats` | Statistics for one plan. |
| `GET` | `/v1/subscription_products/{id}/subscribers` | Paginated subscribers; optional `page`, `per_page` (max 100), and `status` (`active`, `trial`, `expired`, `cancelled`). |
| `GET` | `/v1/subscription_products/{id}/invoices` | Paginated billing-history view with the same filters. These are derived from subscription records; there is no separate invoice table behind this endpoint. |
| `GET` | `/v1/subscription_products/{id}/revenue` | Monthly revenue and subscriber-count series. |

```bash
curl "$API/v1/subscription_products/42/subscribers?page=1&per_page=20&status=active" \
  -H "Authorization: Bearer $TOKEN"

curl "$API/v1/subscription_products/42/invoices?page=1&per_page=20" \
  -H "Authorization: Bearer $TOKEN"

curl "$API/v1/subscription_products/42/revenue" \
  -H "Authorization: Bearer $TOKEN"
```

The aggregate stats include active, cancelled, expired, and trial counts, MRR/ARR, churn rate, new and churned subscribers for the current month, average cancelled-subscriber lifetime, and total recorded subscription revenue. Values are computed from subscription records and should be treated as operational analytics, not a replacement for accounting invoices or the general ledger.

## Customer subscription lifecycle

The `/v1/subscriptions` router also contains customer and platform subscription operations:

| Method | Endpoint | Purpose |
|---|---|---|
| `GET` | `/v1/subscriptions/subscribe/products?account_id={account_id}` | List available plans for an account; optionally filter by `type`. |
| `POST` | `/v1/subscriptions/subscribe` | Confirm a mobile-store purchase using `account_id`, `subscription_product_id`, and receipt `token`. The current handler verifies iOS/Android receipts. |
| `POST` | `/v1/subscriptions/subscribe/confirm` | Confirm a mobile-store purchase using the same required fields. |
| `POST` | `/v1/subscriptions/email_trigger` | Send the subscription prompt email for an eligible account (`account_id`). |
| `DELETE` | `/v1/subscriptions/unsubscribe` | Cancel the account's subscription (`account_id`). |
| `GET` | `/v1/subscriptions/subscribe/manage?account_id={account_unique_id}&tenant_id={tenant_unique_id}` | Open the subscription-management page/customer portal where supported (currently Stripe). |

The create/confirm purchase handlers currently support iOS and Android receipt verification. A plan configured as `stripe` is not confirmed by those receipt endpoints; use the configured Stripe checkout/portal flow. The web handoff endpoints use a separate legacy contract and are intentionally not shown as Bearer-only client examples here.

## Related APIs

- [Memberships](memberships) — listing-based memberships, customer contributions, and price logs.
- [Listings](listings) — listing plans/products and checkout.
