Update Charge Amount
POST/subscriptions/:id/update-charge-amountDescription
Change the recurring charge_amount on an active subscription. The Exodus API submits the on-chain SubscriptionManager.updateChargeAmount() transaction and pays gas.
The new amount takes effect immediately for the next cycle charge. The customer is not re-prompted to sign — the original subscribe signature authorized the contract for any amount up to cap_amount, and the merchant’s signature on this action proves the price change was authorized.
new_amount must be <= cap_amount. The contract reverts with ChargeAmountExceedsCap
otherwise — there is no way to raise the cap without the customer re-subscribing. Set a generous
cap at intent creation if you anticipate price increases.
Headers
| Header | Description | Required |
|---|---|---|
| Authorization | Bearer token with your API key | yes |
| Content-Type | application/json | yes |
| X-Signature | Signature from `signUpdateChargeAmount(...)` | yes |
Path Parameters
| Name | Type | Description | Required |
|---|---|---|---|
| id | string | The subscription ID (e.g. `sub_abc123def456`). | yes |
Body Parameters
| Name | Type | Description | Required |
|---|---|---|---|
| new_amount | string | New `charge_amount` in token's smallest unit. MUST equal the value passed to `signUpdateChargeAmount`. MUST be `> 0` and `<= cap_amount`. | yes |
| charge_amount_update_nonce | number | Pre-increment `charge_amount_update_nonce` from the latest `GET /subscriptions/:id` read. MUST equal the value passed to `signUpdateChargeAmount`. | yes |
Producing the Signature
import { signUpdateChargeAmount } from '@exodus/checkout-signer'
const sub = await fetch('https://checkout.exodus.com/subscriptions/sub_abc123def456', {
headers: { Authorization: `Bearer ${process.env.API_KEY}` },
}).then((r) => r.json())
const newAmount = '12990000' // $12.99 USDC (raised from $9.99)
const signature = signUpdateChargeAmount(
sub.onchain_id,
newAmount,
sub.charge_amount_update_nonce,
sub.subscription_manager_address,
sub.chain,
process.env.SIGNING_PRIVATE_KEY,
)
const response = await fetch(
`https://checkout.exodus.com/subscriptions/${sub.id}/update-charge-amount`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
'Content-Type': 'application/json',
'X-Signature': signature,
},
body: JSON.stringify({
new_amount: newAmount,
charge_amount_update_nonce: sub.charge_amount_update_nonce,
}),
},
)
const updatedSub = await response.json()
console.log(updatedSub.charge_amount) // "12990000"Response
{
"object": "subscription",
"id": "sub_abc123def456",
"status": "active",
"onchain_id": "0x9f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a",
"subscriber": "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE21",
"chain": "eip155:1",
"subscription_manager_address": "0xA1B2C3D4E5F6789012345678901234567890ABCD",
"token_symbol": "USDC",
"charge_amount": "12990000",
"cap_amount": "120000000",
"period_duration": 2592000,
"charge_nonce": 3,
"charge_amount_update_nonce": 1,
"cancel_at_period_end": false,
"last_charged_at": "2026-04-19T12:02:18Z",
"next_charge_at": "2026-05-19T12:02:18Z",
"metadata": { "external_plan_ref": "pro_monthly" },
"created_at": "2026-02-19T12:02:18Z"
}Errors
| Status | Code | Description |
|---|---|---|
| 400 | subscription_cancelled | Subscription is cancelled or cancelling. |
| 400 | charge_amount_exceeds_cap | new_amount > cap_amount. |
| 400 | invalid_signature | Signature does not recover to your registered signing address. |
| 400 | nonce_mismatch | charge_amount_update_nonce is stale. Re-fetch and re-sign. |
| 404 | not_found | Subscription ID does not exist. |
| 403 | forbidden | Subscription belongs to another merchant. |
After the Update
The new amount is reflected on the next GET /subscriptions/:id read. Subsequent calls to POST /subscriptions/:id/charge MUST pass the new charge_amount. The on-chain contract enforces the new value as well as the original cap.
