> For the complete documentation index, see [llms.txt](https://docs.peakcommerce.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.peakcommerce.com/developers/reference-pages/subscriptions.md).

# Subscriptions

A subscription is an ongoing commitment from an account to a plan.

* [`GET /api/v1/subscriptions`](#get-apiv1subscriptions) — list
* [`POST /api/v1/subscriptions`](#post-apiv1subscriptions) — create
* [`GET /api/v1/subscriptions/{id}`](#get-apiv1subscriptionsid) — retrieve
* [`POST /api/v1/subscriptions/{id}/change-plan`](#post-apiv1subscriptionsidchange-plan)
* [`POST /api/v1/subscriptions/{id}/change-quantity`](#post-apiv1subscriptionsidchange-quantity)
* [`POST /api/v1/subscriptions/{id}/cancel`](#post-apiv1subscriptionsidcancel)
* [`POST /api/v1/subscriptions/{id}/renew`](#post-apiv1subscriptionsidrenew)

***

## `GET /api/v1/subscriptions`

List all subscriptions in this tenant.

* **Auth scope:** `read`

### Response `200`

```json
{
  "data": [
    {
      "id": "sub_01H...",
      "tenantId": "ten_01H...",
      "productId": "prod_01H...",
      "contactId": null,
      "organizationId": "org_01H...",
      "priceCents": 4900,
      "currency": "USD",
      "billingInterval": "month",
      "status": "active",
      "currentPeriodStart": "2026-04-01T00:00:00Z",
      "currentPeriodEnd": "2026-05-01T00:00:00Z",
      "createdAt": "2026-04-01T00:00:00Z"
    }
  ],
  "nextActions": [
    { "rel": "createSubscription", "method": "POST", "href": "/api/v1/subscriptions" }
  ]
}
```

### cURL

```bash
curl -H "Authorization: Bearer $PEAK_API_KEY" \
  https://api.example.com/api/v1/subscriptions
```

***

## `POST /api/v1/subscriptions`

Create a subscription.

* **Auth scope:** `commerce`

### Body

| Field             | Type          | Required | Description                       |
| ----------------- | ------------- | -------- | --------------------------------- |
| `productId`       | UUID          | yes      | Product to subscribe to           |
| `contactId`       | UUID          | no       | Subscriber contact                |
| `organizationId`  | UUID          | no       | Account                           |
| `priceCents`      | integer       | no       | Override price in cents           |
| `currency`        | string (≤10)  | no       | Defaults to `USD`                 |
| `billingInterval` | string (≤50)  | no       | e.g. `month`, `year`              |
| `externalId`      | string (≤255) | no       | External billing-system reference |

### Response `201`

```json
{
  "data": {
    "id": "sub_01H...",
    "productId": "prod_01H...",
    "status": "active",
    "priceCents": 4900,
    "currency": "USD",
    "billingInterval": "month",
    "createdAt": "2026-05-02T14:01:23Z"
  },
  "nextActions": [
    { "rel": "self", "method": "GET", "href": "/api/v1/subscriptions/sub_01H..." },
    { "rel": "changePlan", "method": "POST", "href": "/api/v1/subscriptions/sub_01H.../change-plan" },
    { "rel": "cancel", "method": "POST", "href": "/api/v1/subscriptions/sub_01H.../cancel" },
    { "rel": "renew", "method": "POST", "href": "/api/v1/subscriptions/sub_01H.../renew" }
  ]
}
```

### cURL

```bash
curl -X POST https://api.example.com/api/v1/subscriptions \
  -H "Authorization: Bearer $PEAK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "5f1c2d34-3b1f-4f9a-8c7c-2c3a4d5e6f70",
    "organizationId": "org_uuid_here",
    "currency": "USD",
    "billingInterval": "month"
  }'
```

***

## `GET /api/v1/subscriptions/{id}`

Retrieve a subscription.

* **Auth scope:** `read`

### Response `200`

Same shape as the `data` field returned by `POST`.

### Errors

| Status | When                                  |
| ------ | ------------------------------------- |
| `404`  | Subscription not found in this tenant |

### cURL

```bash
curl -H "Authorization: Bearer $PEAK_API_KEY" \
  https://api.example.com/api/v1/subscriptions/sub_01H...
```

***

## `POST /api/v1/subscriptions/{id}/change-plan`

Change a subscription's plan. The platform evaluates `plan_change` policies first; a blocking policy returns `403`.

* **Auth scope:** `commerce`

### Body

| Field             | Type         | Description  |
| ----------------- | ------------ | ------------ |
| `productId`       | UUID         | New product  |
| `priceCents`      | integer      | New price    |
| `billingInterval` | string (≤50) | New interval |

All fields are optional, but you should provide at least one.

### Response `200`

Same shape as `GET`. Audit-log entry: `api.subscription.changePlan`.

### Errors

| Status | When                                         |
| ------ | -------------------------------------------- |
| `403`  | A `plan_change` policy blocked the operation |
| `404`  | Subscription not found                       |

### cURL

```bash
curl -X POST https://api.example.com/api/v1/subscriptions/sub_01H.../change-plan \
  -H "Authorization: Bearer $PEAK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "new_prod_uuid",
    "billingInterval": "year"
  }'
```

***

## `POST /api/v1/subscriptions/{id}/change-quantity`

Change a charge's quantity (or `includedUnits`) on a subscription. Routes through the plan-dependency engine so dependent charges adjust automatically.

* **Auth scope:** `commerce`

### Body

| Field             | Type                          | Required | Description                                    |
| ----------------- | ----------------------------- | -------- | ---------------------------------------------- |
| `accountId`       | string                        | yes      | Billing-system account ID                      |
| `chargeId`        | string                        | yes      | Billing-system charge ID                       |
| `newQuantity`     | integer ≥ 0                   | yes      | Target quantity                                |
| `editedField`     | `quantity` \| `includedUnits` | no       | Defaults to `quantity`                         |
| `catalogChargeId` | string                        | no       | Local catalog charge ID for resolver hints     |
| `integrationId`   | string                        | no       | Pin a specific integration; defaults to active |

### Response `200`

```json
{
  "data": {
    "applied": [
      { "chargeId": "charge_a", "field": "quantity", "previous": 5, "next": 7 },
      { "chargeId": "charge_b", "field": "quantity", "previous": 5, "next": 7, "viaLink": "Base seats ↔ add-on seats" }
    ],
    "warnings": []
  },
  "nextActions": [ /* subscription nextActions */ ]
}
```

### Errors

| Status | When                                                                  |
| ------ | --------------------------------------------------------------------- |
| `400`  | Validation failed; no billing integration; adapter lacks self-service |
| `403`  | Subscription does not belong to this account                          |
| `404`  | Subscription not found                                                |

### cURL

```bash
curl -X POST https://api.example.com/api/v1/subscriptions/sub_01H.../change-quantity \
  -H "Authorization: Bearer $PEAK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "A00000123",
    "chargeId": "C-1234",
    "newQuantity": 10
  }'
```

***

## `POST /api/v1/subscriptions/{id}/cancel`

Cancel a subscription. Evaluates `subscription_cancel` policies first.

* **Auth scope:** `commerce`

### Body

None required. Cancellation timing follows the matched cancellation business rule (`immediate` or `at_period_end`).

### Response `200`

Same shape as `GET`, with `status: "cancelled"`.

### Errors

| Status | When                                        |
| ------ | ------------------------------------------- |
| `403`  | A cancellation policy blocked the operation |
| `404`  | Subscription not found                      |

### cURL

```bash
curl -X POST https://api.example.com/api/v1/subscriptions/sub_01H.../cancel \
  -H "Authorization: Bearer $PEAK_API_KEY"
```

***

## `POST /api/v1/subscriptions/{id}/renew`

Renew a subscription for one period. For paid subscriptions, a `processing` payment session is recorded; the provider's webhook finalizes it.

* **Auth scope:** `commerce`

### Response `200`

```json
{
  "data": {
    "id": "sub_01H...",
    "status": "active",
    "currentPeriodStart": "2026-05-02T14:01:23Z",
    "currentPeriodEnd": "2026-06-02T14:01:23Z"
  },
  "paymentSessionId": "pmt_01H...",
  "nextActions": [ /* subscription nextActions */ ]
}
```

`paymentSessionId` is `null` for free renewals.

### Errors

| Status | When                   |
| ------ | ---------------------- |
| `404`  | Subscription not found |

### cURL

```bash
curl -X POST https://api.example.com/api/v1/subscriptions/sub_01H.../renew \
  -H "Authorization: Bearer $PEAK_API_KEY"
```

***

## Related

* [Pricing previews](/developers/reference-pages/pricing-previews.md) — preview a plan change before committing
* [Orders](/developers/reference-pages/orders.md)
* [Payments](/developers/reference-pages/payments.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.peakcommerce.com/developers/reference-pages/subscriptions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
