HelipodDocs

Instances

Create, list, inspect and terminate VPS instances, and follow their status.

An instance is one virtual server. Creating one charges the wallet and starts provisioning in the background; you then poll the instance until it is running.

The instance object

{
  "id": "1f757fd5-98d1-490a-81a0-58b5798a5815",
  "projectId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "planId": "4db565ff-782f-46c8-9324-e4670517233f",
  "name": "web-1",
  "hostname": "web-1",
  "status": "running",
  "osType": "ubuntu",
  "osVersion": "24.04",
  "publicIp": "203.0.113.24",
  "cpuCores": 2,
  "ramMb": 2048,
  "diskGb": 40,
  "billingPeriod": 1,
  "priceSnapshot": "64900.00",
  "autoRenew": true,
  "expiresAt": "2026-11-15T08:22:12.000Z",
  "provisionedAt": "2026-10-16T08:24:40.000Z",
  "terminatedAt": null,
  "lastJobType": "vps:create",
  "lastJobStatus": "success",
  "lastJobError": null,
  "lastJobAt": "2026-10-16T08:22:13.000Z",
  "createdAt": "2026-10-16T08:22:12.000Z",
  "updatedAt": "2026-10-16T08:24:40.000Z",
  "plan": {
    "id": "4db565ff-782f-46c8-9324-e4670517233f",
    "name": "VPS S - Jakarta",
    "slug": "vps-s-jkt",
    "priceMonthly": "64900.00",
    "yearlyDiscount": 15,
    "providerCode": 2
  }
}
FieldTypeDescription
idstringInstance id, used in every /vps/:id route.
projectIdstring | nullProject the server belongs to.
planId, planstring, objectCurrent plan. plan.providerCode is an opaque number.
namestringName you gave it.
hostnamestringLowercase hostname derived from name.
statusstringSee Statuses.
osType, osVersionstringInstalled operating system.
publicIpstringPublic IPv4 address. It is the text "pending" until provisioning has finished.
cpuCores, ramMb, diskGbnumberCurrent size.
billingPeriodnumberLength of the paid period in months: 1, 3, 6 or 12.
priceSnapshotstringPrice paid for one period, in Rupiah. A renewal for the same period length charges this amount again.
autoRenewbooleanWhether the server renews itself from the wallet before it expires.
expiresAtstringEnd of the paid period (ISO 8601).
provisionedAtstring | nullWhen the server first came up.
terminatedAtstring | nullWhen it was terminated.
lastJobTypestring | nullLast background job, for example vps:create, vps:start, vps:reinstall, vps:renew.
lastJobStatusstring | nullpending while it runs, then success or error.
lastJobErrorstring | nullReason the last job failed.
createdAt, updatedAtstringTimestamps (ISO 8601).

Responses can contain more fields than the ones listed here. Undocumented fields are internal: they can change or disappear without notice, so do not build on them.

Statuses

StatusMeaningBusy
pendingBeing created.yes
provisioningBeing created, or busy with a start, reinstall, upgrade or snapshot restore.yes
runningUp.no
stoppingShutting down.yes
stoppedPowered off.no
restartingRebooting.yes
errorThe create or terminate failed. lastJobError says why.no
terminatedDeleted. Kept in lists for your records.no

While a server is busy (or a backup, restore or snapshot is in progress on it), calls that change it answer 409 VPS_BUSY. Billing calls (renew, auto-renew) and reads are never blocked.

When any other job fails, the server does not go to error: a failed start, stop, restart, reinstall, snapshot rollback or upgrade puts back the status the server had before the call, and a failed renewal keeps the status as it is. lastJobStatus becomes error and lastJobError says why. Jobs that are not power actions (reset password, firewall, snapshots, renewals) never change the power status, so a stopped server stays stopped.

Polling

Poll GET /vps/:id every 5 to 10 seconds after an action until the status is running, stopped, terminated or error. Creating a server usually takes a few minutes; power actions take seconds to a minute. Check lastJobStatus and lastJobError to see how the last job ended.

