Vitamin API reference

Sell and manage VPN accounts and virtual servers from your own site, bot or script.

Base URLhttps://apiservice.vitamindata.net/api/v1

Keys are issued in your panel. If you do not see the section, ask support to switch API access on for your account.

1

Create an API key in your panel and send it as a bearer token on every call.

2

Call Ping to prove the key, then pull your prices with ListPlans and GetVPSStorefront.

3

Sell with FUNDING_AUTO: your balance pays when it covers, your customer gets a payment link when it does not.

Authentication

Every request carries your key as a bearer token. There are no cookies on this host and no CSRF token to fetch — a key is the whole credential.

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are created in your panel under API. The secret is shown once, at creation: we store only a hash of it, so if it is lost the fix is to rotate the key — which issues a new secret and keeps the old one working for a day, so you can deploy without downtime.

A key can be limited to a list of source addresses, given its own per-minute rate limit, and have individual operations withheld from it. Those are set by our operators — ask support if you need a key that, say, may buy but may never delete a service.

Test keys

A key beginning sk_test_ reads real data but refuses anything that would spend money or touch a real machine, so you can wire up and verify your integration before it can cost anything.

What a failure looks like

An unknown key and a key whose account has no API access answer the same 401, word for word: the answer never confirms whether a key exists. A key of yours that outlived its rotation overlap is the exception — it says api key expired, because you already know that key was real and you need to know why it stopped. Repeated failures from one address are throttled — see rate limits.

Paying for things

Everything you buy — a VPN account, an extension, a server, a renewal, an upgrade — goes through one contract. You choose how it is funded:

fundingWhat happens
FUNDING_AUTORecommended. Takes it from your balance if the balance covers it; otherwise the call still succeeds and hands you a payment link. One call, no error to interpret.
FUNDING_BALANCEBalance only. If the balance is short the call FAILS with insufficient_balance (the error still carries a payable link).
FUNDING_INVOICEAlways returns a payment link, even if your balance would have covered it.

With FUNDING_AUTO you branch on one field:

{"order": {...}, "paid": true,  "status": "completed"}
{"order": {...}, "status": "payment_required",
 "invoice": {"invoice_id": "inv_9m2r4t",
             "pay_url": "https://pay.example.com/i/inv_9m2r4t",
             "price_usd": "11.90", "expires_at": "1785499200"}}

completed means the money is taken and delivery has started — poll GetOrder until its status is delivered. payment_required means send your customer to pay_url; delivery starts by itself when the invoice is paid.

Prices are always ours. You may send expected_total_usd as a guard: if our fresh quote differs, the sale is refused with price_changed and the new total, rather than charging a number your customer never saw.

Retrying safely

Send an Idempotency-Key header on every call that spends money. If a response never reaches you — a timeout, a dropped connection, a redeployed worker — retry the SAME request with the SAME key and you get the original outcome back instead of a second charge.

Idempotency-Key: 8f14e45f-ea6d-4b3a-9c1b-2f0d5a7e91c3

Use a fresh key per customer action (a UUID is ideal). Re-using a key with a different body is refused with idempotency_conflict — that combination means a bug, and guessing which request you meant would be worse than telling you.

Rate limits

Every authenticated response tells you where your key stands:

RateLimit-Limit: 6000
RateLimit-Remaining: 5987
RateLimit-Policy: 6000;w=60

The limit is per key, per minute, and it is set for your account — the default suits a storefront; a busy bot should ask for more rather than pace itself at the default. Exceeding it answers 429 with Retry-After.

A 429 also appears if a lot of requests from one address fail to authenticate. That is brute-force protection, not your quota: it clears by itself (the header says when), and a working key never triggers it.

You will also see an X-Ratelimit-* family on every response, including ones with no key. That is a coarse per-ADDRESS brake in front of the whole API, not your key's budget — pace against RateLimit-Remaining, and treat the X- headers as someone else's business.

Errors

Failures come back as an HTTP status and a JSON body. The top-level code is the transport category; the top-level message carries the stable machine code. The full detail — the same machine code, a safe sentence, and any actionable extras — rides in details under debug:

{"code": "failed_precondition",
 "message": "insufficient_balance",
 "details": [{"type": "neopay.seller.publicapi.v1.ErrorInfo",
              "value": "CgptZXNzYWdl…",
              "debug": {"code": "insufficient_balance",
                        "message": "insufficient balance",
                        "payUrl": "https://pay.example.com/i/inv_9m2r4t",
                        "orderId": "ord_8c3d1e",
                        "totalUsd": "11.90"}}]}

Two quirks of the error channel, both worth coding for: value is the base64 protobuf (ignore it — read debug), and inside debug the extra fields are lowerCamelCase — payUrl, orderId, totalUsd, retryAfterSeconds — a Connect convention, unlike the snake_case everywhere else. Branch on the top-level message or on debug.code; never on the sentence.

Refusals before your request is authorized

A request that never reaches a handler — no key or a bad one (401), a source address the key does not allow (403), a rate limit (429), an authentication outage (503) — is refused by the front door, and the front door answers a SHORTER body: a transport code and a plain sentence, with no details and no machine code.

{"code": "unauthenticated", "message": "invalid api key"}

So branch on the HTTP STATUS for these (and honour Retry-After on a 429); the machine codes below belong to calls that got as far as a handler. The coarse per-address brake is older still and answers {"error":"rate_limited"} — keyed error, not code.

CodeMeansWhat to do
forbidden_scopeThe key lacks the scope for this call, or this operation has been withheld from it.Ask support to widen the key.
not_foundNo such object for this account.Check the id. A foreign id also answers this — by design.
invalid_requestA field is malformed.Fix and resend; retrying unchanged will not help.
insufficient_balanceBalance-only funding could not cover it.Use the payUrl in the detail, or switch to FUNDING_AUTO.
price_changedYour expected_total_usd no longer matches.Re-quote and confirm the new total with your customer.
idempotency_conflictThat key was used with a different body.Use a fresh key per action.
sandbox_unavailableA sk_test_ key tried to spend money or touch a real machine.Expected — use a live key for that call. Test keys read, they never buy.
unpriceableThe configuration you asked for is not sellable — an unpriced plan or node, or a cart that no model covers.Re-read the storefront and quote before you buy; a quote refuses the same cart.
upstream_unavailableA plane we depend on is down.Retry with backoff; reads may be briefly stale.
order_failedDelivery failed after payment.Do not re-buy. Poll GetOrder; we recover these automatically and support can see it.
internalSomething broke on our side that we did not classify.Retry once with backoff; if it repeats, quote the X-Request-Id to support.

Conventions

  • Transport. Every call is POST {base}/{Method} with a JSON body — POST /api/v1/CreateVPNOrder. Nothing else exists: any other HTTP method answers 405, there is no REST verb mapping and there are no path parameters.
  • Money is a string. "19.99", never 19.99: a JSON number is a float in most languages and cannot hold a cent exactly.
  • 64-bit integers arrive as strings. Every byte count and every unix timestamp is 64-bit, and the wire quotes them — "remaining_bytes": "96636764160". Parse them as integers; on requests you may send either form.
  • Zero is omitted. A field whose value is 0, false or empty does not appear in a response at all. Treat absent as zero — and remember a 0 daily limit means NO limit, not "0 GB".
  • Times are unix seconds, except the transaction window, which takes and returns RFC3339 or YYYY-MM-DD.
  • Traffic is bytes, never gigabytes, on every figure.
  • Ids are opaque. acc_…, ord_…, vpn_…. Never parse or generate one, and never assume they are sequential.
  • Paging is by opaque cursor: pass the previous response's next_cursor back. An empty next_cursor means you have everything.
  • The field tables use short type tokens. money — an exact decimal string; unix — unix seconds (64-bit, so a string); bytes — a byte count (64-bit, so a string); int64 — any other 64-bit (a string); int — 32-bit, a plain number; cursor — an opaque paging handle; enum — exactly one of the listed values.
  • New fields appear without warning and existing ones do not change meaning. Ignore what you do not recognise.

Getting started

The one call to make first: it proves the key works and tells you which account it belongs to.

POST

Ping

any key
https://apiservice.vitamindata.net/api/v1/Ping

Answers your account id, whether the key is live or test, and our clock.

Request

No parameters — send an empty object, {}.

Example request
curl https://apiservice.vitamindata.net/api/v1/Ping \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'
Response200 · application/json
account_idstring

The key's account — proof the key resolved.

modeenum

Which kind of key you sent.

One of:livetest
server_timeunix

Our clock — useful for spotting drift before it breaks signatures or windows.

Sample response
{
  "account_id": "acc_9f3k2m7q",
  "mode": "live",
  "server_time": "1785412800"
}

Account and balance

POST

GetAccount

read
https://apiservice.vitamindata.net/api/v1/GetAccount

Your account's email, status, locale and whether the address is verified.

Request

No parameters — send an empty object, {}.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetAccount \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'
Response200 · application/json
accountobject

Your account.

account.account_idstring

Public id (acc_…).

account.emailstring

