curl --request POST \
--url https://bouclay.com/api/v1/subscriptions/{subscription}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"reason": "<string>",
"comment": "<string>"
}
'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({reason: '<string>', comment: '<string>'})
};
fetch('https://bouclay.com/api/v1/subscriptions/{subscription}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://bouclay.com/api/v1/subscriptions/{subscription}/cancel"
payload = {
"reason": "<string>",
"comment": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://bouclay.com/api/v1/subscriptions/{subscription}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '<string>',
'comment' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"data": {
"id": "<string>",
"planId": "<string>",
"status": "<string>",
"pauseReason": "requested",
"pauseResumesAt": "2023-11-07T05:31:56Z",
"currency": "<string>",
"collectionMode": "automatic",
"daysUntilDue": 123,
"reference": "<string>",
"invoicingThreshold": 1,
"minimumCommitment": 1,
"spendingCap": {
"amount": 1,
"covers": "usage",
"behavior": "cap",
"warnAt": [
123
],
"since": "2023-11-07T05:31:56Z"
},
"trialEndsAt": "2023-11-07T05:31:56Z",
"currentPeriodStart": "2023-11-07T05:31:56Z",
"currentPeriodEnd": "2023-11-07T05:31:56Z",
"canceledAt": "2023-11-07T05:31:56Z",
"endsAt": "2023-11-07T05:31:56Z",
"cancellationDetails": {
"reason": "<string>",
"comment": "<string>",
"source": "portal"
},
"customer": {
"id": "<string>",
"email": "jsmith@example.com",
"name": "<string>",
"taxId": "<string>"
},
"discount": {
"id": "<string>",
"code": "<string>",
"remainingIntervals": 123
},
"createdAt": "2023-11-07T05:31:56Z",
"trialEndBehavior": "<string>",
"paymentMethod": {
"id": "<string>"
},
"items": [
{
"id": "<string>",
"productId": "<string>",
"planId": "<string>",
"chargeId": "<string>",
"priceId": "<string>",
"quantity": 123,
"status": "<string>"
}
],
"scheduledChanges": [
{
"action": "cancel",
"effectiveAt": "2023-11-07T05:31:56Z",
"itemId": "si_123",
"quantity": 2,
"priceId": "<string>",
"remove": true,
"planId": "<string>",
"chargeId": "<string>",
"resumesAt": "2023-11-07T05:31:56Z"
}
],
"sharedMeters": [
{
"meterId": "<string>",
"meterName": "<string>",
"subscriptionIds": [
"<string>"
]
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"type": "request_error",
"code": "authentication_failed",
"detail": "<string>",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"type": "request_error",
"code": "authentication_failed",
"detail": "<string>",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"type": "request_error",
"code": "authentication_failed",
"detail": "<string>",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}Cancel a subscription
curl --request POST \
--url https://bouclay.com/api/v1/subscriptions/{subscription}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"reason": "<string>",
"comment": "<string>"
}
'const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({reason: '<string>', comment: '<string>'})
};
fetch('https://bouclay.com/api/v1/subscriptions/{subscription}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://bouclay.com/api/v1/subscriptions/{subscription}/cancel"
payload = {
"reason": "<string>",
"comment": "<string>"
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://bouclay.com/api/v1/subscriptions/{subscription}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'reason' => '<string>',
'comment' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"data": {
"id": "<string>",
"planId": "<string>",
"status": "<string>",
"pauseReason": "requested",
"pauseResumesAt": "2023-11-07T05:31:56Z",
"currency": "<string>",
"collectionMode": "automatic",
"daysUntilDue": 123,
"reference": "<string>",
"invoicingThreshold": 1,
"minimumCommitment": 1,
"spendingCap": {
"amount": 1,
"covers": "usage",
"behavior": "cap",
"warnAt": [
123
],
"since": "2023-11-07T05:31:56Z"
},
"trialEndsAt": "2023-11-07T05:31:56Z",
"currentPeriodStart": "2023-11-07T05:31:56Z",
"currentPeriodEnd": "2023-11-07T05:31:56Z",
"canceledAt": "2023-11-07T05:31:56Z",
"endsAt": "2023-11-07T05:31:56Z",
"cancellationDetails": {
"reason": "<string>",
"comment": "<string>",
"source": "portal"
},
"customer": {
"id": "<string>",
"email": "jsmith@example.com",
"name": "<string>",
"taxId": "<string>"
},
"discount": {
"id": "<string>",
"code": "<string>",
"remainingIntervals": 123
},
"createdAt": "2023-11-07T05:31:56Z",
"trialEndBehavior": "<string>",
"paymentMethod": {
"id": "<string>"
},
"items": [
{
"id": "<string>",
"productId": "<string>",
"planId": "<string>",
"chargeId": "<string>",
"priceId": "<string>",
"quantity": 123,
"status": "<string>"
}
],
"scheduledChanges": [
{
"action": "cancel",
"effectiveAt": "2023-11-07T05:31:56Z",
"itemId": "si_123",
"quantity": 2,
"priceId": "<string>",
"remove": true,
"planId": "<string>",
"chargeId": "<string>",
"resumesAt": "2023-11-07T05:31:56Z"
}
],
"sharedMeters": [
{
"meterId": "<string>",
"meterName": "<string>",
"subscriptionIds": [
"<string>"
]
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"type": "request_error",
"code": "authentication_failed",
"detail": "<string>",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"type": "request_error",
"code": "authentication_failed",
"detail": "<string>",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"type": "request_error",
"code": "authentication_failed",
"detail": "<string>",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
]
},
"meta": {
"requestId": "req_550e8400-e29b-41d4-a716-446655440000"
}
}Authorizations
Use a Bouclay secret key (sk_test_..., sk_live_...) or restricted key
(rk_test_..., rk_live_...).
A secret key can call every endpoint. A restricted key can only call the
endpoints its scopes cover. A scope is {resource}:{access}, for example
customers:read or refunds:write, and write includes read. GET
requests need read access and everything else needs write, except
POST /subscriptions/{id}/items/{item}/preview, which only needs
subscriptions:read. A request outside the key's scopes gets 403 permission_denied naming the missing scope, and its Idempotency-Key
is not used up.
| Resource | Endpoints | Access |
|---|---|---|
customers | customers, their addresses and payment methods | read, write |
entitlements | /customers/{id}/entitlements | read |
credits | /customers/{id}/credit-balance, credit purchases, credit grants, auto-reloads, credit holds (committing one records its usage) | read, write |
catalog | products, plans, prices | read, write |
meters | meters | read, write |
usage | meter events, /customers/{id}/usage | read, write |
discounts | discounts | read |
subscriptions | subscriptions | read, write |
invoices | invoices | read, write |
credit_notes | credit notes, POST /invoices/{id}/credit-notes (a refund to the card also needs refunds:write) | read, write |
payments | payments | read |
refunds | refunds, POST /payments/{id}/refunds, chargebacks and their outcome | read, write |
checkout_sessions | checkout sessions | read, write |
portal_sessions | portal sessions | write |
events | events | read |
webhook_endpoints | webhook endpoints | read, write |
metrics | metrics | read |
Publishable keys (pk_...) are rejected by server-side API endpoints. A
key's mode scopes what it can see and charge: a test key can never reach
a live card.
Headers
Required for POST and PATCH requests. Replays identical method + path + body requests and rejects mismatched re-use.
1 - 255Path Parameters
Body
immediately, period_end One of your cancellation reasons (crsn_…), set up under Subscriptions, Cancellation flow. Recorded with the cancel and returned as cancellationDetails.reason.
What the customer said, in their words.
1000