Authentication

Every request must include your API key in the Authorization header. Generate and manage keys from your dashboard API page.

shell
$ curl https://otwa.cloud/api/v1/account \
  -H "Authorization: Bearer YOUR_API_KEY"

Scopes

Each key carries one or more scopes. Calls missing a required scope return 403. servers:destroy is opt-in and NOT included in the default set — mint a dedicated key for destructive integrations.

account:readAccount info, balance, reseller state, catalog (products, regions, OS templates)
servers:readList/view servers, credentials, live stats, snapshots, add-on catalog, IP list
servers:writeDeploy, rename, power, reset password, create/delete snapshots, purchase add-ons, issue SSO, set/clear PTR
servers:destroyTerminate, reinstall, or revert a snapshot (all destroy disk data; opt-in; NOT in the default scope set)
billing:readBalance, invoices, transactions, wallet, deposit addresses
webhooks:readList webhook subscriptions and delivery history (opt-in)
webhooks:writeCreate, update, delete webhook subscriptions; trigger redelivery (opt-in)

Error Codes

All errors return a JSON body with a message field.

400

Bad Request

Missing or invalid request body parameters.

401

Unauthorized

Missing, invalid, or expired API key.

402

Payment Required

Insufficient wallet balance to deploy a server.

403

Forbidden

API key lacks the required permission for this action.

404

Not Found

The requested resource does not exist or belongs to another account.

429

Too Many Requests

Rate limit exceeded (100 req/min). Retry after X-RateLimit-Reset.

500

Internal Server Error

Unexpected server error. If persistent, contact support.

Account

3 endpoints

Catalog

4 endpoints

Servers

38 endpoints

Billing

7 endpoints

Webhooks

8 endpoints

Server Statuses

The status field on server objects transitions through these values. Poll GET /api/v1/servers/:id to track provisioning.

pending

Order placed, waiting for worker to pick up the provision job.

building

VM is being deployed, configured, and booted. Takes ~60–120s.

running

Server is online and accepting connections.

stopped

Server is powered off. Can be started via power API.

suspended

Account billing issue — server inaccessible. Add funds to reactivate.

failed

Provisioning failed. Contact support or use admin retry.

terminated

Permanently destroyed. All data and IPs released.

Typical flow: pendingbuildingrunning (~60–120s from order)

Rate Limits

The API is limited to 100 requests per minute per API key. Requests exceeding this limit return 429 Too Many Requests. The response headers include X-RateLimit-Remaining and X-RateLimit-Reset.

Machine Readable

These endpoints serve the API reference in formats designed for automated tooling — LLMs, code generators, and API clients.

Changelog

All notable API changes. Versioning follows semantic conventions — minor versions add features, patch versions fix bugs.

v1.21latest2026-08-18
  • new**The pool IP block is now managed over the API.** `GET /pools/:id/ips` returns every address the pool owns with its PTR and current holder, and `POST /pools/:id/ips/apply` assigns, moves or returns them in one batch (`serverId: null` hands an address back). Free — the block was bought with the pool. Addresses only move between the pool and its own servers; there is no path to or from the global pool, because an address from outside the block breaks the per-subnet grouping the isolation is built on. The set is validated whole, so a bad batch writes nothing. Rate limited to 6 calls per minute.
  • new`POST /pools/:id/resize-quote` prices a resize without applying it — new monthly, the difference, and the prorated amount that would be taken now. The same code produces this figure and takes the money on PATCH.
  • new`GET /pools/:id/pricing` returns the unit rates and bounds the pool was SOLD at, which is what a resize is priced from — not today's catalogue.
  • changedPOST /servers accepts `ipCount` for servers built into a pool: how many of the pool's own public addresses the machine gets. Default 1, and 0 is valid — **a server with `ipCount: 0` has no public interface, no default route and therefore no internet at all** (we do not NAT), reachable only on the pool's private network and through the web console. It is what lets a pool run more servers than it has addresses. Linux only; Windows, RouterOS and custom-ISO deploys are refused at order time.
  • changedGET /pools/:id now returns a `policy` block (`vcpuOvercommit`, `mhzPerVcpu`). Read the overcommit ratio from here rather than hardcoding it, or your shrink floor will eventually disagree with the one enforced.
  • new`POST /pools/:id/suspend` and `POST /pools/:id/restore` — power every server in a pool off and back on as one unit, for billing systems that suspend on non-payment. Suspension releases nothing and keeps billing; restore returns each machine to the state it held rather than starting everything. Built for reseller and WHMCS integrations, which previously had no pool equivalent of stopping a server.
  • fixedA capacity pool suspended for non-payment now comes back on its own once the balance covers the renewal, the same way a suspended server always has. Pools shipped with the suspend half and not the restore half, so a customer who topped up stayed suspended indefinitely with every server in the pool powered off.
  • fixed**Reseller keys were charged full retail for capacity pools.** Every other line — plans, add-ons, BGP — prices through the reseller discount; pools did not, so a reseller paid list price and had no margin to resell on. `GET /pools/catalogue` now returns discounted `pricing` with `retailPricing` beside it, and purchase charges the discounted amount. ⚑ The discount is applied to the unit RATES, which are what gets snapshotted onto the pool — so it survives every renewal and every resize, rather than applying once and being clawed back at the first upgrade.