async function waitForVps(id, key, { timeoutMs = 10 * 60_000 } = {}) {
  const settled = ['running', 'stopped', 'terminated', 'error'];
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await fetch(`https://api.helipod.io/api/vps/${id}`, {
      headers: { Authorization: `Bearer ${key}` },
    });
    const body = await res.json();
    if (!body.success) throw new Error(`${body.code}: ${body.error}`);
    if (settled.includes(body.data.status)) return body.data;
    await new Promise((r) => setTimeout(r, 5000));
  }
  throw new Error('Timed out waiting for the VPS');
}

List instances

GET/vps
List the servers the key can reach, newest first.

Prop

Type

curl "https://api.helipod.io/api/vps?status=running" \
  -H "Authorization: Bearer hel_your_key"
{
  "success": true,
  "data": [
    {
      "id": "1f757fd5-98d1-490a-81a0-58b5798a5815",
      "projectId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "name": "web-1",
      "status": "running",
      "publicIp": "203.0.113.24",
      "cpuCores": 2,
      "ramMb": 2048,
      "diskGb": 40,
      "osType": "ubuntu",
      "osVersion": "24.04",
      "billingPeriod": 1,
      "priceSnapshot": "64900.00",
      "autoRenew": true,
      "expiresAt": "2026-11-15T08:22:12.000Z",
      "createdAt": "2026-10-16T08:22:12.000Z",
      "plan": {
        "id": "4db565ff-782f-46c8-9324-e4670517233f",
        "name": "VPS S - Jakarta",
        "slug": "vps-s-jkt",
        "priceMonthly": "64900.00",
        "yearlyDiscount": 15,
        "providerCode": 2
      }
    }
  ]
}

Terminated servers stay in the list with status: "terminated". Filter with ?status= to hide them. A projectId you do not own returns an empty list.

Get an instance

GET/vps/:id
Return one server.
curl https://api.helipod.io/api/vps/1f757fd5-98d1-490a-81a0-58b5798a5815 \
  -H "Authorization: Bearer hel_your_key"

Returns the instance object. An unknown id, another account's server, or a server outside the key's project answers 404 INSTANCE_NOT_FOUND.

Rename an instance

PATCH/vps/:id
Change the server's name. Nothing on the server itself changes.

Prop

Type

curl -X PATCH https://api.helipod.io/api/vps/1f757fd5-98d1-490a-81a0-58b5798a5815 \
  -H "Authorization: Bearer hel_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "web-2" }'
{ "success": true, "data": { "id": "1f757fd5-98d1-490a-81a0-58b5798a5815", "name": "web-2" } }

A name that breaks the rule, or a terminated server, answers 400 REQUEST_ERROR.

Metrics

GET/vps/:id/metrics
CPU, memory, disk and network usage of the server.

The answer has one of two shapes, told apart by source.

Time series for the chosen range: 1h (default), 6h, 24h, 7d or 30d.

curl "https://api.helipod.io/api/vps/1f757fd5-98d1-490a-81a0-58b5798a5815/metrics?range=24h" \
  -H "Authorization: Bearer hel_your_key"
{
  "success": true,
  "data": {
    "source": "monitoring",
    "range": "24h",
    "periodSeconds": 300,
    "series": {
      "cpuPercent": [[1760600000000, 12.5], [1760600300000, 9.8]],
      "memoryPercent": [[1760600000000, 41.2], [1760600300000, 40.9]],
      "networkOutBytesPerSec": [[1760600000000, 5120], [1760600300000, 4210]]
    },
    "summary": {
      "cpuCores": 2, "memoryGb": 4, "diskGb": 60, "bandwidthMbps": 30,
      "traffic": { "usedBytes": 1073741824, "totalBytes": 536870912000, "remainingBytes": 535797170176, "overflowBytes": 0, "periodStart": "2026-10-01T00:00:00Z", "periodEnd": "2026-11-01T00:00:00Z" }
    }
  }
}

