curl --request POST \
--url https://my.server.express/api/v1/client/cloud/buy \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"planId": "<string>",
"hostname": "<string>",
"hostnames": [
"<string>"
],
"quantity": 5,
"location": "<string>",
"nodeGroupId": "<string>",
"os": "<string>",
"app": "<string>",
"appInputs": {},
"billingCycle": "monthly",
"addonIds": [
"<string>"
],
"couponCode": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
planId: '<string>',
hostname: '<string>',
hostnames: ['<string>'],
quantity: 5,
location: '<string>',
nodeGroupId: '<string>',
os: '<string>',
app: '<string>',
appInputs: {},
billingCycle: 'monthly',
addonIds: ['<string>'],
couponCode: '<string>'
})
};
fetch('https://my.server.express/api/v1/client/cloud/buy', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://my.server.express/api/v1/client/cloud/buy"
payload = {
"planId": "<string>",
"hostname": "<string>",
"hostnames": ["<string>"],
"quantity": 5,
"location": "<string>",
"nodeGroupId": "<string>",
"os": "<string>",
"app": "<string>",
"appInputs": {},
"billingCycle": "monthly",
"addonIds": ["<string>"],
"couponCode": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"serviceId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoiceId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoiceNumber": "<string>",
"total": 123,
"currency": "<string>",
"serviceIds": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
]
}
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>",
"data": {
"billingDetailsRequired": true,
"missing": [
"addressLine1"
]
}
}{
"success": false,
"error": "<string>"
}Order one or more cloud servers
Reserves up to 10 VDS on one plan: a PENDING service per server and a single UNPAID invoice covering the lot. Nothing is
cloned on Proxmox until that invoice settles. Prices, term discounts and add-ons are re-derived server-side from ids; the
caller never sends an amount. Requires a complete billing address (428 otherwise) and refuses sanctioned countries (403).
When quantity exceeds the number of hostnames, extra names are generated from the first one (web.example.com →
web-2.example.com). Hostnames must be unique, letters/digits/hyphens, up to 63 characters per label and 253 overall.
curl --request POST \
--url https://my.server.express/api/v1/client/cloud/buy \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"planId": "<string>",
"hostname": "<string>",
"hostnames": [
"<string>"
],
"quantity": 5,
"location": "<string>",
"nodeGroupId": "<string>",
"os": "<string>",
"app": "<string>",
"appInputs": {},
"billingCycle": "monthly",
"addonIds": [
"<string>"
],
"couponCode": "<string>"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
planId: '<string>',
hostname: '<string>',
hostnames: ['<string>'],
quantity: 5,
location: '<string>',
nodeGroupId: '<string>',
os: '<string>',
app: '<string>',
appInputs: {},
billingCycle: 'monthly',
addonIds: ['<string>'],
couponCode: '<string>'
})
};
fetch('https://my.server.express/api/v1/client/cloud/buy', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://my.server.express/api/v1/client/cloud/buy"
payload = {
"planId": "<string>",
"hostname": "<string>",
"hostnames": ["<string>"],
"quantity": 5,
"location": "<string>",
"nodeGroupId": "<string>",
"os": "<string>",
"app": "<string>",
"appInputs": {},
"billingCycle": "monthly",
"addonIds": ["<string>"],
"couponCode": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"data": {
"serviceId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoiceId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"invoiceNumber": "<string>",
"total": 123,
"currency": "<string>",
"serviceIds": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
]
}
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>"
}{
"success": false,
"error": "<string>",
"data": {
"billingDetailsRequired": true,
"missing": [
"addressLine1"
]
}
}{
"success": false,
"error": "<string>"
}Authorizations
Session access token (JWT) from POST /v1/auth/login or /v1/auth/refresh. A session satisfies every API scope.
Body
Cloud plan slug (the id from GET /v1/client/cloud/marketplace).
Hostname for a single server. Required unless hostnames is given.
One hostname per server. Wins over hostname when non-empty.
Number of servers. Defaults to the number of hostnames, or 1.
1 <= x <= 10CloudLocation slug. Uncertain — when omitted, the order service picks a default location.
Pin the order to one group of hardware within the location.
OS template slug. An unknown slug is rejected.
One-click app slug. Needs a Docker-capable template to be configured.
Values for the app's env schema, validated against it.
Term to buy on. Longer terms are discounted (5%–30%). Any other value is rejected.
monthly, quarterly, semi-annually, annually, biennially, triennially AddonProduct ids billed alongside every server. Each must be sold in the customer's billing currency.
Optional discount code, validated server-side.