Subscriptions
Update a Subscription
Update the editable fields of an existing subscription, or move it to another plan.
PATCH
/
v2
/
subscriptions
/
{id}
Update a Subscription
curl --request PATCH \
--url https://api.pagou.ai/v2/subscriptions/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"metadata": {},
"retry_offsets_days": [
15
],
"product_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"quantity": 500000
}
'import requests
url = "https://api.pagou.ai/v2/subscriptions/{id}"
payload = {
"metadata": {},
"retry_offsets_days": [15],
"product_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"quantity": 500000
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
metadata: {},
retry_offsets_days: [15],
product_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
quantity: 500000
})
};
fetch('https://api.pagou.ai/v2/subscriptions/{id}', 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.pagou.ai/v2/subscriptions/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'metadata' => [
],
'retry_offsets_days' => [
15
],
'product_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'quantity' => 500000
]),
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.pagou.ai/v2/subscriptions/{id}"
payload := strings.NewReader("{\n \"metadata\": {},\n \"retry_offsets_days\": [\n 15\n ],\n \"product_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"quantity\": 500000\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.pagou.ai/v2/subscriptions/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"metadata\": {},\n \"retry_offsets_days\": [\n 15\n ],\n \"product_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"quantity\": 500000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pagou.ai/v2/subscriptions/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"metadata\": {},\n \"retry_offsets_days\": [\n 15\n ],\n \"product_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"quantity\": 500000\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"requestId": "0190a2b4-18a7-7de0-9a43-69b7cf261201",
"data": {
"id": "string",
"customerId": "string",
"status": "incomplete",
"paymentMethod": "credit_card",
"billingModel": "merchant_initiated",
"billingDayOfMonth": 1,
"interval": "day",
"intervalCount": 1,
"amount": 0,
"currency": "BRL",
"trialEnd": "2026-07-14T12:00:00.000Z",
"currentPeriodStart": "2026-07-14T12:00:00.000Z",
"currentPeriodEnd": "2026-07-14T12:00:00.000Z",
"cancelAtPeriodEnd": false,
"canceledAt": "2026-07-14T12:00:00.000Z",
"failurePolicy": "immediate_cancel",
"retryOffsetsDays": [
1
],
"cancellationReason": "user_requested",
"customerEmail": "string",
"cardLast4": "string",
"metadata": {},
"informations": [
{
"key": "string",
"value": "string"
}
],
"products": [
{
"name": "string",
"price": 0,
"quantity": 1,
"tangible": false
}
],
"recurringAuthorization": {
"status": "pending"
},
"createdAt": "2026-07-14T12:00:00.000Z",
"updatedAt": "2026-07-14T12:00:00.000Z"
}
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>"
}This endpoint does two different jobs, and one request may do only one of them.
The billing cycle takes precedence: moving between plans billed on different cycles is always immediate and always charges the full price, whichever tier the target is.
When a charge is declined, the plan does not change — the request fails with
- Edit fields.
metadata,failure_policyandretry_offsets_dayscan be changed at any time. - Change the plan. Send
product_id, optionally withquantity.
product_id cannot be combined with the editable fields, and quantity requires product_id. Both combinations are rejected with 422 so that no part of the request is silently dropped.
Amount, interval and currency are not edited directly — they come from the plan, and they change when the plan changes.
A subscription.updated webhook is dispatched in every case.
On a Pix Automático subscription, plan changes are not available: sending product_id returns 422 and the plan stays the same (PLAN_CHANGE_UNAVAILABLE_FOR_PAYMENT_METHOD when the subscription is tied to a catalog plan). retry_offsets_days is stored but has no effect there, because retry timing follows the Pix Automático rules.
Which plans a subscription can move to
A subscription can only change plans when it is already tied to a catalog plan. CheckproductId first: when it is null, no plan change is possible.
The target must be an active plan in the same product family as the current one — productFamilyId tells you which family that is. A target in another family, or a product that is not a recurring plan, is rejected before anything is charged or written.
What happens, and when the customer is charged
| Case | Effect |
|---|---|
| Plan of a higher tier | charged immediately for what remains of the current period; the renewal date does not move |
| Plan of a lower tier | scheduled for current_period_end. Nothing is charged and nothing is refunded |
| Any plan whose total comes out below the current amount — fewer units, or a higher tier priced under the current plan | scheduled for current_period_end, exactly like a lower tier |
| Plan with a different billing cycle | applied immediately at the full price of the new plan; the renewal date restarts |
Subscription is past_due | applied immediately at the full price of the new plan, settling the open cycle; the subscription returns to active |
402 and the subscription is left exactly as it was.
When the charge returns no result at all — a timeout, for instance — the outcome is unknown rather than a decline, so the request fails with 409 PLAN_CHARGE_INDETERMINATE instead. The plan does not change. Retry the same change once the charge settles; retrying it earlier is refused, because the charge may already have gone through.
Scheduled changes
A change scheduled for the end of the period is visible inpendingChange, with the target plan, the amount that will be billed — the plan plus anything billed alongside it — and the date it takes effect. Its price is fixed when you schedule it, so later edits to the plan do not alter what was agreed.
To cancel a scheduled change, send product_id with the plan the subscription is currently on. pendingChange returns to null.
Response fields
| Field | Meaning |
|---|---|
productId | the plan being billed, or null when the subscription is not tied to one |
productFamilyId | the product family the plan belongs to, or null |
pendingChange | the scheduled change (productId, amount, effectiveAt), or null |
Errors
| Status | Code | When |
|---|---|---|
422 | PRODUCT_NOT_FOUND | the product id does not resolve, or the target plan is inactive |
422 | PRODUCT_NOT_RECURRING | the target is not a recurring plan |
422 | PLAN_NOT_IN_SAME_FAMILY | the target belongs to another family, or either side has none |
422 | SUBSCRIPTION_HAS_NO_CATALOG_PLAN | the subscription is not tied to a catalog plan |
422 | PLAN_CHANGE_CURRENCY_MISMATCH | the target is priced in a different currency |
422 | PLAN_CHANGE_SAME_PRODUCT | the subscription is already on that plan and has nothing scheduled |
422 | PLAN_CHANGE_NOT_ALLOWED_DURING_TRIAL | the subscription is trialing |
422 | PLAN_CHANGE_NOT_ALLOWED_FOR_STATUS | the status does not allow a change |
422 | PLAN_CHANGE_UNAVAILABLE_FOR_PAYMENT_METHOD | the payment method does not support changing plans |
409 | PLAN_CHANGE_BLOCKED_BY_PENDING_RENEWAL | a charge for this subscription is still in flight; retry once it settles |
402 | PLAN_CHARGE_DECLINED | the charge for the new plan was declined |
409 | PLAN_CHARGE_INDETERMINATE | the charge returned no result; the plan did not change and the charge is being reconciled |
409 | PLAN_CHANGE_NOT_APPLIED | the subscription changed while the change was being applied; the plan was not changed |
Read next
Authorizations
BearerAuthApiKeyAuthBasicAuth
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Public subscription id (UUID).
Pattern:
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Body
application/json
Show child attributes
Show child attributes
Available options:
immediate_cancel, retry_then_cancel Required array length:
1 - 10 elementsRequired range:
1 <= x <= 30Public id of a catalog product in the same family. Changes the subscription's plan.
Pattern:
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Quantity for the new plan. Omitted keeps the current one.
Required range:
1 <= x <= 999999Update a Subscription
curl --request PATCH \
--url https://api.pagou.ai/v2/subscriptions/{id} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"metadata": {},
"retry_offsets_days": [
15
],
"product_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"quantity": 500000
}
'import requests
url = "https://api.pagou.ai/v2/subscriptions/{id}"
payload = {
"metadata": {},
"retry_offsets_days": [15],
"product_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"quantity": 500000
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
metadata: {},
retry_offsets_days: [15],
product_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
quantity: 500000
})
};
fetch('https://api.pagou.ai/v2/subscriptions/{id}', 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.pagou.ai/v2/subscriptions/{id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'metadata' => [
],
'retry_offsets_days' => [
15
],
'product_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'quantity' => 500000
]),
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.pagou.ai/v2/subscriptions/{id}"
payload := strings.NewReader("{\n \"metadata\": {},\n \"retry_offsets_days\": [\n 15\n ],\n \"product_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"quantity\": 500000\n}")
req, _ := http.NewRequest("PATCH", 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.patch("https://api.pagou.ai/v2/subscriptions/{id}")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"metadata\": {},\n \"retry_offsets_days\": [\n 15\n ],\n \"product_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"quantity\": 500000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pagou.ai/v2/subscriptions/{id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"metadata\": {},\n \"retry_offsets_days\": [\n 15\n ],\n \"product_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"quantity\": 500000\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"requestId": "0190a2b4-18a7-7de0-9a43-69b7cf261201",
"data": {
"id": "string",
"customerId": "string",
"status": "incomplete",
"paymentMethod": "credit_card",
"billingModel": "merchant_initiated",
"billingDayOfMonth": 1,
"interval": "day",
"intervalCount": 1,
"amount": 0,
"currency": "BRL",
"trialEnd": "2026-07-14T12:00:00.000Z",
"currentPeriodStart": "2026-07-14T12:00:00.000Z",
"currentPeriodEnd": "2026-07-14T12:00:00.000Z",
"cancelAtPeriodEnd": false,
"canceledAt": "2026-07-14T12:00:00.000Z",
"failurePolicy": "immediate_cancel",
"retryOffsetsDays": [
1
],
"cancellationReason": "user_requested",
"customerEmail": "string",
"cardLast4": "string",
"metadata": {},
"informations": [
{
"key": "string",
"value": "string"
}
],
"products": [
{
"name": "string",
"price": 0,
"quantity": 1,
"tangible": false
}
],
"recurringAuthorization": {
"status": "pending"
},
"createdAt": "2026-07-14T12:00:00.000Z",
"updatedAt": "2026-07-14T12:00:00.000Z"
}
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"title": "<string>",
"status": 0,
"type": "<string>",
"detail": "<string>",
"instance": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"detail": "<string>",
"instance": "<string>"
}