Each point is [timestamp in milliseconds, value]. The series that can appear are cpuPercent, memoryPercent, memoryUsedBytes, diskPercent, load1, load5, load15, networkInBytesPerSec, networkOutBytesPerSec, packetsInPerSec, packetsOutPerSec, bandwidthOutPercent and tcpConnections. summary.traffic is the monthly traffic allowance, when the plan has one.

A server that offers neither answers 409 FEATURE_NOT_AVAILABLE.

Create an instance

POST/vpscharges your wallet
Charge the wallet and start provisioning a new server.

Prop

Type

Password rules. 12 to 30 characters from letters, numbers and the symbols ! @ # $ % ^ *, with at least one uppercase letter, one lowercase letter, one number and one of those symbols. No spaces. The same rule applies to reinstall and password reset.

curl -X POST https://api.helipod.io/api/vps \
  -H "Authorization: Bearer hel_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "planId": "4db565ff-782f-46c8-9324-e4670517233f",
    "projectId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "name": "web-1",
    "osType": "ubuntu",
    "osVersion": "24.04",
    "rootPassword": "Str0ng!Passw0rd",
    "billingPeriod": "monthly"
  }'

Response 201

{
  "success": true,
  "data": {
    "instance": {
      "id": "1f757fd5-98d1-490a-81a0-58b5798a5815",
      "projectId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
      "planId": "4db565ff-782f-46c8-9324-e4670517233f",
      "name": "web-1",
      "hostname": "web-1",
      "status": "provisioning",
      "osType": "ubuntu",
      "osVersion": "24.04",
      "publicIp": "pending",
      "cpuCores": 2,
      "ramMb": 2048,
      "diskGb": 40,
      "billingPeriod": 1,
      "priceSnapshot": "64900.00",
      "autoRenew": true,
      "expiresAt": "2026-11-15T08:22:12.000Z",
      "lastJobType": "vps:create",
      "lastJobStatus": "pending",
      "createdAt": "2026-10-16T08:22:12.000Z"
    },
    "password": "Str0ng!Passw0rd"
  }
}

What happens

  1. The request is checked: project, plan, operating system, name, password, the billing period, the account's server limit (10 servers by default; terminated servers do not count) and the wallet balance. A failed check charges nothing.
  2. The wallet is charged for the whole period and the server is stored with status: "provisioning" and publicIp: "pending". expiresAt is set 30, 90, 180 or 365 days ahead.
  3. Provisioning runs in the background. Poll GET /vps/:id until status is running; publicIp is then filled in. The image's default firewall rules are recorded at that point (see Firewall).
  4. If provisioning fails, the status becomes error, lastJobError explains why, and the charge is refunded to the wallet. Terminate the failed server and try again.

Retrying a create is not safe: there is no idempotency key, and each successful call charges the wallet. If a create timed out, list your servers first and check whether it was created.

Errors

StatusCodeWhen
422VALIDATIONA required field is missing, or name or rootPassword is out of range.
400PROJECT_REQUIREDprojectId is empty.
404PROJECT_NOT_FOUNDThe project does not exist, is not yours, or is outside the key's project.
400INVALID_BILLING_PERIODbillingPeriod is not monthly, 3month, 6month or yearly.
402INSUFFICIENT_BALANCEThe wallet cannot cover the period. error gives the required and available amounts.
400REQUEST_ERRORInvalid name or password, unknown plan or operating system, or server limit reached. Read error.

Terminate an instance

DELETE/vps/:id
Delete a server for good.
curl -X DELETE https://api.helipod.io/api/vps/1f757fd5-98d1-490a-81a0-58b5798a5815 \
  -H "Authorization: Bearer hel_your_key"
{ "success": true, "data": { "success": true } }

Allowed when the server is running, stopped or error. The status changes to terminated immediately and the server and its disk are deleted in the background. This cannot be undone and the unused part of the period is not refunded. Copy off anything you need first, for example with a backup download.

Returns 409 VPS_BUSY while the server is busy, and 400 REQUEST_ERROR when it is already terminated or in a status that cannot be terminated.

On this page