The login address.

account.statusstring

active unless the account is restricted.

account.localestring

The account's language — card names and notifications localize to it.

account.email_verifiedbool

The address has been confirmed.

account.created_atunix

When the account was created.

Sample response
{
  "account": {
    "account_id": "acc_9f3k2m7q",
    "email": "dev@example.com",
    "status": "active",
    "locale": "en",
    "email_verified": true,
    "created_at": "1769000000"
  }
}
POST

GetBalance

read
https://apiservice.vitamindata.net/api/v1/GetBalance

Your ledger balance and what a purchase can actually draw on.

available_usd is the number to check before buying — it excludes anything already locked by an in-flight order.

Request

No parameters — send an empty object, {}.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetBalance \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'
Response200 · application/json
balance_usdmoney

The full ledger balance.

available_usdmoney

Balance minus locks — what a purchase can spend right now.

as_ofstring

The ledger timestamp of the figures, RFC3339.

Sample response
{
  "balance_usd": "72.60",
  "available_usd": "60.70",
  "as_of": "2026-07-29T12:00:00Z"
}
POST

ListTransactions

read
https://apiservice.vitamindata.net/api/v1/ListTransactions

Your ledger, newest first.

Defaults to the last three months. A window wider than 92 days is narrowed, and the response tells you the window it actually used — so a short answer is never ambiguous.

Request
limitintoptional

Page size. Default 25, max 100.

cursorcursoroptional

The previous response's next_cursor. Omit it for the first page.

kindstringoptional

Optional comma-separated filter over the kind values below.

fromstringoptional

Window start, RFC3339 or YYYY-MM-DD.

tostringoptional

Window end. A date-only to includes that whole day.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListTransactions \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"limit":25,"cursor":"","kind":"topup_credit,invoice_debit_reserved","from":"2026-05-01","to":"2026-07-30"}'
Response200 · application/json
transactionsarray

The ledger rows.

transactions[].idstring

The ledger row's id.

transactions[].tsstring

When, RFC3339.

transactions[].kindenum

What moved the money. topup_credit is a paid top-up; the two invoice_debit_* kinds are purchases. An in-flight order's internal hold and release are not part of this feed.

One of:topup_creditexternal_creditinvoice_debit_reservedinvoice_debit_externaloverpay_creditmanual_creditmanual_debitspend_debitspend_refundautomated_correction
transactions[].amount_usdmoney

Signed — negative on debits.

transactions[].balance_after_usdmoney

The running balance after this row.

transactions[].ref_invoice_idstring

The invoice behind the movement — feed it to GetInvoice for the full story.

transactions[].descriptionstring

A short human label. Display-only; never branch on it.

next_cursorcursor

Pass it back as cursor for the next page. Absent/empty = you have everything.

has_morebool

More rows exist beyond this page.

fromstring

The window ACTUALLY applied (RFC3339). Narrower than you asked ⇒ you hit the 92-day cap.

tostring

The applied window's end.

Sample response
{
  "transactions": [
    {"id": "tx_01j9zq", "ts": "2026-07-28T09:14:03Z", "kind": "invoice_debit_reserved",
     "amount_usd": "-11.90", "balance_after_usd": "72.60",
     "ref_invoice_id": "inv_5k8p2q", "description": "order ord_7b2c9d"},
    {"id": "tx_01j8xw", "ts": "2026-07-25T18:40:11Z", "kind": "topup_credit",
     "amount_usd": "50.00", "balance_after_usd": "84.50", "ref_invoice_id": "inv_3d1x8n"}
  ],
  "from": "2026-04-30T00:00:00Z",
  "to": "2026-07-29T23:59:59Z"
}
POST

CreateTopup

buyIdempotency-Key
https://apiservice.vitamindata.net/api/v1/CreateTopup

Creates a payable invoice that adds funds to your balance.

Send your customer (or yourself) to invoice.pay_url. The balance moves when it is paid.

Request
amount_usdmoneyrequired

How much to add, as a decimal string.

Example request
curl https://apiservice.vitamindata.net/api/v1/CreateTopup \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"amount_usd":"50.00"}'
Response200 · application/json
invoiceobject

The payable invoice.

invoice.invoice_idstring

The gateway's invoice id (inv_…).

invoice.statusenum

pending is payable. Everything from confirmed through delivered_redirected means the money is irreversibly in — treat all of those as PAID. expired means the link lapsed unpaid.

One of:pendingconfirmedsweepingsweptdeliveringdelivereddelivered_redirectedexpired
invoice.pay_urlstring

The hosted payment page. Send your customer here; every rail (coins, chains, balance) lives behind it.

invoice.pay_telegram_urlstring

The same invoice, payable inside Telegram: it opens the payment provider's own bot, which shows the amount and takes the payment there. Offer it beside pay_url for customers who would rather not leave the app. May be absent — only a payment provider with a bot configured has one, so never make it your only pay button.

invoice.price_usdmoney

What the invoice collects.

invoice.expires_atunix

When the payment window closes.

Sample response
{
  "invoice": {
    "invoice_id": "inv_3d1x8n",
    "status": "pending",
    "pay_url": "https://pay.example.com/i/inv_3d1x8n",
    "pay_telegram_url": "https://t.me/VitaminPayBot?start=3d1x8n",
    "price_usd": "50.00",
    "expires_at": "1785499200"
  }
}
POST

GetInvoice

read
https://apiservice.vitamindata.net/api/v1/GetInvoice

One payment in full detail, including what the chain shows.

A ledger row's ref_invoice_id is the join key. Only invoices your own account created can be read.

Request
invoice_idstringrequired

From a ledger row's ref_invoice_id, an order, or a top-up.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetInvoice \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"invoice_id":"inv_5k8p2q"}'
Response200 · application/json
invoiceobject

The invoice itself.

invoice.invoice_idstring

The gateway's invoice id (inv_…).

invoice.statusenum

pending is payable. Everything from confirmed through delivered_redirected means the money is irreversibly in — treat all of those as PAID. expired means the link lapsed unpaid.

One of:pendingconfirmedsweepingsweptdeliveringdelivereddelivered_redirectedexpired
invoice.pay_urlstring

The hosted payment page. Send your customer here; every rail (coins, chains, balance) lives behind it.

invoice.pay_telegram_urlstring

The same invoice, payable inside Telegram: it opens the payment provider's own bot, which shows the amount and takes the payment there. Offer it beside pay_url for customers who would rather not leave the app. May be absent — only a payment provider with a bot configured has one, so never make it your only pay button.

invoice.price_usdmoney

What the invoice collects.

invoice.expires_atunix

When the payment window closes.

paymentsarray

What the chain shows. Empty for a balance-funded invoice (no chain was involved) and for one nobody has paid yet — an empty list is not an error.

payments[].chainstring

The network the payment arrived on (tron, bsc, …).

payments[].assetstring

What was paid (USDT, …).

payments[].amountmoney

The asset amount, exact.

payments[].tx_hashstring

The on-chain transaction — your customer's proof of payment.

payments[].confirmedbool

The chain has finalised it.

payments[].seen_atunix

When we first observed it.

order_idstring

What the invoice bought. Absent for a top-up, which buys nothing.

Sample response
{
  "invoice": {
    "invoice_id": "inv_5k8p2q",
    "status": "confirmed",
    "pay_url": "https://pay.example.com/i/inv_5k8p2q",
    "price_usd": "11.90",
    "expires_at": "1785499200"
  },
  "payments": [
    {"chain": "tron", "asset": "USDT", "amount": "11.90",
     "tx_hash": "c4a1f09e2b7d", "confirmed": true, "seen_at": "1785412920"}
  ],
  "order_id": "ord_7b2c9d"
}

Prices

Nothing here creates anything — quote as often as you like before your customer commits.

POST

ListPlans

read
https://apiservice.vitamindata.net/api/v1/ListPlans

What you may sell: the slider's bounds and, if your storefront uses them, the fixed-price cards.

Render whichever is populated. A card is bought by putting its bundle_id in the cart; a slider cart uses gb/months/users.

Request

No parameters — send an empty object, {}.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListPlans \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'
Response200 · application/json
model_namestring

Your storefront's pricing model.

model_typestring

Its engine version.

boundsobject

The configurator's rules — every quote and order re-checks them server-side.

bounds.gb_minint

Smallest traffic a cart may ask for.

bounds.gb_maxint

Largest.

bounds.gb_stepint

The slider's step.

bounds.users_minint

Fewest concurrent devices.

bounds.users_maxint

Most.

bounds.months_minint

Shortest term.

bounds.months_maxint

Longest.

bounds.new_accounts_maxint

Most accounts one order may mint.

bounds.extend_maxint

Most accounts one order may extend.

bounds.default_gbint

A sensible slider default.

bounds.default_usersint

Default devices.

bounds.default_monthsint

Default term.

bundlesarray

The fixed-price cards, when your storefront is a card catalogue.

bundles[].bundle_idstring

The handle to put in cart.bundle_id.

bundles[].namestring

The card's name, already localized for the account's locale.

