Add-ons
4 endpoints · base URL https://papaproxy.net/account_new · authentication
List available add-ons
/api/v1/billing/services/{service_id}/addons/availableAdd-ons you can buy for this service, with prices and your current wallet balance. Each add-on lists its price_cents and a params_schema that describes the params to send when you buy it. Add-ons that are already active are not listed. Works with any valid API key.
Parameters
| Field | Type | Description |
|---|---|---|
service_id (path) required | string (uuid) | ID of the service. |
Request example Try it in the Dashboard
curl -X GET "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/available" \ -H "Authorization: Bearer $PAPAPROXY_API_KEY"
import os
import requests
resp = requests.get(
"https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/available",
headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
timeout=30,
)
resp.raise_for_status()
print(resp.json())const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/available", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
},
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/available');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/available", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(data))
}Responses
200Available addons for a given service. · 8 fieldsExample response · JSON{ "service_id": "cb532178-5b03-506a-84ea-0230ff28576b", "addons": [ { "addon_type": "udp_unlock", "display_name": "UDP Unlock", "description": "Enable UDP traffic through your proxy service.", "price_cents": 4750, "params_schema": null }, { "addon_type": "extra_whitelist_ip", "display_name": "Extra Whitelist IPs", "description": "Add more IPs to your whitelist beyond the free quota.", "price_cents": 100, "params_schema": { "type": "object", "properties": { "ip_count": { "type": "integer", "minimum": 1, "maximum": 10000 } }, "required": [ "ip_count" ] } } ], "wallet_balance_cents": 1997 }Field Type Description service_idrequiredstring (uuid) ID of the service. addonsrequiredarray of object addons[].addon_typerequiredstring Add-on type: udp_unlock,extra_connections, orextra_whitelist_ip.addons[].display_namerequiredstring Name shown in the Dashboard. addons[].descriptionrequiredstring addons[].price_centsrequiredinteger Price in cents. addons[].params_schemaobject, nullable JSON Schema of the paramsto send when buying this add-on.wallet_balance_centsrequiredinteger Your wallet balance, in cents. 401The API key is missing, malformed, revoked, or expired, or the account is inactive.Example · JSON{ "detail": "Invalid or revoked API key." }404Service not found, or it doesn't belong to your account.Example · JSON{ "detail": "Service 3fa85f64-5717-4562-b3fc-2c963f66afa6 not found." }422Validation error: a field is missing or has the wrong type or value.Example · JSON{ "detail": "Validation error" }429Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.Example · JSON{ "detail": "Too Many Requests" }
List active add-ons
/api/v1/billing/services/{service_id}/addonsActive add-ons on this service: type, status, purchase price, parameters, and activation date. Canceled add-ons are not listed. Works with any valid API key.
Parameters
| Field | Type | Description |
|---|---|---|
service_id (path) required | string (uuid) | ID of the service. |
Request example Try it in the Dashboard
curl -X GET "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons" \ -H "Authorization: Bearer $PAPAPROXY_API_KEY"
import os
import requests
resp = requests.get(
"https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons",
headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
timeout=30,
)
resp.raise_for_status()
print(resp.json())const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
},
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(data))
}Responses
200Active addons for a given service. · 10 fieldsExample response · JSON{ "service_id": "cb532178-5b03-506a-84ea-0230ff28576b", "addons": [ { "addon_id": "fc9f2062-7726-5122-ab7f-d6a7814ed548", "service_id": "cb532178-5b03-506a-84ea-0230ff28576b", "addon_type": "extra_connections", "status": "active", "price_cents": 3, "nominal_price_cents": 3, "params": { "conn_count": 1 }, "activated_at": "2026-11-07T14:30:43.459177Z" } ] }Field Type Description service_idrequiredstring (uuid) ID of the service. addonsrequiredarray of object addons[].addon_idrequiredstring (uuid) ID of the add-on. addons[].service_idrequiredstring (uuid) ID of the service. addons[].addon_typerequiredstring Add-on type: udp_unlock,extra_connections, orextra_whitelist_ip.addons[].statusrequiredstring addons[].price_centsrequiredinteger Price in cents. addons[].nominal_price_centsinteger List price in cents, before discounts.Default: 0addons[].paramsobject, nullable addons[].activated_atrequiredstring (date-time) When it was activated (ISO 8601, UTC). 401The API key is missing, malformed, revoked, or expired, or the account is inactive.Example · JSON{ "detail": "Invalid or revoked API key." }404Service not found, or it doesn't belong to your account.Example · JSON{ "detail": "Service 3fa85f64-5717-4562-b3fc-2c963f66afa6 not found." }422Validation error: a field is missing or has the wrong type or value.Example · JSON{ "detail": "Validation error" }429Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.Example · JSON{ "detail": "Too Many Requests" }
Buy an add-on
/api/v1/billing/services/{service_id}/addonsservices:writeBuy an add-on and pay from your wallet. Get the add-on types, prices, and the params each one needs from GET …/addons/available.
extra_connections: sendparams.conn_count(1 to 10,000). The total is the add-onprice_cents×conn_count.extra_whitelist_ip: sendparams.ip_count, the number of extra whitelisted IPs.udp_unlock: no params. The total is the add-onprice_cents(50% of the base plan price).- Always send
expected_price_centswith the total you showed the user. Without it the request fails with409anddetail.error = "PRICE_CONFIRMATION_REQUIRED"; the response includesactual_price_cents. If it doesn't match the current price, the request fails with409anddetail.error = "PRICE_CHANGED". Nothing is charged in either case. - Insufficient balance returns
402withdetail.error = "insufficient_funds". Nothing is charged. - An unknown
addon_typeor a missing parameter returns400. - Idempotent on
idempotency_key.
Parameters
| Field | Type | Description |
|---|---|---|
service_id (path) required | string (uuid) | ID of the service. |
Request body
| Field | Type | Description |
|---|---|---|
addon_type required | string | Add-on type from GET …/addons/available: udp_unlock, extra_connections, or extra_whitelist_ip. |
idempotency_key required | string | Unique key for this purchase. A retry with the same key returns the original result without charging again. |
params | object, nullable | Add-on parameters, as described by params_schema in GET …/addons/available: {"conn_count": N} for extra_connections, {"ip_count": N} for extra_whitelist_ip, none for udp_unlock. |
expected_price_cents required | integer, nullable | The total you showed the user, in cents. Without it the request fails with 409 PRICE_CONFIRMATION_REQUIRED; if it doesn't match the current price, with 409 PRICE_CHANGED. Nothing is charged in either case.Minimum: 0 |
Request example Try it in the Dashboard
curl -X POST "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons" \
-H "Authorization: Bearer $PAPAPROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"addon_type": "extra_connections", "params": {"conn_count": 1}, "expected_price_cents": 3, "idempotency_key": "addon-2026-001"}'import os
import requests
resp = requests.post(
"https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons",
headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
json={
"addon_type": "extra_connections",
"params": {
"conn_count": 1
},
"expected_price_cents": 3,
"idempotency_key": "addon-2026-001"
},
timeout=30,
)
resp.raise_for_status()
print(resp.json())const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"addon_type": "extra_connections",
"params": {
"conn_count": 1
},
"expected_price_cents": 3,
"idempotency_key": "addon-2026-001"
}),
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'addon_type' => 'extra_connections',
'params' => [
'conn_count' => 1
],
'expected_price_cents' => 3,
'idempotency_key' => 'addon-2026-001'
]),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
req, err := http.NewRequest("POST", "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons", strings.NewReader(`{"addon_type": "extra_connections", "params": {"conn_count": 1}, "expected_price_cents": 3, "idempotency_key": "addon-2026-001"}`))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(data))
}Responses
201Result of addon purchase. · 7 fieldsExample response · JSON{ "addon_id": "fc9f2062-7726-5122-ab7f-d6a7814ed548", "service_id": "cb532178-5b03-506a-84ea-0230ff28576b", "addon_type": "extra_connections", "status": "active", "price_cents": 3, "nominal_price_cents": 3, "created_at": "2026-11-07T14:30:43.459177Z" }Field Type Description addon_idrequiredstring (uuid) ID of the add-on. service_idrequiredstring (uuid) ID of the service. addon_typerequiredstring Add-on type: udp_unlock,extra_connections, orextra_whitelist_ip.statusrequiredstring price_centsrequiredinteger Price in cents. nominal_price_centsinteger List price in cents, before discounts.Default: 0created_atrequiredstring (date-time) When it was created (ISO 8601, UTC). 400Unknownaddon_type, or a required parameter is missing.Example · unknown type · JSON{ "detail": "Unknown addon type: 'udp'" }Example · missing param · JSON{ "detail": "conn_count is required for extra_connections (integer 1..10000)." }401The API key is missing, malformed, revoked, or expired, or the account is inactive.Example · JSON{ "detail": "Invalid or revoked API key." }402Insufficient wallet balance. Nothing is charged.Example · JSON{ "detail": { "error": "insufficient_funds", "required_cents": 4750, "balance_cents": 1777 } }403The API key lacks scopeservices:write.Example · JSON{ "detail": "API key is missing required scope: services:write" }404Service not found, or it doesn't belong to your account.Example · JSON{ "detail": "Service 3fa85f64-5717-4562-b3fc-2c963f66afa6 not found." }409expected_price_centsis missing or doesn't match the current price. Nothing is charged.Example · price confirmation required · JSON{ "detail": { "error": "PRICE_CONFIRMATION_REQUIRED", "actual_price_cents": 3, "message": "expected_price_cents is required to confirm the purchase total (current 3)" } }Example · price changed · JSON{ "detail": { "error": "PRICE_CHANGED", "expected_price_cents": 1, "actual_price_cents": 3 } }422Validation error: a field is missing or has the wrong type or value.Example · JSON{ "detail": "Validation error" }429Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.Example · JSON{ "detail": "Too Many Requests" }
Cancel an add-on
/api/v1/billing/services/{service_id}/addons/{addon_id}services:writeCancel an active add-on. Unused time may be refunded to your wallet on a prorated basis. Idempotent: canceling an add-on that is already canceled returns success.
Parameters
| Field | Type | Description |
|---|---|---|
service_id (path) required | string (uuid) | ID of the service. |
addon_id (path) required | string (uuid) | ID of the add-on. |
Request example Try it in the Dashboard
curl -X DELETE "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/3fa85f64-5717-4562-b3fc-2c963f66afa6" \ -H "Authorization: Bearer $PAPAPROXY_API_KEY"
import os
import requests
resp = requests.delete(
"https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/3fa85f64-5717-4562-b3fc-2c963f66afa6",
headers={"Authorization": f"Bearer {os.environ['PAPAPROXY_API_KEY']}"},
timeout=30,
)
resp.raise_for_status()
print(resp.json())const resp = await fetch("https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/3fa85f64-5717-4562-b3fc-2c963f66afa6", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.PAPAPROXY_API_KEY}`,
},
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
console.log(await resp.json());<?php
$ch = curl_init('https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/3fa85f64-5717-4562-b3fc-2c963f66afa6');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('PAPAPROXY_API_KEY'),
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
var_dump($status, json_decode($response, true));package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("DELETE", "https://papaproxy.net/account_new/api/v1/billing/services/3fa85f64-5717-4562-b3fc-2c963f66afa6/addons/3fa85f64-5717-4562-b3fc-2c963f66afa6", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PAPAPROXY_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(data))
}Responses
200Result of disabling (cancelling) an addon. · 6 fieldsExample response · JSON{ "addon_id": "fc9f2062-7726-5122-ab7f-d6a7814ed548", "service_id": "cb532178-5b03-506a-84ea-0230ff28576b", "addon_type": "extra_connections", "status": "cancelled", "message": "Addon disabled. Prorated refund: 3 cents.", "refund_cents": 3 }Field Type Description addon_idrequiredstring (uuid) ID of the add-on. service_idrequiredstring (uuid) ID of the service. addon_typerequiredstring Add-on type: udp_unlock,extra_connections, orextra_whitelist_ip.statusrequiredstring messagerequiredstring refund_centsinteger Prorated refund credited to the wallet for the unused portion of the current billing period (0 when nothing was refunded).Default: 0401The API key is missing, malformed, revoked, or expired, or the account is inactive.Example · JSON{ "detail": "Invalid or revoked API key." }403The API key lacks scopeservices:write.Example · JSON{ "detail": "API key is missing required scope: services:write" }404Add-on not found, or it doesn't belong to your account. — error format422Validation error: a field is missing or has the wrong type or value.Example · JSON{ "detail": "Validation error" }429Too many requests: the per-account limit is 60 requests per minute, shared by all keys and the MCP server. Retry with exponential backoff.Example · JSON{ "detail": "Too Many Requests" }
Last updated: November 9, 2026. Generated from the OpenAPI 3.1 specification (API version 0.1.0). Markdown version for AI agents.