Subscriptions
Recurring billing on your spuke account
Subscriptions
Create recurring subscriptions for customers using a recurring price from your catalog. Subscriptions run on your connected spuke account; spuke deducts the platform fee automatically from each renewal invoice.
Base URL: https://api.spuke.com/v1
Auth: Authorization: Bearer sk_live_… (or sk_test_…)
Prerequisites
- Create a product and a recurring price (see Products and Prices).
- The price must have
recurring.intervalset (day,week,month,year).
Create a subscription
POST /v1/subscriptions
curl -X POST https://api.spuke.com/v1/subscriptions \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: sub_20260718_001" \
-d '{
"price_id": "price_1Tu…",
"customer_name": "Jane Doe",
"customer_email": "jane@example.com",
"quantity": 1,
"trial_days": 14,
"metadata": { "plan": "pro" }
}'
Body
| Field | Type | Required | Notes |
|---|---|---|---|
price_id |
string | ✅ | Recurring price from your catalog. |
customer_name |
string | ✅ | 2–120 chars. |
customer_email |
string | ✅ | Valid email. Receipts and dunning are sent here. |
quantity |
integer | ➖ | Defaults to 1. |
trial_days |
integer | ➖ | Free trial before first charge. |
metadata |
object | ➖ | Free-form key/value returned in webhooks. |
Response
{
"id": "sub_1Tu…",
"object": "subscription",
"status": "incomplete",
"customer": "cus_1Tu…",
"price_id": "price_1Tu…",
"quantity": 1,
"currency": "EUR",
"amount": 1990,
"current_period_start": 1735689600,
"current_period_end": 1738368000,
"latest_invoice": "in_1Tu…",
"client_secret": "pi_1Tu…_secret_…",
"trial_end": null,
"created": 1735689600
}
The subscription is created in incomplete state. Use the returned client_secret on the customer's browser to confirm the first payment. Once confirmed the status transitions to active (or trialing if trial_days was set) and a customer.subscription.updated webhook is fired.
Retrieve
GET /v1/subscriptions/{id}
List
GET /v1/subscriptions?limit=20
Returns up to 100 subscriptions ordered by creation date (newest first).
Cancel
POST /v1/subscriptions/{id}/cancel
curl -X POST https://api.spuke.com/v1/subscriptions/sub_1Tu…/cancel \
-H "Authorization: Bearer sk_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cancel_sub_1Tu_20260718" \
-d '{ "at_period_end": true }'
| Field | Default | Meaning |
|---|---|---|
at_period_end |
true |
Keep active until the current period ends, then cancel. |
at_period_end: false |
— | Cancel immediately. No further invoices. |
Status model
| Status | Meaning |
|---|---|
incomplete |
Waiting for the first payment confirmation. |
incomplete_expired |
First payment was not confirmed within 23 h. |
trialing |
Free trial in progress. |
active |
Paid and current. |
past_due |
Renewal failed. Dunning in progress. |
unpaid |
All retries exhausted, subscription frozen. |
canceled |
Terminated. |
Fees
Recurring invoices carry a platform fee computed from your plan and payment method. Fees are settled in EUR and deducted per invoice — same rules as one-off Checkout Sessions. See Fees, currency & minimum amounts.
Idempotency
Send Idempotency-Key: <your-key> on POST /v1/subscriptions and POST /v1/subscriptions/{id}/cancel. Keys are scoped to (merchant_id, method, path) and expire after 24 hours.
- Same key + same body → original response replayed verbatim.
- Same key + different body →
409 Conflictwithtype: "idempotency_error",code: "idempotency_key_reused". See Error reference.
Errors
| HTTP | error.type |
error.code |
Meaning |
|---|---|---|---|
| 400 | invalid_request |
field-specific | Missing / malformed field, or price is not recurring. |
| 401 | authentication_error |
invalid_api_key |
Bad, revoked or wrong-mode key. |
| 404 | not_found |
price_not_found / subscription_not_found |
Not owned by your account. |
| 409 | idempotency_error |
idempotency_key_reused |
Same key with different body. |
| 409 | resource_conflict |
merchant_not_ready / subscription_already_canceled |
See message. |
| 429 | rate_limit_error |
rate_limited |
Respect Retry-After. |
Webhooks
Listen for these events (see Event reference for payloads):
customer.subscription.created/updated/deletedcustomer.subscription.trial_will_end— fires 3 days before the trial endsinvoice.payment_succeeded— renewal chargedinvoice.payment_failed— dunning stage advanced (notice → warning → final)