bundles[].gbint

Traffic included.

bundles[].monthsint

Validity.

bundles[].online_usersint

Concurrent devices.

bundles[].price_usdmoney

The price. It IS the price — no quote needed for a card.

bundles[].highlightbool

The storefront's featured card.

bundles[].daily_cap_gbint

Daily cap in GB. Absent = none.

Sample response
{
  "model_name": "vpn_dynamic",
  "model_type": "dynamic_v2",
  "bounds": {
    "gb_min": 10, "gb_max": 500, "gb_step": 10,
    "users_min": 1, "users_max": 10,
    "months_min": 1, "months_max": 12,
    "new_accounts_max": 5, "extend_max": 10,
    "default_gb": 100, "default_users": 3, "default_months": 1
  },
  "bundles": [
    {"bundle_id": "card_100_1m", "name": "100 GB · 1 month", "gb": 100, "months": 1,
     "online_users": 3, "price_usd": "11.90", "highlight": true}
  ]
}
POST

Quote

read
https://apiservice.vitamindata.net/api/v1/Quote

The final price of a VPN cart, itemised. Discounts are already applied.

Request
cartobjectrequired

The cart to price.

cart.kindenumrequired

Buy new accounts, or add traffic and time to existing ones.

One of:newextend
cart.gbintoptional

Traffic per account, in GB, within the bounds from ListPlans.

cart.monthsintoptional

Validity per account, in months.

cart.usersintoptional

Concurrent devices per account.

cart.new_accountsintoptional

kind:"new" only: how many accounts to mint. Usernames are server-minted.

cart.extend_vpn_idsarrayoptional

kind:"extend" only: the accounts to extend, by public id.

cart.bundle_idstringoptional

Buys a fixed-price card from ListPlans.bundles instead of a configured cart. When set, gb/months/users are ignored — the card's own values and price apply.

Example request
curl https://apiservice.vitamindata.net/api/v1/Quote \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"cart":{"kind":"new","gb":100,"months":1,"users":3,"new_accounts":2}}'
Other shapes of this request
Extend accounts you already hold
{"cart":{"kind":"extend","gb":50,"months":1,"users":3,"extend_vpn_ids":["vpn_6t2k9p","vpn_1a4b7c"]}}
Buy a fixed-price card instead of a configured cart
{"cart":{"kind":"new","bundle_id":"card_100_1m","new_accounts":1}}
Response200 · application/json
quoteobject

The itemised price. Nothing is created and nothing is charged.

quote.model_namestring

Which pricing model answered.

quote.model_typestring

The model's engine version.

quote.base_usdmoney

The price before discounts and surcharges.

quote.total_usdmoney

The final price — what a sale with this cart charges.

quote.linesarray

The itemisation, signed, summing exactly to the total.

quote.lines[].codestring

What this row is (base, a discount code, a surcharge…).

quote.lines[].kindstring

The row's category, for grouping in your UI.

quote.lines[].amount_usdmoney

Signed. Discounts are negative; the rows sum exactly to total_usd.

quote.lines[].pctstring

The percentage behind the row, when there is one.

quote.cappedbool

The total hit the model's ceiling.

quote.floor_appliedbool

The total was raised to the model's floor.

quote.clampedenum

Set when a cart value was clamped into bounds before pricing.

One of:minmax
quote.invalidstring

Non-empty means the cart cannot be priced — show it and NEVER charge such a quote.

quote.metamap

Model extras, string→string (e.g. an upgrade's remaining_days).

Sample response
{
  "quote": {
    "model_name": "vpn_dynamic",
    "model_type": "dynamic_v2",
    "base_usd": "14.00",
    "total_usd": "11.90",
    "lines": [
      {"code": "base", "kind": "base", "amount_usd": "14.00"},
      {"code": "loyalty", "kind": "discount", "amount_usd": "-2.10", "pct": "15"}
    ]
  }
}

Selling VPN

POST

CreateVPNOrder

buyIdempotency-Key
https://apiservice.vitamindata.net/api/v1/CreateVPNOrder

Buys new VPN accounts, or extends existing ones ("kind":"extend" with extend_vpn_ids).

See Paying for things for the two shapes the response can take.

Request
cartobjectrequired

What to buy — the same shape Quote prices.

cart.kindenumrequired

Buy new accounts, or add traffic and time to existing ones.

One of:newextend
cart.gbintoptional

Traffic per account, in GB, within the bounds from ListPlans.

cart.monthsintoptional

Validity per account, in months.

cart.usersintoptional

Concurrent devices per account.

cart.new_accountsintoptional

kind:"new" only: how many accounts to mint. Usernames are server-minted.

cart.extend_vpn_idsarrayoptional

kind:"extend" only: the accounts to extend, by public id.

cart.bundle_idstringoptional

Buys a fixed-price card from ListPlans.bundles instead of a configured cart. When set, gb/months/users are ignored — the card's own values and price apply.

fundingenumrequired

How to pay — see Paying for things. There is no default: omitting it is refused.

One of:FUNDING_AUTOFUNDING_BALANCEFUNDING_INVOICE
expected_total_usdmoneyoptional

Confirmation guard. If set and our fresh quote differs, the sale is refused with price_changed instead of charging a number your customer never saw.

Example request
curl https://apiservice.vitamindata.net/api/v1/CreateVPNOrder \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"cart":{"kind":"new","gb":100,"months":1,"users":3,"new_accounts":1},"funding":"FUNDING_AUTO","expected_total_usd":"11.90"}'
Other shapes of this request
Extend accounts you already hold
{"cart":{"kind":"extend","gb":50,"months":1,"users":3,"extend_vpn_ids":["vpn_6t2k9p"]},"funding":"FUNDING_AUTO"}
Buy a fixed-price card, paid from balance
{"cart":{"kind":"new","bundle_id":"card_100_1m","new_accounts":1},"funding":"FUNDING_BALANCE"}
Response200 · application/json
orderobject

The order — poll GetOrder until its status is delivered.

order.order_idstring

The order's public id (ord_…).

order.productenum

What was bought.

One of:vpnvps
order.kindstring

The purchase shape: new, extend, upgrade

order.statusenum

The lifecycle. delivered is the goal; needs_operator means the money is in and a human is finishing delivery — do not re-buy; expired means the invoice lapsed unpaid.

One of:createdinvoicedpaiddeliveringdeliveredneeds_operatorfailedrefund_pendingrefundedexpired
order.fundingenum

Which rail actually funded it. FUNDING_AUTO resolves to one of these.

One of:invoicebalance
order.total_usdmoney

What the order charges.

order.invoice_idstring

The invoice behind it — every order has one, whichever rail funded it.

order.itemsint

How many entitlements the order mints or extends.

order.created_atunix

When it was placed.

order.delivered_atunix

When delivery finished. Absent until then.

order.breakdownobject

The quote the customer agreed to, verbatim — the same shape Quote returns.

invoiceobject

The invoice behind the order: payable when payment_required, already settled when completed.

invoice.invoice_idstring

The gateway's invoice id (inv_…).

invoice.statusenum

pending is payable. Everything from confirmed through delivered_redirected means the money is irreversibly in — treat all of those as PAID. expired means the link lapsed unpaid.

One of:pendingconfirmedsweepingsweptdeliveringdelivereddelivered_redirectedexpired
invoice.pay_urlstring

The hosted payment page. Send your customer here; every rail (coins, chains, balance) lives behind it.

invoice.pay_telegram_urlstring

The same invoice, payable inside Telegram: it opens the payment provider's own bot, which shows the amount and takes the payment there. Offer it beside pay_url for customers who would rather not leave the app. May be absent — only a payment provider with a bot configured has one, so never make it your only pay button.

invoice.price_usdmoney

What the invoice collects.

invoice.expires_atunix

When the payment window closes.

paidbool

The money is captured. Absent (= false) with payment_required.

statusenum

THE field to branch on — see Paying for things.

One of:completedpayment_required
Sample response
{
  "order": {
    "order_id": "ord_8c3d1e", "product": "vpn", "kind": "new",
    "status": "invoiced", "funding": "invoice", "total_usd": "11.90",
    "invoice_id": "inv_9m2r4t", "items": 1, "created_at": "1785412800"
  },
  "invoice": {
    "invoice_id": "inv_9m2r4t", "status": "pending",
    "pay_url": "https://pay.example.com/i/inv_9m2r4t",
    "pay_telegram_url": "https://t.me/VitaminPayBot?start=9m2r4t",
    "price_usd": "11.90", "expires_at": "1785499200"
  },
  "status": "payment_required"
}
POST

GetOrder

read
https://apiservice.vitamindata.net/api/v1/GetOrder

Poll this after buying: created → invoiced → paid → delivering → delivered.

Request
order_idstringrequired

From the order you placed.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetOrder \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"order_id":"ord_7b2c9d"}'
Response200 · application/json
orderobject

The order, with its full breakdown.

order.order_idstring

The order's public id (ord_…).

order.productenum

What was bought.

One of:vpnvps
order.kindstring

