# Subscription plans and subscriptions API

> Create subscription plans, list subscribers, and cancel, pause or resume subscriptions with the Anytime Capital Payments API, with trials and webhooks.

Source: https://anytime-capital.com/docs/subscriptions · Markdown: https://anytime-capital.com/docs/subscriptions.md · All docs: https://anytime-capital.com/docs/llms-full.txt

A **plan** is a recurring price customers subscribe to at its `url`, billed every `week`, `month`, `quarter` or `year`, with an optional free trial. A **subscription** is one customer's subscription to a plan. Customers subscribe themselves at checkout; the API creates plans, reads subscribers and cancels, pauses or resumes them, following the dashboard's rules and sending the same emails. Both need the `subscriptions` permission.

| Topic | Detail |
| --- | --- |
| Endpoints | [`POST /api/v1/subscription-plans`](#create-subscription-plan), [`GET /api/v1/subscription-plans`](#list-subscription-plans), [`GET /api/v1/subscription-plans/{id}`](#get-subscription-plan), [`GET /api/v1/subscriptions`](#list-subscriptions), [`GET /api/v1/subscriptions/{id}`](#get-subscription), [`POST /api/v1/subscriptions/{id}/cancel`](#cancel-subscription), [`POST /api/v1/subscriptions/{id}/pause`](#pause-subscription), [`POST /api/v1/subscriptions/{id}/resume`](#resume-subscription) |
| Key permissions | `subscriptions:write`, `subscriptions:read` |
| Samples | curl; Node.js 18+ with the built-in `fetch` (in an ES module); the [Node SDK](https://anytime-capital.com/docs/node-sdk.md) (preview: not on npm yet); Python with `requests`. |
| Every request can also answer | 401 `UNAUTHORIZED`, 401 `API_KEY_EXPIRED`, 403 `NOT_ENROLLED`, 403 `PERMISSION_DENIED`, 403 `IP_NOT_ALLOWED`, 429 `RATE_LIMITED`, 500 `INTERNAL_ERROR`. See [Error codes](https://anytime-capital.com/docs/error-codes.md). |

> **Done in the dashboard:** Changing a subscriber's plan (with proration), discount codes on subscriptions, usage meters on a plan and archiving a plan are set up on the **Subscriptions** page. See [Subscription discounts and plan changes](https://anytime-capital.com/docs/subscription-discounts.md) and [Usage billing](https://anytime-capital.com/docs/usage-billing.md). A plan's price and interval are fixed once it exists.

## The subscription plan object

```json
{
  "object": "subscription_plan",
  "livemode": true,
  "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
  "title": "Studio membership",
  "description": "Unlimited sessions each month",
  "amountCents": 4900,
  "currency": "usd",
  "interval": "month",
  "trialDays": 14,
  "url": "https://anytime-capital.com/pay/business/pl_Qx8Rt2Vb7Nc4Zp9WkLm3",
  "active": true,
  "createdAt": "2026-09-15T12:00:00.000Z",
  "archivedAt": null
}
```

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | The type of object. Always `subscription_plan`. |
| `livemode` | boolean | False for test-mode data (an `ak_test_` key); true for live data. |
| `id` | string | `sp_` followed by 20 letters and digits. |
| `title` | string | Name. |
| `description` | string or null | Description. |
| `amountCents` | integer | Price per interval. Integer cents. |
| `currency` | string | usd. |
| `interval` | string | One of `week`, `month`, `quarter`, `year`. |
| `trialDays` | integer | Free trial length, in days. |
| `url` | string (uri) or null | Where customers subscribe; null once archived. |
| `active` | boolean | Taking new subscribers. |
| `createdAt` | string (date-time) | Created. |
| `archivedAt` | string (date-time) or null | Archived. |

## Plan endpoints

### Create a subscription plan

`POST /api/v1/subscription-plans`

Permission: `subscriptions:write`. Supports `Idempotency-Key`.

A recurring price customers subscribe to at its url. Price and interval are fixed once made. Share the plan's `url`: customers subscribe there.

**Headers**

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | no | Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | string | yes | Name customers recognise. Up to 120 characters. |
| `description` | string or null | no | Description. Up to 500 characters. |
| `amountCents` | integer | yes | Price per interval, in cents. At least 1 ($0.01). |
| `interval` | string | yes | How often it bills. One of `week`, `month`, `quarter`, `year`. |
| `trialDays` | integer | no | Free trial days. Default 0. From 0 to 90. |

**Request (curl)**

```bash
curl -X POST https://anytime-capital.com/api/v1/subscription-plans \
  -H "Authorization: Bearer $ANYTIME_API_KEY" \
  -H "Idempotency-Key: plan-studio-monthly" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Studio membership",
    "description": "Unlimited sessions each month",
    "amountCents": 4900,
    "interval": "month",
    "trialDays": 14
  }'
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscription-plans", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "plan-studio-monthly",
  },
  body: JSON.stringify({
    title: "Studio membership",
    description: "Unlimited sessions each month",
    amountCents: 4900,
    interval: "month",
    trialDays: 14,
  }),
});
const plan = await res.json();
if (!res.ok) throw new Error(`${plan.error.code}: ${plan.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const plan = await client.subscriptionPlans.create(
  {
    title: "Studio membership",
    description: "Unlimited sessions each month",
    amountCents: 4900,
    interval: "month",
    trialDays: 14,
  },
  { idempotencyKey: "plan-studio-monthly" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.post(
    "https://anytime-capital.com/api/v1/subscription-plans",
    headers={
        "Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
        "Idempotency-Key": "plan-studio-monthly",
    },
    json={
        "title": "Studio membership",
        "description": "Unlimited sessions each month",
        "amountCents": 4900,
        "interval": "month",
        "trialDays": 14,
    },
    timeout=30,
)
plan = res.json()
if not res.ok:
    raise RuntimeError(f"{plan['error']['code']}: {plan['error']['message']}")
```

**Response** (201)

```json
{
  "object": "subscription_plan",
  "livemode": true,
  "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
  "title": "Studio membership",
  "description": "Unlimited sessions each month",
  "amountCents": 4900,
  "currency": "usd",
  "interval": "month",
  "trialDays": 14,
  "url": "https://anytime-capital.com/pay/business/pl_Qx8Rt2Vb7Nc4Zp9WkLm3",
  "active": true,
  "createdAt": "2026-09-15T12:00:00.000Z",
  "archivedAt": null
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_JSON` | The body isn't valid JSON, or couldn't be read. An empty body reads as `{}`. Send `Content-Type: application/json` and a JSON object. |
| 400 | `INVALID_INPUT` | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` Fix the named field. Don't retry the same request unchanged. |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. |

### List subscription plans

`GET /api/v1/subscription-plans`

Permission: `subscriptions:read`.

Plans, newest first.

**Query**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `active` | boolean | no | true for live objects, false for deactivated or archived ones. |
| `limit` | integer | no | How many objects per page, 1–100. Default 25. See [Pagination](https://anytime-capital.com/docs/pagination.md). |
| `cursor` | string | no | The previous page's `nextCursor`, for the page after it. |

**Request (curl)**

```bash
curl "https://anytime-capital.com/api/v1/subscription-plans?active=true" \
  -H "Authorization: Bearer $ANYTIME_API_KEY"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscription-plans?active=true", {
  headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const page = await res.json();
if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const page = await client.subscriptionPlans.list({ active: true });
```

**Request (Python)**

```python
import os

import requests

res = requests.get(
    "https://anytime-capital.com/api/v1/subscription-plans",
    headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
    params={"active": "true"},
    timeout=30,
)
page = res.json()
if not res.ok:
    raise RuntimeError(f"{page['error']['code']}: {page['error']['message']}")
```

**Response** (200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "subscription_plan",
      "livemode": true,
      "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
      "title": "Studio membership",
      "description": "Unlimited sessions each month",
      "amountCents": 4900,
      "currency": "usd",
      "interval": "month",
      "trialDays": 14,
      "url": "https://anytime-capital.com/pay/business/pl_Qx8Rt2Vb7Nc4Zp9WkLm3",
      "active": true,
      "createdAt": "2026-09-15T12:00:00.000Z",
      "archivedAt": null
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_INPUT` | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` Fix the named field. Don't retry the same request unchanged. |

### Get a subscription plan

`GET /api/v1/subscription-plans/{id}`

Permission: `subscriptions:read`.

One plan.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `sp_…` |

**Request (curl)**

```bash
curl https://anytime-capital.com/api/v1/subscription-plans/sp_Vb7Nc4Zp9WkLm3Qx8Rt2 \
  -H "Authorization: Bearer $ANYTIME_API_KEY"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscription-plans/sp_Vb7Nc4Zp9WkLm3Qx8Rt2", {
  headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const plan = await res.json();
if (!res.ok) throw new Error(`${plan.error.code}: ${plan.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const plan = await client.subscriptionPlans.retrieve("sp_Vb7Nc4Zp9WkLm3Qx8Rt2");
```

**Request (Python)**

```python
import os

import requests

res = requests.get(
    "https://anytime-capital.com/api/v1/subscription-plans/sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
    headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
    timeout=30,
)
plan = res.json()
if not res.ok:
    raise RuntimeError(f"{plan['error']['code']}: {plan['error']['message']}")
```

**Response** (200)

```json
{
  "object": "subscription_plan",
  "livemode": true,
  "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
  "title": "Studio membership",
  "description": "Unlimited sessions each month",
  "amountCents": 4900,
  "currency": "usd",
  "interval": "month",
  "trialDays": 14,
  "url": "https://anytime-capital.com/pay/business/pl_Qx8Rt2Vb7Nc4Zp9WkLm3",
  "active": true,
  "createdAt": "2026-09-15T12:00:00.000Z",
  "archivedAt": null
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |

## The subscription object

```json
{
  "object": "subscription",
  "livemode": true,
  "id": "sub_2208",
  "status": "active",
  "plan": {
    "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
    "title": "Studio membership",
    "amountCents": 4900,
    "currency": "usd",
    "interval": "month"
  },
  "customer": {
    "name": "Jordan Lee",
    "email": "jordan@example.com"
  },
  "currentPeriodEnd": "2026-11-15T12:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "canceledAt": null,
  "createdAt": "2026-09-15T12:05:00.000Z"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `object` | string | The type of object. Always `subscription`. |
| `livemode` | boolean | False for test-mode data (an `ak_test_` key); true for live data. |
| `id` | string | `sub_…` |
| `status` | string | `trialing`, `active`, `past_due` (a renewal failed and is being retried), `paused`, `canceling`, or `canceled`, `expired` or `completed` once it has ended. |
| `plan` | object |  |
| `plan.id` | string | `sp_…` |
| `plan.title` | string | Name. |
| `plan.amountCents` | integer | Price. Integer cents. |
| `plan.currency` | string | usd. |
| `plan.interval` | string | week, month, quarter or year. |
| `customer` | object |  |
| `customer.name` | string or null | Name. |
| `customer.email` | string | Email. |
| `currentPeriodEnd` | string (date-time) or null | When the period paid for ends (the next renewal). |
| `cancelAtPeriodEnd` | boolean | Set to cancel when the period ends. |
| `canceledAt` | string (date-time) or null | When it ended. |
| `createdAt` | string (date-time) | Subscribed. |

## Subscription endpoints

### List subscriptions

`GET /api/v1/subscriptions`

Permission: `subscriptions:read`.

Subscribers, newest first.

**Query**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | string | no | As the Subscriptions page groups them. One of `active`, `trialing`, `past_due`, `paused`, `canceling`, `ended`. |
| `planId` | string | no | Only subscribers of this plan, `sp_…`. |
| `customerEmail` | string | no | Only this customer's, any case. Up to 255 characters. |
| `limit` | integer | no | How many objects per page, 1–100. Default 25. See [Pagination](https://anytime-capital.com/docs/pagination.md). |
| `cursor` | string | no | The previous page's `nextCursor`, for the page after it. |

**Request (curl)**

```bash
curl "https://anytime-capital.com/api/v1/subscriptions?status=active&planId=sp_Vb7Nc4Zp9WkLm3Qx8Rt2" \
  -H "Authorization: Bearer $ANYTIME_API_KEY"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscriptions?status=active&planId=sp_Vb7Nc4Zp9WkLm3Qx8Rt2", {
  headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const page = await res.json();
if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const page = await client.subscriptions.list(
  { status: "active", planId: "sp_Vb7Nc4Zp9WkLm3Qx8Rt2" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.get(
    "https://anytime-capital.com/api/v1/subscriptions",
    headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
    params={"status": "active", "planId": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2"},
    timeout=30,
)
page = res.json()
if not res.ok:
    raise RuntimeError(f"{page['error']['code']}: {page['error']['message']}")
```

**Response** (200)

```json
{
  "object": "list",
  "data": [
    {
      "object": "subscription",
      "livemode": true,
      "id": "sub_2208",
      "status": "active",
      "plan": {
        "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
        "title": "Studio membership",
        "amountCents": 4900,
        "currency": "usd",
        "interval": "month"
      },
      "customer": {
        "name": "Jordan Lee",
        "email": "jordan@example.com"
      },
      "currentPeriodEnd": "2026-11-15T12:00:00.000Z",
      "cancelAtPeriodEnd": false,
      "canceledAt": null,
      "createdAt": "2026-09-15T12:05:00.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_INPUT` | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` Fix the named field. Don't retry the same request unchanged. |

### Get a subscription

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

Permission: `subscriptions:read`.

One subscriber.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `sub_…` |

**Request (curl)**

```bash
curl https://anytime-capital.com/api/v1/subscriptions/sub_2208 \
  -H "Authorization: Bearer $ANYTIME_API_KEY"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscriptions/sub_2208", {
  headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const subscription = await res.json();
if (!res.ok) throw new Error(`${subscription.error.code}: ${subscription.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const subscription = await client.subscriptions.retrieve("sub_2208");
```

**Request (Python)**

```python
import os

import requests

res = requests.get(
    "https://anytime-capital.com/api/v1/subscriptions/sub_2208",
    headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
    timeout=30,
)
subscription = res.json()
if not res.ok:
    raise RuntimeError(f"{subscription['error']['code']}: {subscription['error']['message']}")
```

**Response** (200)

```json
{
  "object": "subscription",
  "livemode": true,
  "id": "sub_2208",
  "status": "active",
  "plan": {
    "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
    "title": "Studio membership",
    "amountCents": 4900,
    "currency": "usd",
    "interval": "month"
  },
  "customer": {
    "name": "Jordan Lee",
    "email": "jordan@example.com"
  },
  "currentPeriodEnd": "2026-11-15T12:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "canceledAt": null,
  "createdAt": "2026-09-15T12:05:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |

### Cancel a subscription

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

Permission: `subscriptions:write`. Supports `Idempotency-Key`.

At the period's end (default) or now. The subscriber is emailed. At the period's end, `cancelAtPeriodEnd` is `true` until then; `atPeriodEnd: false` ends it now. The subscriber gets the same email as when you cancel from the dashboard.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `sub_…` |

**Headers**

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | no | Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |

**Body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `atPeriodEnd` | boolean | no | true (default): at the end of the period paid for. false: now. |

**Request (curl)**

```bash
curl -X POST https://anytime-capital.com/api/v1/subscriptions/sub_2208/cancel \
  -H "Authorization: Bearer $ANYTIME_API_KEY" \
  -H "Idempotency-Key: cancel-sub_2208" \
  -H "Content-Type: application/json" \
  -d '{
    "atPeriodEnd": true
  }'
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscriptions/sub_2208/cancel", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "cancel-sub_2208",
  },
  body: JSON.stringify({ atPeriodEnd: true }),
});
const subscription = await res.json();
if (!res.ok) throw new Error(`${subscription.error.code}: ${subscription.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const subscription = await client.subscriptions.cancel(
  "sub_2208",
  { atPeriodEnd: true },
  { idempotencyKey: "cancel-sub_2208" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.post(
    "https://anytime-capital.com/api/v1/subscriptions/sub_2208/cancel",
    headers={
        "Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
        "Idempotency-Key": "cancel-sub_2208",
    },
    json={"atPeriodEnd": True},
    timeout=30,
)
subscription = res.json()
if not res.ok:
    raise RuntimeError(f"{subscription['error']['code']}: {subscription['error']['message']}")
```

**Response** (200)

```json
{
  "object": "subscription",
  "livemode": true,
  "id": "sub_2208",
  "status": "active",
  "plan": {
    "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
    "title": "Studio membership",
    "amountCents": 4900,
    "currency": "usd",
    "interval": "month"
  },
  "customer": {
    "name": "Jordan Lee",
    "email": "jordan@example.com"
  },
  "currentPeriodEnd": "2026-11-15T12:00:00.000Z",
  "cancelAtPeriodEnd": true,
  "canceledAt": null,
  "createdAt": "2026-09-15T12:05:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_JSON` | The body isn't valid JSON, or couldn't be read. An empty body reads as `{}`. Send `Content-Type: application/json` and a JSON object. |
| 400 | `INVALID_INPUT` | A field, filter, `limit` or `cursor` is missing or invalid. The message starts with the field's name, as in `amountCents: …`, or reads `Invalid cursor.` Fix the named field. Don't retry the same request unchanged. |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |
| 409 | `SUBSCRIPTION_ENDED` | The subscription has already ended (canceled, expired or completed). Nothing to do; a customer subscribes again at the plan's `url`. |
| 409 | `ALREADY_CANCELING` | The subscription is already set to cancel at the end of its period. Send `atPeriodEnd: false` to end it now, or `POST …/resume` to keep it. |
| 409 | `CHANGE_IN_PROGRESS` | The subscription is switching plans right now. Retry in a minute. |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. |
| 502 | `PROCESSOR_ERROR` | The payments network didn't answer in time. Retry with the same `Idempotency-Key`. A refund is keyed at the network by that key too, so a retry never refunds twice. |
| 503 | `NOT_CONFIGURED` | The part of Anytime Capital Payments this needs isn't available right now. Retry later, with the same `Idempotency-Key` on a write. |

### Pause a subscription

`POST /api/v1/subscriptions/{id}/pause`

Permission: `subscriptions:write`. Supports `Idempotency-Key`.

Stop collecting payments on an active or trialing subscription.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `sub_…` |

**Headers**

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | no | Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |

**Request (curl)**

```bash
curl -X POST https://anytime-capital.com/api/v1/subscriptions/sub_2208/pause \
  -H "Authorization: Bearer $ANYTIME_API_KEY" \
  -H "Idempotency-Key: pause-sub_2208"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscriptions/sub_2208/pause", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
    "Idempotency-Key": "pause-sub_2208",
  },
});
const subscription = await res.json();
if (!res.ok) throw new Error(`${subscription.error.code}: ${subscription.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const subscription = await client.subscriptions.pause(
  "sub_2208",
  { idempotencyKey: "pause-sub_2208" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.post(
    "https://anytime-capital.com/api/v1/subscriptions/sub_2208/pause",
    headers={
        "Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
        "Idempotency-Key": "pause-sub_2208",
    },
    timeout=30,
)
subscription = res.json()
if not res.ok:
    raise RuntimeError(f"{subscription['error']['code']}: {subscription['error']['message']}")
```

**Response** (200)

```json
{
  "object": "subscription",
  "livemode": true,
  "id": "sub_2208",
  "status": "paused",
  "plan": {
    "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
    "title": "Studio membership",
    "amountCents": 4900,
    "currency": "usd",
    "interval": "month"
  },
  "customer": {
    "name": "Jordan Lee",
    "email": "jordan@example.com"
  },
  "currentPeriodEnd": "2026-11-15T12:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "canceledAt": null,
  "createdAt": "2026-09-15T12:05:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |
| 409 | `SUBSCRIPTION_ENDED` | The subscription has already ended (canceled, expired or completed). Nothing to do; a customer subscribes again at the plan's `url`. |
| 409 | `CANNOT_PAUSE` | Only an active or trialing subscription that renews can be paused: this one is paused, past due or set to cancel. Read the subscription's `status` and `cancelAtPeriodEnd` first. |
| 409 | `CHANGE_IN_PROGRESS` | The subscription is switching plans right now. Retry in a minute. |
| 409 | `SWITCH_SCHEDULED` | The subscription switches plans at its next renewal, so it can't be paused or resumed. Undo the switch from the subscriber's details on the **Subscriptions** page first. |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. |
| 502 | `PROCESSOR_ERROR` | The payments network didn't answer in time. Retry with the same `Idempotency-Key`. A refund is keyed at the network by that key too, so a retry never refunds twice. |
| 503 | `NOT_CONFIGURED` | The part of Anytime Capital Payments this needs isn't available right now. Retry later, with the same `Idempotency-Key` on a write. |

### Resume a subscription

`POST /api/v1/subscriptions/{id}/resume`

Permission: `subscriptions:write`. Supports `Idempotency-Key`.

Undo a pause, or a cancellation set for the period's end.

**Path**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | `sub_…` |

**Headers**

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | string | no | Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |

**Request (curl)**

```bash
curl -X POST https://anytime-capital.com/api/v1/subscriptions/sub_2208/resume \
  -H "Authorization: Bearer $ANYTIME_API_KEY" \
  -H "Idempotency-Key: resume-sub_2208"
```

**Request (Node.js)**

```javascript
const res = await fetch("https://anytime-capital.com/api/v1/subscriptions/sub_2208/resume", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
    "Idempotency-Key": "resume-sub_2208",
  },
});
const subscription = await res.json();
if (!res.ok) throw new Error(`${subscription.error.code}: ${subscription.error.message}`);
```

**Request (Node SDK)**

```typescript
import { AnytimeCapital } from "@anytimecapital/node";

const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });

const subscription = await client.subscriptions.resume(
  "sub_2208",
  { idempotencyKey: "resume-sub_2208" },
);
```

**Request (Python)**

```python
import os

import requests

res = requests.post(
    "https://anytime-capital.com/api/v1/subscriptions/sub_2208/resume",
    headers={
        "Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
        "Idempotency-Key": "resume-sub_2208",
    },
    timeout=30,
)
subscription = res.json()
if not res.ok:
    raise RuntimeError(f"{subscription['error']['code']}: {subscription['error']['message']}")
```

**Response** (200)

```json
{
  "object": "subscription",
  "livemode": true,
  "id": "sub_2208",
  "status": "active",
  "plan": {
    "id": "sp_Vb7Nc4Zp9WkLm3Qx8Rt2",
    "title": "Studio membership",
    "amountCents": 4900,
    "currency": "usd",
    "interval": "month"
  },
  "customer": {
    "name": "Jordan Lee",
    "email": "jordan@example.com"
  },
  "currentPeriodEnd": "2026-11-15T12:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "canceledAt": null,
  "createdAt": "2026-09-15T12:05:00.000Z"
}
```

**Errors**

| Status | Code | When |
| --- | --- | --- |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` header is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See [Idempotency](https://anytime-capital.com/docs/idempotency.md). |
| 404 | `NOT_FOUND` | No such object on your business (an id from another business reads the same), or no such endpoint under `/api/v1`, which still answers in JSON. Check the id and the path. Ids are case-sensitive. |
| 409 | `SUBSCRIPTION_ENDED` | The subscription has already ended (canceled, expired or completed). Nothing to do; a customer subscribes again at the plan's `url`. |
| 409 | `NOT_PAUSED` | The subscription is neither paused nor set to cancel, so there's nothing to resume. Nothing to do. |
| 409 | `CHANGE_IN_PROGRESS` | The subscription is switching plans right now. Retry in a minute. |
| 409 | `SWITCH_SCHEDULED` | The subscription switches plans at its next renewal, so it can't be paused or resumed. Undo the switch from the subscriber's details on the **Subscriptions** page first. |
| 422 | `IDEMPOTENCY_KEY_REUSED` | The `Idempotency-Key` was used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. |
| 502 | `PROCESSOR_ERROR` | The payments network didn't answer in time. Retry with the same `Idempotency-Key`. A refund is keyed at the network by that key too, so a retry never refunds twice. |
| 503 | `NOT_CONFIGURED` | The part of Anytime Capital Payments this needs isn't available right now. Retry later, with the same `Idempotency-Key` on a write. |

## Events

`subscription.created`, `subscription.updated`, `subscription.paused`, `subscription.resumed`, `subscription.past_due` and `subscription.canceled` carry the subscription object; each renewal's charge sends `payment.succeeded` with `source.type` `subscription`. The [subscriptions guide](https://anytime-capital.com/docs/sell-subscriptions.md) turns these into access in your app.

---

Anytime Capital is a fintech company, not a bank. Payment processing, banking and payout services are provided by Anytime Capital's licensed partners. Rates shown are for US card payments; international cards, currency conversion and disputes may carry additional fees shown in your dashboard before you accept a payment.
