Buy an add-on, or move between tiers of one.
const url = 'https://api.siteqwality.com/billing/addons';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"pack_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","billing_interval":"example","idempotency_key":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.siteqwality.com/billing/addons \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "pack_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "billing_interval": "example", "idempotency_key": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" }'Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”object
"month" or "year".
Client-generated, stable across retries of ONE confirm attempt.
This is what makes a double-click safe: the same value twice is one
Stripe mutation. A client that omits it gets no protection, which is
why it is required rather than optional here (unlike change-plan,
which has to keep accepting bodies from already-cached bundles).
Example generated
{ "pack_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "billing_interval": "example", "idempotency_key": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}Responses
Section titled “ Responses ”Add-on purchased or changed
object
Retained for symmetry with change-plan, whose older clients read it.
"updated" when the change is live, "requires_payment" when the
customer must confirm a payment before it takes effect.
Example generated
{ "success": true, "status": "example", "client_secret": "example", "stripe_subscription_id": "example"}Invalid interval, unknown pack, pack is not an add-on, or already on that tier
Missing or invalid authentication
Caller is not a super admin
Account has no Stripe customer
Internal server error