v1.202026-08-16
  • new**Capacity pools** — buy a block of vCPU / RAM / disk / IPs once and build as many servers inside it as the quota allows, each of them FREE with no renewal of its own. `GET /pools/catalogue` lists the products with per-unit prices (sold in steps of 1 vCPU, 2 GB RAM, 100 GB disk, 16 IPs), `POST /pools/quote` prices a selection, `POST /pools` buys and provisions one, `PATCH /pools/:id` resizes, and `DELETE /pools/:id` tears it down. Build into a pool by passing `poolId` on POST /servers.
  • changedPOST /servers accepts `poolId`. A server built into a pool is checked against the pool quota instead of your wallet, is never charged, and never appears on a renewal. Add-ons are refused on pool servers — extra IPs and disk come from the pool, so buying them per-server would charge twice for the same capacity.
v1.192026-08-16
  • newDELETE /servers/:id/addons/:addonId — cancel an add-on. Its price leaves the recurring total from the next renewal; there is no prorated refund for the current cycle. IP add-ons release their addresses immediately (PTRs cleared, router ARP withdrawn), so unconfigure them in the guest OS first. Requires `servers:write`.
  • newTwo new webhook events: `server.addon.purchased` and `server.addon.removed`. The catalog is now 22 events — `server.restored` was already emitted but had been missing from this list.
  • fixedPOST /servers silently dropped some add-ons ordered with the machine. Only extra-ip and ip-class were provisioned, and only one of the two — a `diverse-ip` (SEO IP Pack) or `backup` add-on in the `addons` array was charged and included in the recurring price while nothing was allocated, and the server was then reported as already owning it. All four types now provision, and any add-on that cannot be delivered is refunded and dropped from the recurring price instead of being billed. Ordering the SEO IP Pack when distinct-subnet stock is short now fails the order up front rather than charging for it.
  • fixedAdd-ons ordered via POST /servers are now recorded with their `id`, so they correctly report as `owned` and can no longer be purchased a second time from the dashboard.
  • fixedGET /servers/:id/addons quoted retail prices to reseller keys while POST charged the discounted wholesale price. The listing now applies the same discount the purchase will, and returns `retailPrice` alongside it.
  • fixedThe documented "one add-on per type" rule is now enforced on purchase. Buying a second add-on of a type previously stacked: both were billed every month, a second ip-class overwrote the first block's record, and a second backup add-on bought the same flag twice. Moving between tiers is now DELETE then POST. Responses also carry `ownedByType` so a UI can disable the whole group.
v1.182026-07-07
  • newGET /servers/:id/plan-options + POST /servers/:id/change-plan — resize a server in place. change-plan switches to another plan in the same family with the same disk size, applying a new vCPU/RAM allocation (the server reboots ~1 min; disk is never changed). Only the recurring price changes — no proration on an up-switch, no credit on a down-switch. plan-options lists valid targets. Powers the new Cloud Router (MikroTik CHR) line, where you can move between Router S/M/L. Requires `servers:read` / `servers:write`.
v1.172026-07-03
  • fixedGET /servers/:id/stats could get stuck returning empty `hostname`/`ip`/`toolsRunning`/disk/NIC fields (with `live` metrics still populated) after the backend’s internal hypervisor session expired — it had no way to recover until the process restarted. The session now re-authenticates automatically, so a transient blip self-heals on the next call instead of staying stuck.
v1.162026-06-28
  • newGET /iso-library — lists the admin-curated, ready-to-boot custom ISO images. Deploy from one by passing `os: "custom-iso"` and `osTemplate: "<iso id>"` to POST /servers (the ISO id is validated against the library server-side). Requires the `account:read` scope.
