Subscriptions
A subscription is the binding between a customer, a product, and a product option for a defined period of time. Subscriptions can be active or expired, recurring or one-off, paid or free trials.
The Seller API supports listing, reading, granting, extending, and canceling subscriptions. Deletion is not supported.
GET /v1/subscriptions
List subscriptions, most recent first.
Tier: B · Auth: required · Idempotent: N/A
Query parameters
| Parameter | Type | Description |
|---|---|---|
active | boolean | Filter by active state. |
customerId | string | Filter by Discord user ID. |
productId | integer | Filter by product. |
paymentMethod | string | One of stripe, crypto, free. |
limit | integer | Page size (max 100, default 50). |
cursor | string | Pagination cursor. |
Request
curl "https://subscord.com/api/v1/subscriptions?active=true&productId=42&limit=25" \
-H "Authorization: Bearer ssk_live_..."Response — 200 OK
{
"data": [
{
"id": "1532",
"customerDiscordId": "987654321098765432",
"username": "johndoe",
"product": { "name": "Premium" },
"productOption": { "name": "Monthly" },
"startedAt": 1745539921,
"expiresAt": 1748131921,
"status": "Active",
"type": "Recurring",
"freeTrial": "No",
"paymentMethod": "stripe"
}
],
"nextCursor": "eyJpZCI6MTUzMn0"
}GET /v1/subscriptions/{id}
Returns a single subscription.
Tier: A · Auth: required · Idempotent: N/A
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | The subscription ID. |
Request
curl https://subscord.com/api/v1/subscriptions/1532 \
-H "Authorization: Bearer ssk_live_..."Response — 200 OK
Same shape as a single entry in the list response above.
POST /v1/subscriptions
Manually grant a subscription to a customer. Useful for backfills, manual sales handled outside Subscord, or comp accounts.
Tier: D · Auth: required · Idempotent: required (Idempotency-Key header)
Note. This creates the Subscord subscription record and updates the customer's Discord roles. It does not charge the customer or open a Stripe subscription. The created subscription is recorded with
paymentMethod: "free"until a real invoice is attached. If the customer is not yet a Subscord customer, they are created as part of this call.
Request body
| Field | Type | Description |
|---|---|---|
customer.id | string | The Discord user ID. |
customer.username | string | The Discord username (used to create or refresh the customer record). |
customer.avatar | string | Optional avatar URL or hash. |
productId | integer | The product to subscribe them to. |
optionId | integer | The product option that defines billing period. Must belong to productId. |
expiresAt | string | ISO 8601 UTC timestamp when the subscription should expire. Must be in the future. |
Request
curl https://subscord.com/api/v1/subscriptions \
-X POST \
-H "Authorization: Bearer ssk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0e9f8c8b-c3f4-4e1d-9b9a-1a2b3c4d5e6f" \
-d '{
"customer": {
"id": "987654321098765432",
"username": "johndoe"
},
"productId": 42,
"optionId": 101,
"expiresAt": "2026-12-31T23:59:59Z"
}'Response — 201 Created
Returns the newly created subscription using the same shape as GET /v1/subscriptions/{id}.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | Missing or invalid fields. productId does not belong to this server. optionId does not belong to productId. expiresAt is in the past or invalid. |
| 409 | conflict | The server's plan member limit has been reached and no more subscriptions can be granted. |
POST /v1/subscriptions/{id}/extend
Extend a subscription by a number of days. For Stripe-backed subscriptions, this also pushes the next renewal date in Stripe.
Tier: D · Auth: required · Idempotent: required
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | The subscription ID. |
Request body
| Field | Type | Description |
|---|---|---|
days | integer | Number of days to extend (1 to 365). |
Request
curl https://subscord.com/api/v1/subscriptions/1532/extend \
-X POST \
-H "Authorization: Bearer ssk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: extend-1532-2026-04-25" \
-d '{ "days": 30 }'Response — 200 OK
Returns the updated subscription.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | id is not an integer or days is out of range. |
| 404 | not_found | No subscription with that ID exists for this server. |
POST /v1/subscriptions/{id}/cancel
Cancel a subscription. Marks it inactive in Subscord, cancels the Stripe subscription if applicable, and refreshes the customer's Discord roles.
Tier: D · Auth: required · Idempotent: required
No-op when already inactive. Calling cancel on a subscription that is already inactive is safe — the API simply returns the current state without making any changes.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | The subscription ID. |
Request
curl https://subscord.com/api/v1/subscriptions/1532/cancel \
-X POST \
-H "Authorization: Bearer ssk_live_..." \
-H "Idempotency-Key: cancel-1532-once" \
-H "Content-Type: application/json"Response — 200 OK
Returns the canceled subscription with status: "Expired".
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | id is not an integer. |
| 404 | not_found | No subscription with that ID exists for this server. |
Field reference
The response shape mirrors the CSV export in your dashboard, plus customerDiscordId for stable cross-system linking.
| Field | Type | Description |
|---|---|---|
id | string | Subscription ID. |
customerDiscordId | string | The customer's Discord user ID. |
username | string | The customer's Discord username at the time of the response. |
product.name | string | null | The product name. |
productOption.name | string | null | The product option name. |
startedAt | integer | Unix timestamp (seconds) when the subscription started. See Conventions. |
expiresAt | integer | Unix timestamp (seconds) when the subscription expires. |
status | string | "Active" or "Expired". |
type | string | "Recurring" or "One-off". |
freeTrial | string | "Yes" or "No". |
paymentMethod | string | null | "stripe", "crypto", "free", or null. |