The purchase shape: new, extend, upgrade

order.statusenum

The lifecycle. delivered is the goal; needs_operator means the money is in and a human is finishing delivery — do not re-buy; expired means the invoice lapsed unpaid.

One of:createdinvoicedpaiddeliveringdeliveredneeds_operatorfailedrefund_pendingrefundedexpired
order.fundingenum

Which rail actually funded it. FUNDING_AUTO resolves to one of these.

One of:invoicebalance
order.total_usdmoney

What the order charges.

order.invoice_idstring

The invoice behind it — every order has one, whichever rail funded it.

order.itemsint

How many entitlements the order mints or extends.

order.created_atunix

When it was placed.

order.delivered_atunix

When delivery finished. Absent until then.

order.breakdownobject

The quote the customer agreed to, verbatim — the same shape Quote returns.

invoiceobject

Populated only while the order still awaits payment — the same shape as everywhere else.

Sample response
{
  "order": {
    "order_id": "ord_7b2c9d", "product": "vpn", "kind": "new",
    "status": "delivered", "funding": "balance", "total_usd": "11.90",
    "invoice_id": "inv_5k8p2q", "items": 1,
    "created_at": "1785230043", "delivered_at": "1785230103",
    "breakdown": {
    "model_name": "vpn_dynamic",
    "model_type": "dynamic_v2",
    "base_usd": "14.00",
    "total_usd": "11.90",
    "lines": [
      {"code": "base", "kind": "base", "amount_usd": "14.00"},
      {"code": "loyalty", "kind": "discount", "amount_usd": "-2.10", "pct": "15"}
    ]
  }
  }
}
POST

ListOrders

read
https://apiservice.vitamindata.net/api/v1/ListOrders

Your order history, newest first.

Request
limitintoptional

Page size. Default 25, max 100.

cursorcursoroptional

The previous response's next_cursor. Omit it for the first page.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListOrders \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"limit":25,"cursor":""}'
Response200 · application/json
ordersarray

The orders.

orders[].order_idstring

The order's public id (ord_…).

orders[].productenum

What was bought.

One of:vpnvps
orders[].kindstring

The purchase shape: new, extend, upgrade

orders[].statusenum

The lifecycle. delivered is the goal; needs_operator means the money is in and a human is finishing delivery — do not re-buy; expired means the invoice lapsed unpaid.

One of:createdinvoicedpaiddeliveringdeliveredneeds_operatorfailedrefund_pendingrefundedexpired
orders[].fundingenum

Which rail actually funded it. FUNDING_AUTO resolves to one of these.

One of:invoicebalance
orders[].total_usdmoney

What the order charges.

orders[].invoice_idstring

The invoice behind it — every order has one, whichever rail funded it.

orders[].itemsint

How many entitlements the order mints or extends.

orders[].created_atunix

When it was placed.

orders[].delivered_atunix

When delivery finished. Absent until then.

orders[].breakdownobject

The quote the customer agreed to, verbatim — the same shape Quote returns.

next_cursorcursor

Pass it back as cursor for the next page. Absent/empty = you have everything.

Sample response
{
  "orders": [
    {"order_id": "ord_7b2c9d", "product": "vpn", "kind": "new", "status": "delivered",
     "funding": "balance", "total_usd": "11.90", "invoice_id": "inv_5k8p2q",
     "items": 1, "created_at": "1785230043", "delivered_at": "1785230103"}
  ]
}

Managing VPN

POST

ListVPN

read
https://apiservice.vitamindata.net/api/v1/ListVPN

Every VPN account you hold, with traffic left and expiry.

Request
limitintoptional

Page size. Default 50, max 200.

cursorcursoroptional

The previous response's next_cursor. Omit it for the first page.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListVPN \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"limit":25,"cursor":""}'
Response200 · application/json
vpnsarray

Your accounts.

vpns[].vpn_idstring

The public id every other VPN verb takes.

vpns[].usernamestring

The credential name shown in apps.

vpns[].statusstring

The account's state as the panel shows it — active unless suspended or expired.

vpns[].remaining_bytesbytes

Traffic left across all its bundles.

vpns[].total_downloadbytes

Lifetime download, as reported live by the VPN plane. In ListVPN this is always 0 — the list is served from the cached read model, which has no per-direction figures. Use used_bytes for a list, or GetVPN/GetVPNUsage for the split.

vpns[].total_uploadbytes

Lifetime upload. Same caveat as total_download: 0 in ListVPN.

vpns[].used_bytesbytes

Lifetime traffic actually spent (download + upload). This is the figure the estate maintains for every VPN, so it is correct on every endpoint including ListVPN.

vpns[].expires_atunix

When the account lapses.

vpns[].max_onlineint

Concurrent devices allowed.

vpns[].daily_limit_bytesbytes

Today's cap. Absent/0 = NO daily cap.

vpns[].daily_used_bytesbytes

Spent against today's cap.

vpns[].daily_reset_unixunix

When the daily counter resets.

vpns[].allowed_protocolsint

A protocol bitmask used by our apps. Treat it as opaque.

vpns[].cache_atunix

How fresh the figures are: they are as old as this timestamp unless live is set.

vpns[].created_atunix

When the account was minted.

vpns[].livebool

The figures came from the network just now rather than the cache.

next_cursorcursor

Pass it back as cursor for the next page. Absent/empty = you have everything.

Sample response
{
  "vpns": [{
    "vpn_id": "vpn_6t2k9p",
    "username": "u482913",
    "status": "active",
    "remaining_bytes": "96636764160",
    "total_download": "10737418240",
    "used_bytes": "11811160064",
    "total_upload": "1073741824",
    "expires_at": "1793188800",
    "max_online": 3,
    "cache_at": "1785412700",
    "created_at": "1785000000"
  }],
  "next_cursor": "vpn_6t2k9p"
}
POST

GetVPN

read
https://apiservice.vitamindata.net/api/v1/GetVPN

One account in detail. live:true means the figures came from the network just now.

Request
vpn_idstringrequired

The account's public id (vpn_…), from ListVPN or an order's delivery.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetVPN \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vpn_id":"vpn_6t2k9p"}'
Response200 · application/json
vpnobject

The account.

vpn.vpn_idstring

The public id every other VPN verb takes.

vpn.usernamestring

The credential name shown in apps.

vpn.statusstring

The account's state as the panel shows it — active unless suspended or expired.

vpn.remaining_bytesbytes

Traffic left across all its bundles.

vpn.total_downloadbytes

Lifetime download, as reported live by the VPN plane. In ListVPN this is always 0 — the list is served from the cached read model, which has no per-direction figures. Use used_bytes for a list, or GetVPN/GetVPNUsage for the split.

vpn.total_uploadbytes

Lifetime upload. Same caveat as total_download: 0 in ListVPN.

vpn.used_bytesbytes

Lifetime traffic actually spent (download + upload). This is the figure the estate maintains for every VPN, so it is correct on every endpoint including ListVPN.

vpn.expires_atunix

When the account lapses.

vpn.max_onlineint

Concurrent devices allowed.

vpn.daily_limit_bytesbytes

Today's cap. Absent/0 = NO daily cap.

vpn.daily_used_bytesbytes

Spent against today's cap.

vpn.daily_reset_unixunix

When the daily counter resets.

vpn.allowed_protocolsint

A protocol bitmask used by our apps. Treat it as opaque.

vpn.cache_atunix

How fresh the figures are: they are as old as this timestamp unless live is set.

vpn.created_atunix

When the account was minted.

vpn.livebool

The figures came from the network just now rather than the cache.

livebool

Fresh from the network. Absent = served from the read model, as old as cache_at.

Sample response
{
  "vpn": {
    "vpn_id": "vpn_6t2k9p",
    "username": "u482913",
    "status": "active",
    "remaining_bytes": "96636764160",
    "total_download": "10737418240",
    "used_bytes": "11811160064",
    "total_upload": "1073741824",
    "expires_at": "1793188800",
    "max_online": 3,
    "cache_at": "1785412700",
    "created_at": "1785000000"
  },
  "live": true
}
POST

GetVPNUsage

read
https://apiservice.vitamindata.net/api/v1/GetVPNUsage

Traffic used and left, plus today's cap if one applies.

Request
vpn_idstringrequired

The account's public id (vpn_…), from ListVPN or an order's delivery.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetVPNUsage \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vpn_id":"vpn_6t2k9p"}'
Response200 · application/json
vpn_idstring

Echoed back.

remaining_bytesbytes

Traffic left.

total_downloadbytes

Lifetime download.

total_uploadbytes

Lifetime upload.

expires_atunix

When the account lapses.

daily_limit_bytesbytes

Today's cap. Absent = none.

daily_used_bytesbytes

Spent against today's cap.

daily_reset_unixunix

When the daily counter resets.

cache_atunix

Freshness of the figures when not live.

livebool

Fresh from the network just now.

seriesarray

Reserved for the historical usage series — empty today.

series[].tunix

Bucket start.

series[].rxbytes

Downloaded in the bucket.

series[].txbytes