v1.152026-06-22
  • improvedGET /servers/:id/stats now always reports `powerState` for a provisioned server — it is read from the hypervisor independently of the guest agent. The `live` metrics block is `null` (rather than the whole response being empty) when the guest agent (VMware Tools) is quiet: treat that as "running, no telemetry", NOT offline. A stopped server now returns `{ "powerState": "POWERED_OFF", "status": "stopped", "live": null }` instead of an empty body. Backward compatible — no fields removed or renamed.
v1.142026-05-31
  • newServer snapshots — four new endpoints: `GET /servers/:id/snapshots` (list restore points), `POST /servers/:id/snapshots` (create a disk snapshot, max 2 per server), `POST /servers/:id/snapshots/:snapshotId/revert` (roll the disk back to a snapshot — DESTRUCTIVE, requires the `servers:destroy` scope), and `DELETE /servers/:id/snapshots/:snapshotId` (remove a snapshot, non-destructive). Disk-only point-in-time restore points captured via the hypervisor.
v1.132026-05-05
  • newWebhook secret rotation — POST /webhooks/:id/rotate-secret returns a fresh signing secret ONCE. Old secret stops working immediately, so update your receiver before calling. Useful when a secret leaks or for periodic rotation policies.
  • newEight new webhook events: `server.suspended`, `server.unsuspended`, `bgp.session.up`, `bgp.session.down`, `bgp.session.expired`, `domain.registered`, `domain.transferred`, `domain.expiring_soon`. The catalog is now 19 events covering the full server / billing / BGP / domain lifecycle.
  • improvedPer-account cap of 25 active webhook subscriptions. `POST /webhooks` returns 400 with a clear message when the cap is reached.
  • improvedDaily prune cron drops `webhook_deliveries` rows older than 30 days. Subscriptions and failure counters are untouched.
v1.122026-05-04
  • newReverse DNS via API — set, list, and clear PTR records on any IP attached to a server: GET /servers/:id/ips, PUT /servers/:id/ips/:ip/ptr, DELETE /servers/:id/ips/:ip/ptr. Saves a dashboard round-trip for /24 announcers.
  • newBilling read-only — GET /billing/invoices, GET /billing/invoices/:id, GET /billing/invoices/:id/pdf, GET /billing/transactions. All require the existing `billing:read` scope (already in the default set).
  • newWallet read endpoints — GET /wallet (chain-specific wallets), POST /wallet/deposit-address (mint or fetch a TRC-20/ARB-20 deposit address), GET /wallet/transactions. All require `billing:read`.
  • newWebhooks — subscribe to platform events instead of polling. Eleven event types covering servers, invoices, transactions, deposits, and balance thresholds. HMAC-SHA256 signatures on every delivery; automatic retry with exponential backoff (1m, 5m, 15m, 1h, 6h). Two new scopes: `webhooks:read` and `webhooks:write` (opt-in; not in the default scope set).
v1.112026-05-04
  • newRocky Linux 9.7 template is now available at deploy time. Pass `os: "rocky-9.7"` to POST /servers, or list current options via GET /os-templates.
v1.102026-04-29
  • newAlmaLinux 8.10 and AlmaLinux 9.7 templates are now available at deploy time. Pass `os: "almalinux-8.10"` or `os: "almalinux-9.7"` to POST /servers, or list current options via GET /os-templates.
v1.92026-04-24
  • newPOST /servers/:id/reinstall — wipe the disk and rebuild from a new OS template while preserving server ID, label, region, primary IPv4, and attached add-ons. Billing is unchanged. Requires the elevated `servers:destroy` scope because all data on the disk is lost, same blast radius as termination. Accepts an optional `Idempotency-Key` header for 24 h replay safety.
  • improvedScope description for `servers:destroy` now says "terminate or reinstall" to reflect the reinstall action added here. Grant sparingly.
v1.82026-04-21
  • newNew scope `servers:destroy`, required for DELETE /servers/:id. Separated from `servers:write` so a leaked deploy/automation key cannot nuke infrastructure. NOT included in the default scope set — opt in explicitly when minting a key for a destructive integration. Existing keys were grandfathered via `*`.
  • improvedAPI-key scopes are now enforced at the guard layer. Previously the `permissions` column was display-only and any key could call any /v1 endpoint. Calls missing a required scope now return 403.
  • improvedGET /products, /regions, /os-templates now correctly require the `account:read` scope. They were documented as public but the backend always required an API key — docs are now aligned with reality.
