curl --request POST \
--url https://api.payviox.com/subscriptions/{subscription_id}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"at_period_end": true
}
'import requests
url = "https://api.payviox.com/subscriptions/{subscription_id}/cancel"
payload = { "at_period_end": True }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({at_period_end: true})
};
fetch('https://api.payviox.com/subscriptions/{subscription_id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.payviox.com/subscriptions/{subscription_id}/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([
'at_period_end' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.payviox.com/subscriptions/{subscription_id}/cancel"
payload := strings.NewReader("{\n \"at_period_end\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.payviox.com/subscriptions/{subscription_id}/cancel")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"at_period_end\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.payviox.com/subscriptions/{subscription_id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"at_period_end\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "psub_8fKx2LmQp4ZrT7vW1nYc3aBd",
"status": "active",
"amount": 999,
"unit_amount": 1029,
"currency": "USD",
"interval": "month",
"interval_count": 1,
"interval_label": "every month",
"customer": "customer_abc123",
"customer_email": "john@example.com",
"order_id": "order_12345",
"items": [
{ "name": "Pro plan", "quantity": 1, "price": 999 }
],
"metadata": { "user_id": "usr_123" },
"payment_method": "credit",
"provider": "stripe",
"provider_subscription_id": "sub_1QxYz2AbCdEfGhIj",
"cycles_count": 2,
"current_period_end": "2026-12-13T10:30:00+00:00",
"cancel_at_period_end": true,
"canceled_at": null,
"canceled_by": null,
"cancel_requested_by": "api",
"cancellation_reason": "requested_by_merchant",
"initial_session_id": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"management_url": "https://secure.payviox.com/subscription/k3Jd9QwL2xVb7NmP4tRz8YcF1sHg6UaE5oWi0qXn3BvM7ZpD",
"created_at": "2026-10-13T10:30:00+00:00"
}
{
"error": "subscription_update_failed",
"message": "This subscription is already canceled."
}
{
"error": "subscription_update_failed",
"message": "The payment provider is unreachable. Try again in a moment."
}
{
"at_period_end": ["at_period_end must be a boolean."]
}
Cancel Subscription
Cancel a subscription with the secret key. at_period_end=true (default, also when omitted, null or empty) schedules the end at current_period_end — the customer keeps the paid period and you can undo it with resume; at_period_end=false ends it now, voids unpaid Stripe invoices, no refund. Returns the Subscription. Errors: 400 non-boolean, 409 already canceled, 502 Stripe refused, 503 Stripe unreachable
curl --request POST \
--url https://api.payviox.com/subscriptions/{subscription_id}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"at_period_end": true
}
'import requests
url = "https://api.payviox.com/subscriptions/{subscription_id}/cancel"
payload = { "at_period_end": True }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({at_period_end: true})
};
fetch('https://api.payviox.com/subscriptions/{subscription_id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.payviox.com/subscriptions/{subscription_id}/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([
'at_period_end' => true
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.payviox.com/subscriptions/{subscription_id}/cancel"
payload := strings.NewReader("{\n \"at_period_end\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.payviox.com/subscriptions/{subscription_id}/cancel")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"at_period_end\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.payviox.com/subscriptions/{subscription_id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"at_period_end\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "psub_8fKx2LmQp4ZrT7vW1nYc3aBd",
"status": "active",
"amount": 999,
"unit_amount": 1029,
"currency": "USD",
"interval": "month",
"interval_count": 1,
"interval_label": "every month",
"customer": "customer_abc123",
"customer_email": "john@example.com",
"order_id": "order_12345",
"items": [
{ "name": "Pro plan", "quantity": 1, "price": 999 }
],
"metadata": { "user_id": "usr_123" },
"payment_method": "credit",
"provider": "stripe",
"provider_subscription_id": "sub_1QxYz2AbCdEfGhIj",
"cycles_count": 2,
"current_period_end": "2026-12-13T10:30:00+00:00",
"cancel_at_period_end": true,
"canceled_at": null,
"canceled_by": null,
"cancel_requested_by": "api",
"cancellation_reason": "requested_by_merchant",
"initial_session_id": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"management_url": "https://secure.payviox.com/subscription/k3Jd9QwL2xVb7NmP4tRz8YcF1sHg6UaE5oWi0qXn3BvM7ZpD",
"created_at": "2026-10-13T10:30:00+00:00"
}
{
"error": "subscription_update_failed",
"message": "This subscription is already canceled."
}
{
"error": "subscription_update_failed",
"message": "The payment provider is unreachable. Try again in a moment."
}
{
"at_period_end": ["at_period_end must be a boolean."]
}
Overview
Stops the renewals of anactive or past_due subscription. By default the subscription ends at the end of the period the customer already paid for; pass at_period_end: false to end it immediately — this also works on a subscription already scheduled to end.
at_period_end: true (default) | at_period_end: false | |
|---|---|---|
| Further charges | None after current_period_end | None, from now — unpaid Stripe invoices of the subscription are voided, so no retry can charge the customer |
status right after the call | Unchanged (active or past_due), with cancel_at_period_end: true | canceled |
| Can be undone | Yes, with Resume Subscription until the period ends | No |
cancel_requested_by | api | unchanged |
subscription_canceled webhook | When the period ends | Right away |
| Refund of the current period | — | None (no automatic refund) |
management_url of their subscription — you don’t need to build a cancellation flow. See Let customers manage their subscription.Authentication
This endpoint requires your Secret API Key (server-side only). See Authentication for details.Authorization: Bearer sk_live_xxxxxxxxxxxx
Path Parameters
psub_...).Body Parameters
true to end the subscription at current_period_end, false to end it now. Omitted, null or "" mean true; any other non-boolean value returns 400.Response
The updated Subscription object. Calling it again withat_period_end: true on a subscription already scheduled to end returns 200 without changing anything (and keeps its cancel_requested_by). You can still end immediately a subscription the customer scheduled to cancel.
Example Requests
curl https://api.payviox.com/subscriptions/psub_8fKx2LmQp4ZrT7vW1nYc3aBd/cancel \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-X POST
curl https://api.payviox.com/subscriptions/psub_8fKx2LmQp4ZrT7vW1nYc3aBd/cancel \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-X POST \
-d '{ "at_period_end": false }'
async function cancelSubscription(subscriptionId, { now = false } = {}) {
const response = await fetch(
`https://api.payviox.com/subscriptions/${subscriptionId}/cancel`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.PAYVIOX_SECRET_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ at_period_end: !now }),
}
);
if (response.status === 503) {
throw new Error('Stripe unreachable, retry later'); // safe to retry
}
if (!response.ok) {
const { error, message } = await response.json();
throw new Error(`${error}: ${message}`);
}
return response.json(); // Subscription
}
import os
import requests
response = requests.post(
'https://api.payviox.com/subscriptions/psub_8fKx2LmQp4ZrT7vW1nYc3aBd/cancel',
headers={'Authorization': f"Bearer {os.environ['PAYVIOX_SECRET_KEY']}"},
json={'at_period_end': True}
)
subscription = response.json()
print(subscription['cancel_at_period_end']) # True
Example Response
{
"id": "psub_8fKx2LmQp4ZrT7vW1nYc3aBd",
"status": "active",
"amount": 999,
"unit_amount": 1029,
"currency": "USD",
"interval": "month",
"interval_count": 1,
"interval_label": "every month",
"customer": "customer_abc123",
"customer_email": "john@example.com",
"order_id": "order_12345",
"items": [
{ "name": "Pro plan", "quantity": 1, "price": 999 }
],
"metadata": { "user_id": "usr_123" },
"payment_method": "credit",
"provider": "stripe",
"provider_subscription_id": "sub_1QxYz2AbCdEfGhIj",
"cycles_count": 2,
"current_period_end": "2026-12-13T10:30:00+00:00",
"cancel_at_period_end": true,
"canceled_at": null,
"canceled_by": null,
"cancel_requested_by": "api",
"cancellation_reason": "requested_by_merchant",
"initial_session_id": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"management_url": "https://secure.payviox.com/subscription/k3Jd9QwL2xVb7NmP4tRz8YcF1sHg6UaE5oWi0qXn3BvM7ZpD",
"created_at": "2026-10-13T10:30:00+00:00"
}
{
"error": "subscription_update_failed",
"message": "This subscription is already canceled."
}
{
"error": "subscription_update_failed",
"message": "The payment provider is unreachable. Try again in a moment."
}
{
"at_period_end": ["at_period_end must be a boolean."]
}
Error Responses
400 -- Invalid at_period_end
400 -- Invalid at_period_end
at_period_end is neither a boolean nor empty (for example "maybe"). Omitted, null or "" mean true.401 -- Unauthorized
401 -- Unauthorized
404 -- Subscription not found
404 -- Subscription not found
subscription_not_found).409 -- Already canceled
409 -- Already canceled
409 with The subscription changed meanwhile. Try again. means it was modified during your request: retry.502 -- Provider refused
502 -- Provider refused
503 -- Provider unreachable
503 -- Provider unreachable
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Payviox subscription ID (psub_...)
Body
true (default, also when omitted, null or empty): the subscription ends at current_period_end, the customer keeps the period already paid, and you can undo it with resume. false: ends now and voids its unpaid Stripe invoices, so no retry can charge the customer; no automatic refund.
Response
Subscription updated (or already scheduled to cancel); returns the subscription
Payviox subscription ID (psub_ prefix)
active: paid up, renews automatically (also while a cancellation is scheduled, see cancel_at_period_end). past_due: the last renewal charge failed and Stripe is retrying it. canceled: ended, no further charges.
active, past_due, canceled Your price per period in cents — the amount of the signup session
Amount charged to the customer at each renewal, in cents. Includes the fees added for the customer at checkout, but not one-time add-on fees (such as fraud screening) charged on the first payment only, so it can be lower than the first charge.
Always USD
day, week, month, year Human-readable period, e.g. every month, every 3 months
order_id of the signup session. Renewals use {order_id}-{cycle}
Billing cycles started so far, signup included (1 = signup only). A renewal whose charge failed counts too.
true when the subscription is scheduled to end at current_period_end
Session ID of the signup payment
Customer identifier from the signup session
Customer email, from the signup session or entered at checkout
Items of the signup session (one period)
Metadata of the signup session
Method the customer subscribed with (read with provider)
credit, apple_pay, google_pay Provider that charges the renewals (stripe)
ID of the subscription on your Stripe account (sub_...). null until Payviox has created it, usually within a minute of the first payment.
End of the current billing period: date of the next renewal charge, or the date a scheduled cancellation takes effect
When the subscription ended (null while not canceled)
Who ended it: api (your backend), manager (your dashboard), admin (Payviox support), payer (the customer, from the management link), provider (on Stripe, e.g. after failed retries or a cancellation in the Stripe dashboard), system (Payviox, e.g. signup payment refunded or setup_failed). null until the subscription is canceled.
api, manager, admin, payer, provider, system, null Who scheduled the end-of-period cancellation: payer (the customer), api, manager (your dashboard), admin (Payviox support) or system. null when no cancellation is scheduled, or when it was scheduled from your Stripe dashboard. Only the same side can resume it: you (api, manager, null), the customer (payer) or Payviox (admin, system).
payer, api, manager, admin, system, null Set as soon as a cancellation is requested or scheduled: requested_by_merchant, requested_by_customer, initial_payment_refunded, admin (canceled by Payviox support), setup_failed:<code>, or a Stripe reason such as payment_failed, cancellation_requested, provider or provider_deleted. Treat unknown values as informational.
Customer self-service page (view, cancel at period end, undo). Share it with your customer, e.g. in your confirmation email.