Uploaded in the bucket.

Sample response
{
  "vpn_id": "vpn_6t2k9p",
  "remaining_bytes": "96636764160",
  "total_download": "10737418240",
  "total_upload": "1073741824",
  "expires_at": "1793188800",
  "daily_limit_bytes": "53687091200",
  "daily_used_bytes": "1273741824",
  "daily_reset_unix": "1785456000",
  "cache_at": "1785412700",
  "live": true
}
POST

ListVPNBundles

read
https://apiservice.vitamindata.net/api/v1/ListVPNBundles

The traffic wallets behind one account, in the order they will be consumed.

This is how you answer "why did my customer's paid traffic go down while they had free traffic left?" — queue_position 1 is what drains next. live:false means we could not read them, which is not the same as having none.

Request
vpn_idstringrequired

The account's public id (vpn_…), from ListVPN or an order's delivery.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListVPNBundles \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vpn_id":"vpn_6t2k9p"}'
Response200 · application/json
bundlesarray

The wallets, in consumption order.

bundles[].bundle_idstring

The wallet's id.

bundles[].freebool

Granted by the free tier rather than bought.

bundles[].sourceenum

Where the wallet came from.

One of:buyextendmigratedfree_tier
bundles[].granted_bytesbytes

Its full size when granted.

bundles[].remaining_bytesbytes

What is left in it.

bundles[].granted_atunix

When it was granted.

bundles[].expires_atunix

When it lapses whether or not it is spent.

bundles[].daily_limit_bytesbytes

Its own daily cap. Absent = none.

bundles[].statusenum

Spelled, not numbered, so you never have to learn that 2 means exhausted.

One of:activedisabledexhausted
bundles[].queue_positionint

1-based over the USABLE wallets, in consumption order — 1 drains next. 0/absent means it is out of the queue (spent, expired or disabled).

bundles[].plan_gbint

The size it was sold as, in GB.

bundles[].plan_daysint

The validity it was sold as, in days.

bundles[].invoice_idstring

The purchase it came from. Absent on a free grant.

livebool

Absent/false = the entitlement plane was unreachable, so the list is EMPTY rather than wrong. "We could not read them" and "there are none" are different sentences.

Sample response
{
  "bundles": [
    {"bundle_id": "bnd_2m8x", "source": "buy",
     "granted_bytes": "107374182400", "remaining_bytes": "96636764160",
     "granted_at": "1785000000", "expires_at": "1793188800",
     "status": "active", "queue_position": 1, "plan_gb": 100, "plan_days": 30,
     "invoice_id": "inv_5k8p2q"},
    {"bundle_id": "bnd_9k1f", "free": true, "source": "free_tier",
     "granted_bytes": "5368709120", "remaining_bytes": "5368709120",
     "granted_at": "1784000000", "expires_at": "1793188800",
     "status": "active", "queue_position": 2, "plan_gb": 5, "plan_days": 30}
  ],
  "live": true
}
POST

SetVPNPassword

credentials
https://apiservice.vitamindata.net/api/v1/SetVPNPassword

Changes an account's password.

Needs the separate credentials scope — it is never implied by manage.

Request
vpn_idstringrequired

The account's public id (vpn_…), from ListVPN or an order's delivery.

new_passwordstringrequired

The password to set.

credentialstringoptional

Which credential, for multi-credential accounts. Empty = the primary one.

Example request
curl https://apiservice.vitamindata.net/api/v1/SetVPNPassword \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vpn_id":"vpn_6t2k9p","new_password":"s3cr3t-Enough","credential":""}'
Response200 · application/json
usernamestring

The credential the change landed on.

Sample response
{"username": "u482913"}
POST

SetVPNState

manage
https://apiservice.vitamindata.net/api/v1/SetVPNState

Suspends or resumes an account.

Request
vpn_idstringrequired

The account's public id (vpn_…), from ListVPN or an order's delivery.

stateenumrequired

What to do.

One of:suspendresume
Example request
curl https://apiservice.vitamindata.net/api/v1/SetVPNState \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vpn_id":"vpn_6t2k9p","state":"suspend"}'
Response200 · application/json
statusenum

The state the account settled in.

One of:suspendedactive
Sample response
{"status": "suspended"}
POST

DeleteVPN

manage
https://apiservice.vitamindata.net/api/v1/DeleteVPN

Deletes an account. There is no undo and no refund.

Request
vpn_idstringrequired

The account's public id (vpn_…), from ListVPN or an order's delivery.

Example request
curl https://apiservice.vitamindata.net/api/v1/DeleteVPN \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vpn_id":"vpn_6t2k9p"}'
Response200 · application/json
statusenum

Always deleted on success.

One of:deleted
Sample response
{"status": "deleted"}

Selling servers

A VPS sale is the same funding contract as a VPN sale — only the cart differs.

POST

GetVPSStorefront

read
https://apiservice.vitamindata.net/api/v1/GetVPSStorefront

Locations, the plans priced at each, and the bootable images.

A node with available:false is out of capacity — show it, do not offer it.

Request

No parameters — send an empty object, {}.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetVPSStorefront \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'
Response200 · application/json
nodesarray

The locations, each with its own priced plans.

nodes[].node_idstring

What a cart's node_id takes.

nodes[].labelstring

Display name (Frankfurt).

nodes[].regionstring

Coarse region code for grouping.

nodes[].countrystring

ISO country code, for flags.

nodes[].availablebool

Absent/false = out of capacity: show it, do not offer it.

nodes[].plansarray

What is sellable here, priced.

nodes[].plans[].plan_codestring

What a cart's plan_code takes.

nodes[].plans[].namestring

Display name.

nodes[].plans[].vcpuint

Cores.

nodes[].plans[].ram_mbint

Memory in MB.

nodes[].plans[].disk_gbint

Disk in GB.

nodes[].plans[].traffic_bytesbytes

Monthly traffic included.

nodes[].plans[].price_usd_monthmoney

The monthly price AT THIS NODE — the same plan can cost differently elsewhere.

imagesarray

Everything bootable or installable.

images[].shastring

The image's ONLY identifier — carts, reinstalls and ISO attaches all take it.

images[].namestring

Human name (Debian 13).

images[].kindenum

A disk image provisions directly; an iso is an installer you boot from.

One of:diskiso
images[].os_familyenum

For your UI's grouping and icons.

One of:linuxwindowsmikrotik
images[].min_disk_gbint

A reinstall onto a smaller disk is refused — grow the disk first.

months_minint

Shortest term a new VM may be bought for.

months_maxint

Longest.

Sample response
{
  "nodes": [
    {"node_id": "de1", "label": "Frankfurt", "region": "eu", "country": "DE", "available": true,
     "plans": [
       {"plan_code": "s2", "name": "S2", "vcpu": 2, "ram_mb": 4096, "disk_gb": 40,
        "traffic_bytes": "2199023255552", "price_usd_month": "8.00"}
     ]}
  ],
  "images": [
    {"sha": "9a1b8c2d7e6f", "name": "Debian 13", "kind": "disk", "os_family": "linux", "min_disk_gb": 10}
  ],
  "months_min": 1,
  "months_max": 12
}
POST

ListVPSImages

read
https://apiservice.vitamindata.net/api/v1/ListVPSImages

Every image you may boot or install, by sha.

The sha is what a cart, a reinstall and an ISO attach all take. Nothing else identifies an image.

Request

No parameters — send an empty object, {}.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListVPSImages \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'
Response200 · application/json
imagesarray

The catalogue.

images[].shastring

The image's ONLY identifier — carts, reinstalls and ISO attaches all take it.

images[].namestring

Human name (Debian 13).

images[].kindenum

A disk image provisions directly; an iso is an installer you boot from.

One of:diskiso
images[].os_familyenum

For your UI's grouping and icons.

One of:linuxwindowsmikrotik
images[].min_disk_gbint

A reinstall onto a smaller disk is refused — grow the disk first.

Sample response
{
  "images": [
    {"sha": "9a1b8c2d7e6f", "name": "Debian 13", "kind": "disk", "os_family": "linux", "min_disk_gb": 10},
    {"sha": "3f4e5d6c7b8a", "name": "Windows Server 2025 installer", "kind": "iso",
     "os_family": "windows", "min_disk_gb": 40}
  ]
}
POST

QuoteVPS

read
https://apiservice.vitamindata.net/api/v1/QuoteVPS

The price of a new server, itemised.

Request
cartobjectrequired

The server to price.

cart.node_idstringrequired

Where to create the VM, from the storefront's nodes.

cart.placementstringreserved — do not send

Reserved. On this API node_id always decides where the server is created — send it, and pick the node yourself from the storefront.

One of:auto
cart.plan_codestringrequired

The plan, from the chosen node's own price list — plans and prices differ per node.

cart.image_shastringrequired

What to boot or install, by sha. Disk images provision directly; an installer ISO comes attached with the VM set to boot from it.

cart.namestringrequired

The VM's hostname / label.

cart.monthsintrequired

The initial term, within the storefront's months bounds.