v1.72026-04-19
  • newGET /regions — flat list of every deployment region currently offered, with a display-ready name. Keeps your region pickers in sync without hardcoded slugs.
  • newGET /os-templates — every live operating system available at deploy time, flattened per version. The returned id is the exact value to pass as os on POST /servers.
  • newGET /reseller — reseller state of the authenticated account: partner status, brand slug, brand name, and margin percent. Companion to the web /me/reseller endpoint for API-key integrations.
  • newPOST /sso and POST /servers/:id/sso — mint a 5-minute single-use login URL that signs the user straight into the dashboard (or a specific server detail page). Useful for embedding Otwa Cloud inside a billing portal, helpdesk, or WHMCS client area.
  • newPOST /servers/:id/password-reset — rotates the root or Administrator password on a running server and returns the new credential. Applied live through the guest agent, no reboot required.
  • newUbuntu 22.04 LTS and Rocky Linux 10.1 templates are now available at deploy time.
  • improvedPOST /servers now honours an Idempotency-Key header. Retrying the same request with the same key within 24 hours returns the cached result instead of creating a duplicate server — safe for WHMCS and other auto-retry clients.
  • improvedThe os slug format in POST /servers examples has been updated to use a dot between family and version (ubuntu-24.04, rocky-10.1) so that it matches what GET /servers, GET /servers/:id, and GET /os-templates return. Old dotless slugs (ubuntu-2404) still work but will be removed in a future major version.
v1.62026-04-18
  • newGET /public/affiliates/rule — unauthenticated endpoint that exposes the live affiliate program rule: enabled, trigger (verified_signup | first_deposit | every_deposit), rewardType (fixed | percent), and rewardAmount. Lets integrators surface the current offer without duplicating it in their own copy.
  • improvedDELETE /servers/:id — clarified that API-key callers destroy the server immediately. The web dashboard now requires a 6-digit email code or TOTP confirmation, but the API key path intentionally bypasses that step since the key itself is the proof of intent.
  • fixedPOST /servers — body example and description now match the real CreateServerDto. productId, os, and osTemplate are required; label and hostname are optional (auto-generated if omitted); addons take the hash-style IDs returned by GET /servers/:id/addons.
v1.52026-04-17
  • newGET /servers/:id/addons — see every add-on offered by your server's plan, the prorated price to attach it today, and whether the add-on type is already active.
  • newPOST /servers/:id/addons — attach an add-on to a running server. Prorated price is charged to your wallet immediately; the full monthly price is included in future renewals. Guest-OS network configuration for new IPs is your responsibility.
  • newAlmaLinux 10.1 and Windows 11 Pro templates are now available at deploy time.
  • improvedOnly one add-on per type (extra-ip, ip-class, backup) can be active on a server. Orders that include duplicates are rejected with 400.
v1.42026-04-14
  • newGET /servers/:id/stats — live VM metrics: CPU%, memory used/total, disk read/write KB/s, network RX/TX KB/s.
  • newWindows Server 2025 & 2022 support. GET /servers/:id/credentials now returns rdpAddress and Administrator username for Windows VMs.
  • newIP class block add-ons (/25). Additional IPs returned in networking.additionalIps on GET /servers/:id and GET /servers.
  • improvedPOST /servers — hostname and label are now required fields (previously optional).
v1.32026-04-12
  • improvedAll write endpoints now reject requests from keys without the required permission scope (403 instead of silent ignore).
v1.22026-04-03
  • newAuto-provisioning pipeline: VM deployed, network configured, disk resized, OS booted, credentials set — all in ~60–120 seconds.
  • newActivity logging: login, deploy, power actions, reinstall, delete, 2FA changes all recorded.
  • newOrder confirmation and "Server Ready" email notifications.
  • newBilling automation: renewal cron, suspension on payment failure, auto-termination after grace period (configurable in SuperAdmin).
v1.12026-04-02
  • newIP pool management: allocate, release, and track individual IPs with gateway/netmask/CIDR metadata.
  • newCoupon system: create and redeem promotional codes at checkout.
  • improvedGET /servers now includes additionalIps array from IP pool.
v1.02026-04-01
  • newInitial API release: account info, product listing, server CRUD, credentials, power actions, live stats.

Ready to automate?

Create a free account to generate your first API key and start deploying.

Get Started Free