cart.extra_disk_gbintoptional

Extra disk beyond the plan's, in GB.

cart.extra_ipsintoptional

Additional public IPv4 addresses.

cart.extra_traffic_tbmoneyoptional

Extra monthly traffic in TB, as a decimal string ("0.5").

Example request
curl https://apiservice.vitamindata.net/api/v1/QuoteVPS \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"cart":{"node_id":"de1","plan_code":"s2","image_sha":"9a1b8c2d7e6f","months":2,"name":"web-1","extra_disk_gb":20,"extra_ips":1,"extra_traffic_tb":"0.5"}}'
Response200 · application/json
quoteobject

The itemised price. Nothing is created and nothing is charged.

quote.model_namestring

Which pricing model answered.

quote.model_typestring

The model's engine version.

quote.base_usdmoney

The price before discounts and surcharges.

quote.total_usdmoney

The final price — what a sale with this cart charges.

quote.linesarray

The itemisation, signed, summing exactly to the total.

quote.lines[].codestring

What this row is (base, a discount code, a surcharge…).

quote.lines[].kindstring

The row's category, for grouping in your UI.

quote.lines[].amount_usdmoney

Signed. Discounts are negative; the rows sum exactly to total_usd.

quote.lines[].pctstring

The percentage behind the row, when there is one.

quote.cappedbool

The total hit the model's ceiling.

quote.floor_appliedbool

The total was raised to the model's floor.

quote.clampedenum

Set when a cart value was clamped into bounds before pricing.

One of:minmax
quote.invalidstring

Non-empty means the cart cannot be priced — show it and NEVER charge such a quote.

quote.metamap

Model extras, string→string (e.g. an upgrade's remaining_days).

Sample response
{
  "quote": {
    "model_name": "vps_plan",
    "model_type": "vps_plan_v1",
    "base_usd": "16.00",
    "total_usd": "16.00",
    "lines": [
      {"code": "plan_s2", "kind": "base", "amount_usd": "16.00"}
    ],
    "meta": {"months": "2"}
  }
}
POST

CreateVPSOrder

buyIdempotency-Key
https://apiservice.vitamindata.net/api/v1/CreateVPSOrder

Buys and provisions a server.

Poll GetOrder for delivery, then GetVPS for the machine.

Request
cartobjectrequired

What to create — the same shape QuoteVPS prices.

cart.node_idstringrequired

Where to create the VM, from the storefront's nodes.

cart.placementstringreserved — do not send

Reserved. On this API node_id always decides where the server is created — send it, and pick the node yourself from the storefront.

One of:auto
cart.plan_codestringrequired

The plan, from the chosen node's own price list — plans and prices differ per node.

cart.image_shastringrequired

What to boot or install, by sha. Disk images provision directly; an installer ISO comes attached with the VM set to boot from it.

cart.namestringrequired

The VM's hostname / label.

cart.monthsintrequired

The initial term, within the storefront's months bounds.

cart.extra_disk_gbintoptional

Extra disk beyond the plan's, in GB.

cart.extra_ipsintoptional

Additional public IPv4 addresses.

cart.extra_traffic_tbmoneyoptional

Extra monthly traffic in TB, as a decimal string ("0.5").

fundingenumrequired

How to pay — see Paying for things. There is no default: omitting it is refused.

One of:FUNDING_AUTOFUNDING_BALANCEFUNDING_INVOICE
expected_total_usdmoneyoptional

Confirmation guard. If set and our fresh quote differs, the sale is refused with price_changed instead of charging a number your customer never saw.

Example request
curl https://apiservice.vitamindata.net/api/v1/CreateVPSOrder \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"cart":{"node_id":"de1","plan_code":"s2","image_sha":"9a1b8c2d7e6f","months":2,"name":"web-1","extra_disk_gb":20,"extra_ips":1,"extra_traffic_tb":"0.5"},"funding":"FUNDING_AUTO","expected_total_usd":"36.00"}'
Response200 · application/json
orderobject

The order — poll GetOrder until its status is delivered.

order.order_idstring

The order's public id (ord_…).

order.productenum

What was bought.

One of:vpnvps
order.kindstring

The purchase shape: new, extend, upgrade

order.statusenum

The lifecycle. delivered is the goal; needs_operator means the money is in and a human is finishing delivery — do not re-buy; expired means the invoice lapsed unpaid.

One of:createdinvoicedpaiddeliveringdeliveredneeds_operatorfailedrefund_pendingrefundedexpired
order.fundingenum

Which rail actually funded it. FUNDING_AUTO resolves to one of these.

One of:invoicebalance
order.total_usdmoney

What the order charges.

order.invoice_idstring

The invoice behind it — every order has one, whichever rail funded it.

order.itemsint

How many entitlements the order mints or extends.

order.created_atunix

When it was placed.

order.delivered_atunix

When delivery finished. Absent until then.

order.breakdownobject

The quote the customer agreed to, verbatim — the same shape Quote returns.

invoiceobject

The invoice behind the order: payable when payment_required, already settled when completed.

invoice.invoice_idstring

The gateway's invoice id (inv_…).

invoice.statusenum

pending is payable. Everything from confirmed through delivered_redirected means the money is irreversibly in — treat all of those as PAID. expired means the link lapsed unpaid.

One of:pendingconfirmedsweepingsweptdeliveringdelivereddelivered_redirectedexpired
invoice.pay_urlstring

The hosted payment page. Send your customer here; every rail (coins, chains, balance) lives behind it.

invoice.pay_telegram_urlstring

The same invoice, payable inside Telegram: it opens the payment provider's own bot, which shows the amount and takes the payment there. Offer it beside pay_url for customers who would rather not leave the app. May be absent — only a payment provider with a bot configured has one, so never make it your only pay button.

invoice.price_usdmoney

What the invoice collects.

invoice.expires_atunix

When the payment window closes.

paidbool

The money is captured. Absent (= false) with payment_required.

statusenum

THE field to branch on — see Paying for things.

One of:completedpayment_required
Sample response
{
  "order": {
    "order_id": "ord_2f8k3j", "product": "vps", "kind": "new",
    "status": "invoiced", "funding": "invoice", "total_usd": "16.00",
    "invoice_id": "inv_7h4w9s", "items": 1, "created_at": "1785412800"
  },
  "invoice": {
    "invoice_id": "inv_7h4w9s", "status": "pending",
    "pay_url": "https://pay.example.com/i/inv_7h4w9s",
    "price_usd": "16.00", "expires_at": "1785499200"
  },
  "status": "payment_required"
}
POST

QuoteVPSExtend

read
https://apiservice.vitamindata.net/api/v1/QuoteVPSExtend

What renewing a server costs.

Request
vps_idstringrequired

The server's id, from ListVPS.

monthsintrequired

How many months to add. The storefront's months_minmonths_max are what your UI should offer, but a renewal does NOT clamp to them — send a sane number, because whatever you send is what gets priced and charged.

Example request
curl https://apiservice.vitamindata.net/api/v1/QuoteVPSExtend \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vps_id":"vm_7q3k1n","months":1}'
Response200 · application/json
quoteobject

The itemised price. Nothing is created and nothing is charged.

quote.model_namestring

Which pricing model answered.

quote.model_typestring

The model's engine version.

quote.base_usdmoney

The price before discounts and surcharges.

quote.total_usdmoney

The final price — what a sale with this cart charges.

quote.linesarray

The itemisation, signed, summing exactly to the total.

quote.lines[].codestring

What this row is (base, a discount code, a surcharge…).

quote.lines[].kindstring

The row's category, for grouping in your UI.

quote.lines[].amount_usdmoney

Signed. Discounts are negative; the rows sum exactly to total_usd.

quote.lines[].pctstring

The percentage behind the row, when there is one.

quote.cappedbool

The total hit the model's ceiling.

quote.floor_appliedbool

The total was raised to the model's floor.

quote.clampedenum

Set when a cart value was clamped into bounds before pricing.

One of:minmax
quote.invalidstring

Non-empty means the cart cannot be priced — show it and NEVER charge such a quote.

quote.metamap

Model extras, string→string (e.g. an upgrade's remaining_days).

Sample response
{
  "quote": {
    "model_name": "vps_plan",
    "model_type": "vps_plan_v1",
    "base_usd": "16.00",
    "total_usd": "16.00",
    "lines": [
      {"code": "plan_s2", "kind": "base", "amount_usd": "16.00"}
    ],
    "meta": {"months": "2"}
  }
}
POST

ExtendVPS

buyIdempotency-Key
https://apiservice.vitamindata.net/api/v1/ExtendVPS

Renews a server.

Renewing early costs nothing extra: the new expiry is measured from the current one, not from today.

Request
vps_idstringrequired

The server's id, from ListVPS.

monthsintrequired

How many months to add. The storefront's months_minmonths_max are what your UI should offer, but a renewal does NOT clamp to them — send a sane number, because whatever you send is what gets priced and charged.

fundingenumrequired

How to pay — see Paying for things. There is no default: omitting it is refused.

One of:FUNDING_AUTOFUNDING_BALANCEFUNDING_INVOICE
expected_total_usdmoneyoptional

Confirmation guard. If set and our fresh quote differs, the sale is refused with price_changed instead of charging a number your customer never saw.

Example request
curl https://apiservice.vitamindata.net/api/v1/ExtendVPS \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"vps_id":"vm_7q3k1n","months":1,"funding":"FUNDING_AUTO","expected_total_usd":"8.00"}'
Response200 · application/json
orderobject

The order — poll GetOrder until its status is delivered.

order.order_idstring

The order's public id (ord_…).

order.productenum

What was bought.

One of:vpnvps
order.kindstring

The purchase shape: new, extend, upgrade

order.statusenum

The lifecycle. delivered is the goal; needs_operator means the money is in and a human is finishing delivery — do not re-buy; expired means the invoice lapsed unpaid.

One of:createdinvoicedpaiddeliveringdeliveredneeds_operatorfailedrefund_pendingrefundedexpired
order.fundingenum

Which rail actually funded it. FUNDING_AUTO resolves to one of these.

One of:invoicebalance
order.total_usdmoney

What the order charges.

order.invoice_idstring

The invoice behind it — every order has one, whichever rail funded it.

order.itemsint

How many entitlements the order mints or extends.

order.created_atunix

When it was placed.

order.delivered_atunix

When delivery finished. Absent until then.

order.breakdownobject

The quote the customer agreed to, verbatim — the same shape Quote returns.

invoiceobject

The invoice behind the order: payable when payment_required, already settled when completed.

invoice.invoice_idstring

The gateway's invoice id (inv_…).

invoice.statusenum

pending is payable. Everything from confirmed through delivered_redirected means the money is irreversibly in — treat all of those as PAID. expired means the link lapsed unpaid.

One of:pendingconfirmedsweepingsweptdeliveringdelivereddelivered_redirectedexpired
invoice.pay_urlstring

The hosted payment page. Send your customer here; every rail (coins, chains, balance) lives behind it.

invoice.pay_telegram_urlstring

The same invoice, payable inside Telegram: it opens the payment provider's own bot, which shows the amount and takes the payment there. Offer it beside pay_url for customers who would rather not leave the app. May be absent — only a payment provider with a bot configured has one, so never make it your only pay button.

invoice.price_usdmoney

What the invoice collects.

invoice.expires_atunix

When the payment window closes.

paidbool

The money is captured. Absent (= false) with payment_required.

statusenum

THE field to branch on — see Paying for things.

One of:completedpayment_required
Sample response
{
  "order": {
    "order_id": "ord_5d1p8m", "product": "vps", "kind": "extend",
    "status": "paid", "funding": "balance", "total_usd": "8.00",
    "invoice_id": "inv_1c6v3z", "items": 1, "created_at": "1785412800"
  },
  "invoice": {
    "invoice_id": "inv_1c6v3z", "status": "confirmed", "price_usd": "8.00"
  },
  "paid": true,
  "status": "completed"
}
POST

QuoteVPSUpgrade

read
https://apiservice.vitamindata.net/api/v1/QuoteVPSUpgrade

What a bigger shape costs, pro-rated over the time already paid for.

Request
vps_idstringrequired

The server's id, from ListVPS.

specobjectrequired

The TARGET shape. Omitted/zero fields keep their current value.

spec.vcpuintoptional

Target cores. 0 keeps the current value.

spec.ram_mbintoptional

Target memory in MB. 0 keeps the current value.

spec.disk_gbintoptional

Target disk in GB. Disk only grows — a smaller value is refused, because shrinking a filesystem under an OS is data loss.

Example request
curl https://apiservice.vitamindata.net/api/v1/QuoteVPSUpgrade \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"vps_id":"vm_7q3k1n","spec":{"vcpu":4,"ram_mb":8192,"disk_gb":80}}'
Response200 · application/json
quoteobject

The itemised price. Nothing is created and nothing is charged.

quote.model_namestring

Which pricing model answered.

quote.model_typestring

The model's engine version.

quote.base_usdmoney

The price before discounts and surcharges.

quote.total_usdmoney

The final price — what a sale with this cart charges.

quote.linesarray

The itemisation, signed, summing exactly to the total.

quote.lines[].codestring

What this row is (base, a discount code, a surcharge…).

quote.lines[].kindstring

The row's category, for grouping in your UI.

quote.lines[].amount_usdmoney

Signed. Discounts are negative; the rows sum exactly to total_usd.

quote.lines[].pctstring

The percentage behind the row, when there is one.

quote.cappedbool

The total hit the model's ceiling.

quote.floor_appliedbool

The total was raised to the model's floor.

quote.clampedenum

Set when a cart value was clamped into bounds before pricing.

One of:minmax
quote.invalidstring

Non-empty means the cart cannot be priced — show it and NEVER charge such a quote.

quote.metamap

Model extras, string→string (e.g. an upgrade's remaining_days).

restart_requiredbool

Applying this shape needs a power cycle — a customer should learn that BEFORE paying.

monthly_before_usdmoney

The recurring price today.

monthly_after_usdmoney

The recurring price after the upgrade — what renewals will cost from now on.

Sample response
{
  "quote": {
    "model_name": "vps_upgrade",
    "model_type": "vps_plan_v1",
    "base_usd": "4.20",
    "total_usd": "4.20",
    "lines": [
      {"code": "prorate_vcpu", "kind": "vps_upgrade", "amount_usd": "2.40"},
      {"code": "prorate_ram", "kind": "vps_upgrade", "amount_usd": "1.80"}
    ],
    "meta": {"remaining_days": "21", "monthly_delta_usd": "6.00", "quote_expires_at": "1785416400"}
  },
  "restart_required": true,
  "monthly_before_usd": "8.00",
  "monthly_after_usd": "14.00"
}
POST

UpgradeVPS

buyIdempotency-Key
https://apiservice.vitamindata.net/api/v1/UpgradeVPS

Pays for and applies a bigger shape. Disk can only grow.

Request
vps_idstringrequired

The server's id, from ListVPS.

specobjectrequired

The TARGET shape — quote it first; the quote also tells you about the reboot.

spec.vcpuintoptional

Target cores. 0 keeps the current value.

spec.ram_mbintoptional

Target memory in MB. 0 keeps the current value.

spec.disk_gbintoptional

Target disk in GB. Disk only grows — a smaller value is refused, because shrinking a filesystem under an OS is data loss.

fundingenumrequired

How to pay — see Paying for things. There is no default: omitting it is refused.

One of:FUNDING_AUTOFUNDING_BALANCEFUNDING_INVOICE
expected_total_usdmoneyoptional

Confirmation guard. If set and our fresh quote differs, the sale is refused with price_changed instead of charging a number your customer never saw.

Example request
curl https://apiservice.vitamindata.net/api/v1/UpgradeVPS \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"vps_id":"vm_7q3k1n","spec":{"vcpu":4,"ram_mb":8192,"disk_gb":80},"funding":"FUNDING_AUTO","expected_total_usd":"4.20"}'
Response200 · application/json
orderobject

The order — poll GetOrder until its status is delivered.

order.order_idstring

The order's public id (ord_…).

order.productenum

What was bought.

One of:vpnvps
order.kindstring

The purchase shape: new, extend, upgrade

order.statusenum

The lifecycle. delivered is the goal; needs_operator means the money is in and a human is finishing delivery — do not re-buy; expired means the invoice lapsed unpaid.

One of:createdinvoicedpaiddeliveringdeliveredneeds_operatorfailedrefund_pendingrefundedexpired
order.fundingenum

Which rail actually funded it. FUNDING_AUTO resolves to one of these.

One of:invoicebalance
order.total_usdmoney

What the order charges.

order.invoice_idstring

The invoice behind it — every order has one, whichever rail funded it.

order.itemsint

How many entitlements the order mints or extends.

order.created_atunix

When it was placed.

order.delivered_atunix

When delivery finished. Absent until then.

order.breakdownobject

The quote the customer agreed to, verbatim — the same shape Quote returns.

invoiceobject

The invoice behind the order: payable when payment_required, already settled when completed.

invoice.invoice_idstring

The gateway's invoice id (inv_…).

invoice.statusenum

pending is payable. Everything from confirmed through delivered_redirected means the money is irreversibly in — treat all of those as PAID. expired means the link lapsed unpaid.

One of:pendingconfirmedsweepingsweptdeliveringdelivereddelivered_redirectedexpired
invoice.pay_urlstring

The hosted payment page. Send your customer here; every rail (coins, chains, balance) lives behind it.

invoice.pay_telegram_urlstring

The same invoice, payable inside Telegram: it opens the payment provider's own bot, which shows the amount and takes the payment there. Offer it beside pay_url for customers who would rather not leave the app. May be absent — only a payment provider with a bot configured has one, so never make it your only pay button.

invoice.price_usdmoney

What the invoice collects.

invoice.expires_atunix

When the payment window closes.

paidbool

The money is captured. Absent (= false) with payment_required.

statusenum

THE field to branch on — see Paying for things.

One of:completedpayment_required
Sample response
{
  "order": {
    "order_id": "ord_5d1p8m", "product": "vps", "kind": "extend",
    "status": "paid", "funding": "balance", "total_usd": "8.00",
    "invoice_id": "inv_1c6v3z", "items": 1, "created_at": "1785412800"
  },
  "invoice": {
    "invoice_id": "inv_1c6v3z", "status": "confirmed", "price_usd": "8.00"
  },
  "paid": true,
  "status": "completed"
}

Managing servers

Every verb here takes the server by id — the field is literally named id on these calls, unlike the money verbs' vps_id.

POST

ListVPS

read
https://apiservice.vitamindata.net/api/v1/ListVPS

Your machines, with power state and addresses.

Request
limitintoptional

Page size. Default 50.

cursorcursoroptional

The previous response's next_cursor. Omit it for the first page.

Example request
curl https://apiservice.vitamindata.net/api/v1/ListVPS \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"limit":50,"cursor":""}'
Response200 · application/json
vpsarray

Your machines.

vps[].idstring

The server's id — what every management verb's id and every money verb's vps_id take.

vps[].namestring

Its hostname / label.

vps[].node_idstring

Where it runs.

vps[].lifecycleenum

0 provisioning · 1 active · 2 suspended · 3 deleting · 4 deleted.

One of:01234
vps[].provisionedbool

The machine exists on its host.

vps[].power_desiredenum

What the power SHOULD be: 1 on, 0/absent off. The live state is GetVPSStats.running.

One of:01
vps[].vcpuint

Cores.

vps[].mem_mbint

Memory in MB.

vps[].disk_gbint

Disk in GB.

vps[].image_shastring

What it booted or installed from.

vps[].expires_atunix

When the server lapses — renew with ExtendVPS before this.

vps[].created_atunix

When it was created.

vps[].ipsarray

Its public addresses.

vps[].private_ipstring

Its private address, when the plan has one.

next_cursorcursor

Pass it back as cursor for the next page. Absent/empty = you have everything.

Sample response
{
  "vps": [{
    "id": "vm_7q3k1n",
    "name": "web-1",
    "node_id": "de1",
    "lifecycle": 1,
    "provisioned": true,
    "power_desired": 1,
    "vcpu": 2,
    "mem_mb": 4096,
    "disk_gb": 40,
    "image_sha": "9a1b8c2d7e6f",
    "expires_at": "1793188800",
    "created_at": "1785000000",
    "ips": ["203.0.113.10"],
    "private_ip": "10.77.0.10"
  }]
}
POST

GetVPS

read
https://apiservice.vitamindata.net/api/v1/GetVPS

One machine in detail.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetVPS \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n"}'
Response200 · application/json
vpsobject

The machine.

vps.idstring

The server's id — what every management verb's id and every money verb's vps_id take.

vps.namestring

Its hostname / label.

vps.node_idstring

Where it runs.

vps.lifecycleenum

0 provisioning · 1 active · 2 suspended · 3 deleting · 4 deleted.

One of:01234
vps.provisionedbool

The machine exists on its host.

vps.power_desiredenum

What the power SHOULD be: 1 on, 0/absent off. The live state is GetVPSStats.running.

One of:01
vps.vcpuint

Cores.

vps.mem_mbint

Memory in MB.

vps.disk_gbint

Disk in GB.

vps.image_shastring

What it booted or installed from.

vps.expires_atunix

When the server lapses — renew with ExtendVPS before this.

vps.created_atunix

When it was created.

vps.ipsarray

Its public addresses.

vps.private_ipstring

Its private address, when the plan has one.

livebool

Read from the plane just now.

Sample response
{
  "vps": {
    "id": "vm_7q3k1n",
    "name": "web-1",
    "node_id": "de1",
    "lifecycle": 1,
    "provisioned": true,
    "power_desired": 1,
    "vcpu": 2,
    "mem_mb": 4096,
    "disk_gb": 40,
    "image_sha": "9a1b8c2d7e6f",
    "expires_at": "1793188800",
    "created_at": "1785000000",
    "ips": ["203.0.113.10"],
    "private_ip": "10.77.0.10"
  },
  "live": true
}
POST

GetVPSStats

read
https://apiservice.vitamindata.net/api/v1/GetVPSStats

Live CPU, memory, disk and network.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetVPSStats \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n"}'
Response200 · application/json
cpu_pctint

CPU load, percent.

mem_used_mbint

Memory in use, MB.

disk_used_mbint

Disk in use, MB.

rx_bpsint64

Inbound, bits per second.

tx_bpsint64

Outbound, bits per second.

runningbool

The machine is powered on right now.

updated_atunix

When these figures were sampled.

Sample response
{
  "cpu_pct": 12,
  "mem_used_mb": 1536,
  "disk_used_mb": 9216,
  "rx_bps": "1048576",
  "tx_bps": "524288",
  "running": true,
  "updated_at": "1785412790"
}
POST

GetVPSUsage

read
https://apiservice.vitamindata.net/api/v1/GetVPSUsage

Traffic used against the machine's allowance, per bundle.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

Example request
curl https://apiservice.vitamindata.net/api/v1/GetVPSUsage \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n"}'
Response200 · application/json
total_downbytes

Lifetime download.

total_upbytes

Lifetime upload.

bundlesarray

The traffic bundles, with current marking the one draining now.

bundles[].idint64

The traffic bundle's id on the compute plane.

bundles[].modeint

The plane's accounting mode code. Treat as opaque.

bundles[].bytes_totalbytes

The bundle's full allowance.

bundles[].used_dlbytes

Downloaded against it.

bundles[].used_upbytes

Uploaded against it.

bundles[].stateint

The plane's state code. Treat as opaque.

bundles[].currentbool

This is the bundle draining right now.

bundles[].expires_atunix

When the bundle lapses.

Sample response
{
  "total_down": "53687091200",
  "total_up": "10737418240",
  "bundles": [
    {"id": "41", "mode": 1, "bytes_total": "2199023255552",
     "used_dl": "53687091200", "used_up": "10737418240",
     "state": 1, "current": true, "expires_at": "1793188800"}
  ]
}
POST

VPSPower

manage
https://apiservice.vitamindata.net/api/v1/VPSPower

Power control.

shutdown asks the OS; force_stop pulls the plug. Prefer the first.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

actionenumrequired

shutdown/reboot are graceful; reset/force_stop are the power button.

One of:startshutdownrebootresetforce_stop
Example request
curl https://apiservice.vitamindata.net/api/v1/VPSPower \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n","action":"reboot"}'
Response200 · application/json
okbool

The plane accepted the action. Watch GetVPSStats for the machine's actual state.

Sample response
{"ok": true}
POST

ReinstallVPS

manage
https://apiservice.vitamindata.net/api/v1/ReinstallVPS

Wipes the disk and installs a fresh image.

Destructive and NOT retry-safe — one call, one reinstall. The image must fit the current disk; grow it first if not.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

image_shastringrequired

The image, by sha from ListVPSImages. Nothing else names an image, and an unoffered sha is refused.

reset_rootbooloptional

Also mint a fresh root password.

Example request
curl https://apiservice.vitamindata.net/api/v1/ReinstallVPS \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n","image_sha":"9a1b8c2d7e6f","reset_root":true}'
Response200 · application/json
okbool

The plane accepted the action. Watch GetVPSStats for the machine's actual state.

Sample response
{"ok": true}
POST

AttachVPSISO

manage
https://apiservice.vitamindata.net/api/v1/AttachVPSISO

Attaches an installer ISO. Pair it with a boot order of 1 to boot from it.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

image_shastringrequired

The image, by sha from ListVPSImages. Nothing else names an image, and an unoffered sha is refused.

Example request
curl https://apiservice.vitamindata.net/api/v1/AttachVPSISO \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n","image_sha":"3f4e5d6c7b8a"}'
Response200 · application/json
okbool

The plane accepted the action. Watch GetVPSStats for the machine's actual state.

Sample response
{"ok": true}
POST

DetachVPSISO

manage
https://apiservice.vitamindata.net/api/v1/DetachVPSISO

Removes the ISO and returns to booting from disk.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

Example request
curl https://apiservice.vitamindata.net/api/v1/DetachVPSISO \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n"}'
Response200 · application/json
okbool

The plane accepted the action. Watch GetVPSStats for the machine's actual state.

Sample response
{"ok": true}
POST

SetVPSBootOrder

manage
https://apiservice.vitamindata.net/api/v1/SetVPSBootOrder

Which device boots first.

Request
idstringrequired

The server's id, from ListVPS. Note the field is id on the management verbs — only the money verbs spell it vps_id.

orderenumrequired

0 disk first, 1 CD-ROM first.

One of:01
Example request
curl https://apiservice.vitamindata.net/api/v1/SetVPSBootOrder \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"id":"vm_7q3k1n","order":1}'
Response200 · application/json
okbool

The plane accepted the action. Watch GetVPSStats for the machine's actual state.

Sample response
{"ok": true}