Reseller API

Reseller API v1.0

Programmatically create, manage, and monitor VPS instances for your customers. Automate your entire hosting business — from instance creation with cloud images or custom ISOs, to billing, disk management, IP allocation, and full server lifecycle control.

Updated recently 40 min read Production Ready

Introduction

VPS Creation
Cloud images (instant SSH) or custom ISOs — 4 performance tiers.
Wallet Billing
Automated billing from your Purple Wallet. Renew with a single call.
Full VM Control
Start, stop, reboot, suspend, resume — complete lifecycle management.
Real-Time Stats
CPU, memory, disk, and per-interface network throughput.
Advanced Features
Snapshots, extra disks, ISO mounting, VNC/RDP access, cloud-init.
IP Management
Multi-IP per VM, public IP allocation, NAT & bridged modes.
ISO Upload
Chunked ISO upload API to install any OS via VNC.
Extra Disks
Add, resize, and remove additional data volumes per VPS.
Service Aliases
Tag services with customer names for easy identification.
Region Selection
Choose which data centre region to deploy to — filtered by tier availability.

How it works — Your API call hits shulker.in/api/reseller-v1/, which validates your reseller_token, deducts billing from your Purple Wallet, then proxies the request to the appropriate node backend running on the physical host. All VM actions, ISO uploads, disk operations, and IP management pass through this single entry point using the action parameter.

Base URL & Authentication

Base URL
https://shulker.in/api/reseller-v1/
Authentication — add to every request
# Query parameter (GET requests)
?reseller_token=YOUR_RESELLER_TOKEN_HERE

# JSON body (POST requests)
{ "reseller_token": "YOUR_RESELLER_TOKEN_HERE" }

🔐 Token Security — Your reseller_token is found in the Reseller Dashboard. Store it in environment variables — never hard-code it. Tokens are scoped to verified reseller accounts. Unverified accounts receive 403.

VPS Pricing Tiers

Four performance tiers — each charges per unit of RAM, CPU, and Disk.

TierRAM / GBCPU / CoreDisk / GBNode Type
eco₹26₹16₹0.26VPS-Eco
std₹37₹26₹0.37VPS-Std
perf₹53₹42₹0.53VPS-Perf
pwr₹74₹58₹0.79VPS-Pwr

Formula — Monthly price = (RAM_GB × RAM_PRICE) + (CPU_CORES × CPU_PRICE) + (DISK_GB × DISK_PRICE). For example: std tier, 4 GB RAM, 2 cores, 50 GB disk = (4×37) + (2×26) + (50×0.37) = ₹148 + ₹52 + ₹18.50 = ₹218.50/month.

Stock & Tiers Management

Check available VPS tiers, their pricing, and real-time node capacity to ensure you can create instances before attempting to bill.

List Available Tiers

Retrieve all available VPS tiers with their pricing structure and minimum/maximum specifications.

GET — list_tiers
curl "https://shulker.in/api/reseller-v1/?action=list_tiers&reseller_token=TOKEN"
                
                {
                    "success": true,
                    "message": "Available tiers retrieved",
                    "data": {
                        "tiers": {
                            "eco": {
                                "tier": "eco",
                                "node_type": "VPS-Eco",
                                "prices": {
                                    "ram_price_gb": 26,
                                    "cpu_price_core": 16,
                                    "disk_price_gb": 0.26
                                },
                                "min_specs": { "ram_gb": 1, "cpu_cores": 1, "disk_gb": 10 },
                                "max_specs": { "ram_gb": 96, "cpu_cores": 32, "disk_gb": 1000 }
                            }
                            // ... std, perf, pwr similarly
                        }
                    }
                }

Check Stock Availability

Check real-time capacity across all nodes for a specific tier, or get a full stock report for all tiers. This is crucial before creating instances to avoid 503 No available nodes errors.

GET — check_stock (all tiers)
curl "https://shulker.in/api/reseller-v1/?action=check_stock&reseller_token=TOKEN"
                
                {
                    "success": true,
                    "data": {
                        "stock": {
                            "eco": {
                                "tier": "eco",
                                "node_type": "VPS-Eco",
                                "total_capacity": 50,
                                "total_used": 38,
                                "total_available": 12,
                                "has_stock": true,
                                "nodes": [
                                    {
                                        "name": "node-ec-01",
                                        "capacity": 25,
                                        "used": 20,
                                        "available": 5,
                                        "ram_gb": 128,
                                        "cpu_cores": 64,
                                        "disk_gb": 4000
                                    }
                                    // ... more nodes
                                ]
                            }
                            // ... std, perf, pwr similarly
                        }
                    }
                }
GET — check_stock (specific tier)
curl "https://shulker.in/api/reseller-v1/?action=check_stock&reseller_token=TOKEN&tier=std"
                
                {
                    "success": true,
                    "data": {
                        "tier": "std",
                        "node_type": "VPS-Std",
                        "total_capacity": 30,
                        "total_used": 28,
                        "total_available": 2,
                        "has_stock": true,
                        "nodes": [
                            {
                                "name": "node-std-01",
                                "capacity": 15,
                                "used": 14,
                                "available_slots": 1,
                                "total_ram_gb": 256,
                                "used_ram_gb": 212,
                                "available_ram_gb": 44,
                                "total_cpu_cores": 128,
                                "used_cpu_cores": 116,
                                "available_cpu_cores": 12,
                                "total_disk_gb": 8000,
                                "used_disk_gb": 7200,
                                "available_disk_gb": 800,
                                "can_create_vm": true
                            }
                        ]
                    }
                }

💡 Smart Provisioning — Always call check_stock with the desired tier before creating an instance. The response includes both slot capacity and resource availability (RAM, CPU, Disk) per node. A node might have available slots but insufficient resources for your requested specs — the can_create_vm field indicates if any VM can be created with default specs (1GB RAM, 1 CPU, 10GB disk).

⚠️ Resource-aware provisioning — While the API checks for resource availability, you should still validate your requested specs (RAM, CPU, Disk) against the node's available resources. The available_ram_gb, available_cpu_cores, and available_disk_gb fields help you determine if the node can accommodate your VM's specific resource requirements.

Typical Workflow

check_stock
Verify capacity
Calculate cost
Based on pricing
Check wallet
Sufficient balance?
create_instance
Provision VM
Full provisioning flow in bash
#!/bin/bash
                TOKEN="YOUR_RESELLER_TOKEN"
                TIER="std"
                REGION="Mumbai, India"
                RAM_GB=4
                CPU_CORES=2
                DISK_GB=50
                
                BASE_URL="https://shulker.in/api/reseller-v1/"
                
                # 1. Check stock
                echo "📊 Checking stock for tier: $TIER"
                RESPONSE=$(curl -s "${BASE_URL}?action=check_stock&reseller_token=${TOKEN}&tier=${TIER}")
                STOCK=$(echo $RESPONSE | jq -r '.data.has_stock')
                AVAILABLE=$(echo $RESPONSE | jq -r '.data.total_available')
                
                if [ "$STOCK" != "true" ] || [ "$AVAILABLE" -eq 0 ]; then
                    echo "❌ No stock available for tier $TIER"
                    exit 1
                fi
                echo "✅ $AVAILABLE slots available"
                
                # 2. Create VM
                echo "🚀 Creating VM..."
                RESULT=$(curl -s -X POST "${BASE_URL}?action=create_instance&reseller_token=${TOKEN}" \
                  -H "Content-Type: application/json" \
                  -d "{
                    \"type\": \"vps\",
                    \"tier\": \"$TIER\",
                    \"region\": \"$REGION\",
                    \"ram_gb\": $RAM_GB,
                    \"cpu_cores\": $CPU_CORES,
                    \"disk_gb\": $DISK_GB,
                    \"os\": \"ubuntu-2404\"
                  }")
                
                echo $RESULT | jq '.'

Regions

Shulker nodes are spread across multiple geographic locations. Use list_regions to discover available regions and filter by tier, then pass a region when creating an instance to control where the VM is provisioned.

List Regions

Returns all locations that have at least one public node. The optional tier parameter filters results to only locations that have nodes supporting that tier.

GET — list_regions (all)
curl "https://shulker.in/api/reseller-v1/?action=list_regions&reseller_token=TOKEN"
                
                {
                    "success": true,
                    "message": "Regions retrieved",
                    "data": {
                        "regions": [
                            {
                                "location":        "Mumbai, India",
                                "available_tiers": ["eco", "std", "perf"],
                                "has_stock":       true
                            },
                            {
                                "location":        "Delhi, India",
                                "available_tiers": ["eco", "std"],
                                "has_stock":       true
                            }
                        ],
                        "count": 2
                    }
                }
GET — list_regions (filtered by tier)
curl "https://shulker.in/api/reseller-v1/?action=list_regions&reseller_token=TOKEN&tier=perf"
                
                {
                    "success": true,
                    "data": {
                        "regions": [
                            {
                                "location":        "Mumbai, India",
                                "available_tiers": ["eco", "std", "perf"],
                                "has_stock":       true
                            }
                        ],
                        "count": 1
                    }
                }
ParameterTypeRequiredDescription
tierstringoptionalFilter to regions supporting a specific tier: eco, std, perf, or pwr

Workflow — Call list_regions with your target tier before showing a region picker to your customer. Only show regions where has_stock is true and the desired tier appears in available_tiers. Then pass the selected location value as the region parameter in create_instance.

Omitting region — If region is omitted from create_instance, the API selects the least-loaded public node across all regions for the chosen tier. For production deployments, always specify a region to give your customers predictable latency.

OS Catalog

The API supports two boot modes. Pass the OS Key as the os parameter when creating an instance.

☁️ Cloud Image
Instant Boot
Pre-installed OS image. Boots in seconds. SSH/root access auto-configured via cloud-init. No VNC install required.
💿 ISO Install
Manual Install
Upload your own ISO. Connect via VNC to run the installer — just like a physical machine. Required for Windows.
OS KeyLabelBoot ModeFamily
ubuntu-2404Ubuntu 24.04 LTS (Noble)☁ cloudubuntu
ubuntu-2204Ubuntu 22.04 LTS (Jammy)☁ cloudubuntu
ubuntu-2004Ubuntu 20.04 LTS (Focal)☁ cloudubuntu
debian-12Debian 12 (Bookworm)☁ clouddebian
debian-11Debian 11 (Bullseye)☁ clouddebian
fedora-40Fedora 40☁ cloudfedora
centos-9CentOS Stream 9☁ cloudcentos
almalinux-9AlmaLinux 9☁ cloudalmalinux
rockylinux-9Rocky Linux 9☁ cloudrockylinux
opensuse-15openSUSE Leap 15.6☁ cloudopensuse
arch-linuxArch Linux (latest)☁ cloudarch
alpine-319Alpine Linux 3.19☁ cloudalpine
windows-11Windows 11 (ISO)💿 isowindows
iso-customCustom ISO (any OS)💿 isocustom

First-time cloud image download — The first time a cloud OS is used on a node, the image is downloaded from the upstream source (Ubuntu Cloud Images, Debian Cloud, etc.). Subsequent VMs using the same OS key reuse the cached image and provision instantly.

You can also poll the image cache status before creating a VM:

Check image cache status
curl "https://shulker.in/api/reseller-v1/?action=os_image_status&reseller_token=TOKEN&os=ubuntu-2404"

# Response
{
    "success": true,
    "data": {
        "os_key": "ubuntu-2404",
        "cached": true,
        "size_bytes": 616792064,
        "size_human": "588.19 MB"
    }
}

Create Instance

POST

Create a new VPS. Cost is deducted from your Purple Wallet immediately. Supports both cloud images and ISO installs.

Endpoint
https://shulker.in/api/reseller-v1/?action=create_instance&reseller_token=YOUR_TOKEN
ParameterTypeRequiredDescription
typestringrequiredMust be vps
tierstringrequiredeco, std, perf, or pwr
ram_gbintegerrequiredRAM in GB (1–96)
cpu_coresintegerrequiredvCPU cores (1–32)
disk_gbintegeroptionalRoot disk in GB (default: 20, max: 1000)
osstringoptionalOS key from the catalog (default: ubuntu-2404)
isostringoptionalISO filename for ISO-type OS (e.g. windows11.iso). Must already be uploaded.
regionstringoptionalLocation string from list_regions (e.g. Mumbai, India). Omit to auto-select the best available node.
ssh_keystringoptionalPublic SSH key to inject (cloud images only)

Example A — Cloud Linux VM (Ubuntu 24.04)

cURL — Cloud VM
curl -X POST "https://shulker.in/api/reseller-v1/?action=create_instance&reseller_token=TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "vps",
    "tier": "std",
    "region": "Mumbai, India",
    "ram_gb": 4,
    "cpu_cores": 2,
    "disk_gb": 50,
    "os": "ubuntu-2404",
    "ssh_key": "ssh-rsa AAAAB3NzaC1yc2E..."
  }'

Example B — Windows 11 VM (ISO)

cURL — Windows ISO VM
curl -X POST "https://shulker.in/api/reseller-v1/?action=create_instance&reseller_token=TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "vps",
    "tier": "perf",
    "ram_gb": 8,
    "cpu_cores": 4,
    "disk_gb": 100,
    "os": "windows-11",
    "iso": "Win11_23H2_x64.iso"
  }'

# Note: upload the ISO first via action=iso_upload_chunk before creating the VM
Success Response
{
    "success": true,
    "message": "VPS instance created successfully",
    "data": {
        "service_id": 12345,
        "service_name": "aB3dE5fG7hI9jK1lM3nO5pQ",
        "tier": "std",
        "region": "Mumbai, India",
        "cpu_cores": 2,
        "ram_gb": 4,
        "disk_gb": 50,
        "os": "ubuntu-2404",
        "boot_mode": "cloud",       // "cloud" or "iso"
        "ip": "103.x.x.x",
        "root_password": "a1b2c3d4e5f6",  // cloud VMs only
        "total_cost": 218.50,
        "wallet_balance_before": 1000.00,
        "wallet_balance_after": 781.50,
        "valid_until": "2025-09-07 12:00:00",
        "note": "Cloud VM ready — start it, SSH in ~15s at 103.x.x.x"
    }
}

Cloud OS Boot Flow

When you create and start a cloud-image VM, this is the exact sequence of events on the backend.

create_instance
Disk provisioned
start
QEMU launches
cloud-init
~10–15 seconds
SSH ready
root@ip
  • 1
    Disk created — A QCOW2 disk is created as a copy-on-write overlay on top of the cached cloud base image. Only changed blocks are written — the base image is never modified and is shared across all VMs using that OS.
  • 2
    cloud-init ISO generated — A small ISO image is built containing: your root password (hashed), any SSH public key you provided, a static IP/gateway config for every common interface name (eth0, ens3, enp1s0, etc.), and a hostname matching the VM name.
  • 3
    QEMU launches — The VM boots with both the OS disk and cloud-init ISO attached. The OS detects the ISO as a NoCloud datasource and applies the configuration on first boot.
  • 4
    cloud-init configures the VM — Sets the hostname, sets the root password, injects SSH keys, enables root SSH login and password authentication, enables qemu-guest-agent, and configures the static IP.
  • 5
    SSH ready in ~15 seconds — Connect as root@IP_ADDRESS using the root_password from the creation response, or your SSH key.
Connect after boot
# 1. Create the VM
SERVICE_ID=12345
IP="103.x.x.x"
PASSWORD="a1b2c3d4e5f6"

# 2. Start it
curl "https://shulker.in/api/reseller-v1/?action=start&reseller_token=TOKEN&service_id=${SERVICE_ID}"

# 3. Wait ~15 seconds, then SSH in
ssh root@${IP}
# Enter password when prompted, or use your SSH key

Regenerate cloud-init — If you need to change the root password or SSH key after creation, use action=regen_cloudinit. This rebuilds the configuration ISO. Restart the VM for changes to take effect — they will not apply to an already-booted OS (cloud-init runs only once per instance ID).

ISO Upload

Upload ISO files to the node using a chunked multipart upload. This supports large files (multi-GB Windows ISOs, etc.) by splitting them into smaller pieces.

Endpoint
https://shulker.in/api/reseller-v1/?action=iso_upload_chunk&reseller_token=YOUR_TOKEN
Content-Type: multipart/form-data
Form FieldTypeRequiredDescription
filenamestringrequiredFull ISO filename including .iso extension (e.g. Win11_x64.iso)
chunk_indexintegerrequiredZero-based index of this chunk (0, 1, 2, …)
total_chunksintegerrequiredTotal number of chunks in this upload
upload_idstringrequiredUnique identifier for this upload session (alphanumeric, no spaces)
filebinaryrequiredRaw binary data for this chunk

Once the final chunk is received (when received == total_chunks), the server automatically assembles all chunks into the complete ISO file. Previous partial uploads from the same upload_id are cleaned up.

0
1
2
3
4
5
6
7
8
N

■ sent   ■ pending   ■ final chunk → triggers assembly

JavaScript Upload Example

JavaScript — chunked ISO upload
const uploadISO = async (file, token) => {
  const CHUNK_SIZE = 5 * 1024 * 1024; // 5 MB per chunk
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
  const uploadId = Date.now().toString(36) + Math.random().toString(36).slice(2);
  const BASE = "https://shulker.in/api/reseller-v1/";

  for (let i = 0; i < totalChunks; i++) {
    const start  = i * CHUNK_SIZE;
    const chunk  = file.slice(start, start + CHUNK_SIZE);

    const form = new FormData();
    form.append("filename",     file.name);
    form.append("chunk_index",  i);
    form.append("total_chunks", totalChunks);
    form.append("upload_id",    uploadId);
    form.append("file",         chunk, file.name);

    const res = await fetch(
      `${BASE}?action=iso_upload_chunk&reseller_token=${token}`,
      { method: "POST", body: form }
    );
    const json = await res.json();

    console.log(`Chunk ${i+1}/${totalChunks}:`, json.message);

    if (json.data?.assembled) {
      console.log("✅ ISO ready:", json.data.filename);
      return json.data.filename;
    }
  }
};

// Usage
const isoInput = document.getElementById("iso-file");
uploadISO(isoInput.files[0], "YOUR_RESELLER_TOKEN");

bash / curl Example

bash — split and upload
#!/bin/bash
ISO_FILE="Win11_x64.iso"
TOKEN="YOUR_RESELLER_TOKEN"
CHUNK_SIZE="5m"             # 5 MB chunks
UPLOAD_ID="upload_$(date +%s)"
BASE_URL="https://shulker.in/api/reseller-v1/"
TMP_DIR="/tmp/iso_chunks"

mkdir -p $TMP_DIR

# Split the ISO into chunks
split -b $CHUNK_SIZE "$ISO_FILE" "$TMP_DIR/chunk_" --numeric-suffixes=0 -a 6

CHUNKS=($TMP_DIR/chunk_*)
TOTAL=${#CHUNKS[@]}

for i in "${!CHUNKS[@]}"; do
  curl -X POST "${BASE_URL}?action=iso_upload_chunk&reseller_token=${TOKEN}" \
    -F "filename=$(basename $ISO_FILE)" \
    -F "chunk_index=${i}" \
    -F "total_chunks=${TOTAL}" \
    -F "upload_id=${UPLOAD_ID}" \
    -F "file=@${CHUNKS[$i]}"
  echo " ← chunk $((i+1))/$TOTAL"
done

rm -rf $TMP_DIR
echo "✅ ISO upload complete: $(basename $ISO_FILE)"
Chunk response (mid-upload)
{ "success": true, "message": "Chunk received",
  "data": { "chunk": 5, "received": 6, "total": 50 } }

// Final chunk response — ISO is ready
{ "success": true, "message": "ISO upload complete",
  "data": { "filename": "Win11_x64.iso", "size": 5909970944, "assembled": true } }

ISO Management

List, mount, unmount, and delete ISO files on the node.

List ISOs

GET — list_isos
curl "https://shulker.in/api/reseller-v1/?action=list_isos&reseller_token=TOKEN"

# Response
{
    "success": true,
    "data": {
        "isos": [
            {
                "name":       "Win11_x64.iso",
                "size_bytes": 5909970944,
                "size_human": "5.50 GB",
                "modified":   "2025-08-01 10:22:11"
            }
        ]
    }
}

Mount / Change ISO

Attach a different ISO to a service, or eject the current one. Works while the VM is running or stopped. If running, the virtual CD-ROM is hot-swapped via the QEMU monitor.

POST — change_iso
# Mount an ISO
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "change_iso",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "iso": "Win11_x64.iso"
  }'

# Eject ISO (leave iso field empty or omit it)
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "change_iso",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "iso": ""
  }'

Delete ISO

GET — delete_iso
curl "https://shulker.in/api/reseller-v1/?action=delete_iso&reseller_token=TOKEN&iso=Win11_x64.iso"

⚠️ Unmount before deleting — If an ISO is currently mounted on a running VM, eject it first with change_iso (empty iso value) before deleting the file. Deleting a mounted ISO while the VM is running can cause read errors on the virtual CD-ROM.

List Services

GET

Get all services on your reseller account, with optional status filtering.

Endpoint
https://shulker.in/api/reseller-v1/?action=list_services&reseller_token=TOKEN&status=all
ParameterValuesDescription
statusall · active · expiredFilter returned services by expiry status
Success Response
{
    "success": true,
    "data": {
        "services": [{
            "id": 12345,
            "service_name":     "aB3dE5fG7hI9jK1lM3nO5pQ",
            "service_alias":    "customer-site-01",
            "service_type":     "vps",
            "service_tier":     "std",
            "region":           "Mumbai, India",
            "os":               "ubuntu-2404",
            "boot_mode":        "cloud",
            "cpu_cores": 2,      "ram_gb": 4,  "disk_gb": 50,
            "ip":               "103.x.x.x",
            "service_valid_until": "2025-09-07 12:00:00",
            "is_expired":       false,
            "days_remaining":   36
        }],
        "count": 1
    }
}

Renew Service

POST

Extend a service by one month. Works on both active and recently-expired services.

cURL
curl -X POST "https://shulker.in/api/reseller-v1/?action=renew_service&reseller_token=TOKEN&service_id=12345"
Success Response
{
    "success": true,
    "data": {
        "service_id":     12345,
        "previous_expiry": "2025-09-07 12:00:00",
        "new_expiry":      "2025-10-07 12:00:00",
        "renewal_cost":   218.50,
        "wallet_balance_before": 781.50,
        "wallet_balance_after":  563.00,
        "was_expired": false
    }
}

Update Service Alias

POST

Attach a human-readable label to a service — useful for mapping internal customer IDs, project names, or domains.

cURL
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "update_service_alias",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "alias": "customer-website-production"
  }'

VM Lifecycle

All lifecycle actions take service_id as a parameter. Expired services return 403 — renew first.

start
Start a stopped VM. Attaches to correct bridge, starts websockify for VNC.
stop
Sends SIGTERM for graceful shutdown. Falls back to SIGKILL after 3 seconds.
force_stop
Immediate SIGKILL — use when graceful stop hangs or VM is unresponsive.
reboot
Sends a system reset signal via QEMU monitor. Equivalent to pressing reset.
info
Full VM metadata including IP, specs, status, running state, and VNC port.
server_status
Lightweight check — running state, PID, basic resource summary.
Start, stop, reboot, force_stop
# Start
curl "https://shulker.in/api/reseller-v1/?action=start&reseller_token=TOKEN&service_id=12345"

# Graceful stop
curl "https://shulker.in/api/reseller-v1/?action=stop&reseller_token=TOKEN&service_id=12345"

# Force kill
curl "https://shulker.in/api/reseller-v1/?action=force_stop&reseller_token=TOKEN&service_id=12345"

# Reboot
curl "https://shulker.in/api/reseller-v1/?action=reboot&reseller_token=TOKEN&service_id=12345"
Start response
{
    "success": true,
    "message": "Booting cloud image — SSH ready in ~15s at 103.x.x.x",
    "data": {
        "pid":      28741,
        "vnc_port": 5943,
        "vnc_ip":   "in.shulker.in:5943",
        "rdp_address": null   // non-null for Windows VMs
    }
}

⚠️ Expired services — VM control actions return 403 if the service has expired. The VM is automatically stopped at expiry. Renew first with renew_service, then start again.

Suspend & Resume

Pause and unpause a running VM without a full shutdown. The VM process is frozen in memory — all state is preserved.

Suspend & Resume
# Suspend (freeze VM process — RAM preserved, no CPU used)
curl "https://shulker.in/api/reseller-v1/?action=suspend&reseller_token=TOKEN&service_id=12345"

# Resume (unfreeze — VM continues exactly where it left off)
curl "https://shulker.in/api/reseller-v1/?action=resume&reseller_token=TOKEN&service_id=12345"

Suspend vs Stop — suspend sends SIGSTOP to the QEMU process — the VM is frozen but stays in RAM, consuming no CPU. Network connections will time out. stop gracefully shuts down the OS — the VM is fully powered off and RAM is freed. Use suspend for short maintenance windows; use stop for longer pauses.

Stats & Monitoring

Real-time resource metrics and console log access.

Resource Stats

GET — stats
curl "https://shulker.in/api/reseller-v1/?action=stats&reseller_token=TOKEN&service_id=12345"
Stats response
{
    "success": true,
    "data": {
        "running":          true,
        "pid":              28741,

        // CPU: host-process %CPU (not in-guest CPU usage)
        "cpu_usage_pct":    12.3,
        "cpu_alloc_cores":  2,

        // RAM: RSS of the QEMU process in KB
        "ram_used_kb":      3876224,
        "ram_alloc_mb":     4096,

        // Disk: actual QCOW2 file size on host (compressed)
        "disk_used_bytes":  2684354560,
        "disk_used_human":  "2.50 GB",
        "disk_alloc_gb":    50,

        // Network: measured over a 500ms window on the TAP interface
        "inbound_kbps":     4820,
        "outbound_kbps":    1640,
        "inbound_mbps":     4.82,
        "outbound_mbps":    1.64,

        "bandwidth_mbps":   0   // 0 = no limit set
    }
}

Serial / Console Log

Access the VM's serial console output — useful for debugging boot failures or cloud-init issues without VNC.

GET — serial_log
# Last 100 lines (default)
curl "https://shulker.in/api/reseller-v1/?action=serial_log&reseller_token=TOKEN&service_id=12345"

# Last 500 lines (max 2000)
curl "https://shulker.in/api/reseller-v1/?action=serial_log&reseller_token=TOKEN&service_id=12345&lines=500"

VNC & RDP Access

All VMs expose VNC. Windows VMs additionally get RDP forwarding when running in NAT mode.

VNC

GET — vnc_info
curl "https://shulker.in/api/reseller-v1/?action=vnc_info&reseller_token=TOKEN&service_id=12345"

{
    "success": true,
    "data": {
        "vnc_port":    5943,            // raw VNC TCP port
        "vnc_ws_port": 6043,            // WebSocket port (vnc_port + 100)
        "vnc_ip":      "in.shulker.in:5943",
        "ip":          "103.x.x.x",
        "running":     true
    }
}
VNC Client (native)
in.shulker.in:5943
Use any VNC client (RealVNC, TigerVNC, etc.) on the raw TCP port.
noVNC (browser)
WebSocket :6043
Connect your noVNC web client to ws://host:WS_PORT (vnc_port + 100). websockify runs automatically.

RDP (Windows VMs)

GET — rdp_info
curl "https://shulker.in/api/reseller-v1/?action=rdp_info&reseller_token=TOKEN&service_id=12345"

{
    "success": true,
    "data": {
        "rdp_port":    3389,
        "rdp_ip":      "in.shulker.in",
        "rdp_address": "in.shulker.in:34201",  // NAT port mapped to VM's 3389
        "ip":          "10.77.x.x",
        "running":     true,
        "os_protocol": "rdp"
    }
}

Windows VMs in NAT mode — A unique host port is automatically NAT-forwarded to the VM's port 3389. Use the rdp_address field in your Windows Remote Desktop client. Public-bridged Windows VMs have a real IP and are accessible on port 3389 directly.

Snapshot Management

Create point-in-time QCOW2 snapshots of a VM's disk. Snapshots are taken offline — the VM must be stopped first.

Stop the VM first — Snapshots require the VM to be in a stopped state. Taking a snapshot of a running VM is not supported and returns 409 Conflict.

Create snapshot
curl -X POST \
  "https://shulker.in/api/reseller-v1/?action=snapshot_create&reseller_token=TOKEN&service_id=12345&tag=pre-update"

# tag is optional — defaults to YYYYMMDDHHIISS if omitted
# Created file: snap_pre-update.qcow2

{
    "success": true,
    "message": "Snapshot created",
    "data": { "snapshot": "snap_pre-update.qcow2" }
}
List snapshots
curl "https://shulker.in/api/reseller-v1/?action=snapshot_list&reseller_token=TOKEN&service_id=12345"

{
    "data": {
        "snapshots": [
            { "file": "snap_pre-update.qcow2", "size_human": "1.82 GB" },
            { "file": "snap_20250801120000.qcow2", "size_human": "900 MB" }
        ]
    }
}
Delete snapshot
curl "https://shulker.in/api/reseller-v1/?action=snapshot_delete&reseller_token=TOKEN&service_id=12345&tag=pre-update"

Extra Disk Management

Attach up to 8 additional data volumes to any VPS. Extra disks are separate QCOW2 files — they appear inside the VM as additional block devices.

Device naming — Inside a Linux VM, extra disks appear as /dev/vdb, /dev/vdc, … (disk index 1 = vdb, index 2 = vdc, etc.). On Windows VMs they appear as Disk 1, Disk 2, etc. in Disk Management. Use fdisk/mkfs on Linux or Disk Management on Windows to partition and format them before use.

Add Extra Disk

POST — add_extra_disk
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "add_extra_disk",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "size_gb": 100,
    "label": "customer-data"
  }'

{
    "success": true,
    "data": {
        "index":   1,
        "file":    "disk_extra_1.qcow2",
        "size_gb": 100,
        "label":   "customer-data",
        "note":    "Disk created — restart VM to attach as /dev/vdb"
    }
}
ParameterTypeDescription
size_gbintegerDisk size in GB (1–4000)
labelstringOptional human-readable label for the disk

List Extra Disks

GET — list_extra_disks
curl "https://shulker.in/api/reseller-v1/?action=list_extra_disks&reseller_token=TOKEN&service_id=12345"

{
    "data": {
        "extra_disks": [
            {
                "index": 1, "file": "disk_extra_1.qcow2",
                "size_gb": 100, "label": "customer-data",
                "virtual_size_human": "100 GB",
                "actual_size_human":  "1.22 GB"  // compressed on host
            }
        ]
    }
}

Resize Extra Disk

POST — resize_extra_disk (VM must be stopped)
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "resize_extra_disk",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "index": 1,
    "size_gb": 200
  }'

# After resize, extend the partition inside the OS:
# Linux:   growpart /dev/vdb 1 && resize2fs /dev/vdb1
# Windows: Disk Management → right-click → Extend Volume

Remove Extra Disk

POST — remove_extra_disk (VM must be stopped, irreversible)
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "remove_extra_disk",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "index": 1
  }'

⚠️ Permanent deletion — Removing an extra disk deletes the QCOW2 file immediately. Backup any data first. Both resize_extra_disk and remove_extra_disk require the VM to be stopped (409 otherwise). Disk grow-only — shrinking is not supported.

IP Management

Each VM can have multiple IP addresses. The primary IP is used for SSH, cloud-init, and the VNC info panel. Additional IPs can be NAT (private) or bridged (public).

NAT mode
Private IP
VM gets a 10.77.x.x IP. Traffic exits via host NAT masquerade. Suitable for most workloads.
Bridged mode
Public IP
VM gets a real routable IP. No NAT — the VM connects directly to the upstream network via br-public.

List IPs

GET — list_ips
curl "https://shulker.in/api/reseller-v1/?action=list_ips&reseller_token=TOKEN&service_id=12345"

{
    "data": {
        "ips": [
            {
                "ip":           "10.77.0.5",
                "prefix":       "16",
                "gateway":      "10.77.0.1",
                "source":       "local",
                "network_mode": "nat",
                "primary":      true,
                "label":        ""
            }
        ],
        "primary": { "ip": "10.77.0.5", "primary": true }
    }
}

Allocate a Shulker Public IP

Request a new public IP from the Shulker IP pool and assign it to a VM. The IP is automatically reserved in the pool.

POST — allocate_ip
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "allocate_ip",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "primary": false,
    "label": "public"
  }'

Add Manual IP

POST — add_ip
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "add_ip",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "ip": "103.x.x.x",
    "prefix": "24",
    "gateway": "103.x.x.1",
    "label": "extra-public",
    "primary": false
  }'

Set Primary IP

POST — set_primary_ip
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "set_primary_ip",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "ip": "103.x.x.x"
  }'

# Restart the VM to regenerate cloud-init with the new primary IP

Remove IP

POST — remove_ip
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "remove_ip",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "ip": "103.x.x.x",
    "release": true
  }'

# release: true → returns the IP to the Shulker pool
# release: false (default) → just removes from VM record

Cloud-Init & Password Management

Update the root password or SSH key on a cloud VM, or regenerate the entire cloud-init configuration.

Change Root Password

POST — change_root_password
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "change_root_password",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "root_password": "MyNewSecurePass123!"
  }'

# For cloud VMs: also regenerates cloud-init ISO
# Restart the VM and cloud-init will set the new password on next boot

Regenerate Cloud-Init

Rebuild the entire cloud-init ISO — useful to update SSH key, password, or IP config simultaneously.

POST — regen_cloudinit
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "regen_cloudinit",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "root_password": "NewPassword",
    "ssh_key": "ssh-rsa AAAAB3NzaC1yc2E..."
  }'

# Both root_password and ssh_key are optional — omit to keep current values
# Restart VM after regenerating for changes to take effect

Cloud-init runs once — By default cloud-init skips re-running if it has already run on an instance. The backend uses a fresh instance ID each time regen_cloudinit is called, ensuring the new config is applied on the next boot.

Update Resources

Modify CPU, RAM, root disk size, or bandwidth limit for a service.

POST — update_resources
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "update_resources",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "cpu_cores": 4,
    "ram_mb": 8192,
    "disk_gb": 100,
    "bandwidth_mbps": 500
  }'

{
    "success": true,
    "data": {
        "changed": ["cpu_cores", "ram_mb", "disk_gb", "bandwidth_mbps"],
        "note": "Restart needed for CPU/RAM"
    }
}
ParameterNotes
cpu_coresApplied after VM restart. Min: 1.
ram_mbApplied after VM restart. Min: 128 MB.
disk_gbGrow-only. VM must be stopped. After resize, use growpart/resize2fs inside the OS to claim new space.
bandwidth_mbpsApplied immediately using tc tbf on the TAP interface. 0 removes the limit. Affects both inbound and outbound.

Set Bandwidth Only

POST — set_bandwidth
curl -X POST https://shulker.in/api/reseller-v1/ \
  -H "Content-Type: application/json" \
  -d '{
    "action": "set_bandwidth",
    "reseller_token": "TOKEN",
    "service_id": 12345,
    "bandwidth_mbps": 100
  }'

Error Codes

HTTP StatusErrorDescription
400Missing parameterRequired parameter (action, service_id, etc.) is missing
400Insufficient balanceWallet balance too low for the operation
400Unknown OSos key not found in the OS catalog
400Shrink not supportedAttempt to resize disk to smaller value
401Invalid tokenreseller_token is invalid or expired
403Not verifiedReseller account not yet verified
403Service expiredVM expired — renew first
404Service not foundNo service with that service_id
404ISO not foundReferenced ISO filename not on the node
404Snapshot not foundSnapshot tag does not match any file
405Method not allowedPOST required but another method used
409VM already runningStart called on a running VM
409VM not runningStop/reboot called on a stopped VM
409VM runningOperation requires VM to be stopped (snapshot, disk resize, etc.)
409Already existsVM name or IP already in use
500Disk create failedQEMU image creation error — check node disk space
500cloud-init failedISO generation failed — cloud-image-utils may not be installed
502Node unreachableBackend node failed to respond
503No available nodesNo nodes available for the requested tier
503No IPs availableIP pool exhausted — contact support
Error response shape
{
    "success": false,
    "message": "Insufficient balance. Required: ₹218.50, Available: ₹100.00"
}

Attach Public IP

POST

Automatically fetch and attach a public IP to a running VM. If the VM already has a public IP, this action will return the existing one. Requires the VM to be in running state.

Endpoint
https://shulker.in/api/reseller-v1/?action=serververse_attach&reseller_token=YOUR_TOKEN&service_id=12345
ParameterTypeRequiredDescription
service_idintegerrequiredID of the VM service
cURL
curl -X POST \
                  "https://shulker.in/api/reseller-v1/?action=serververse_attach&reseller_token=TOKEN&service_id=12345"
Success Response
{
                    "success": true,
                    "message": "Public IP 185.2.49.180 attached successfully",
                    "data": {
                        "ip": "185.2.49.180",
                        "gateway": "185.2.49.1",
                        "was_already_attached": false
                    }
                }
Error Responses
# VM is not running
                {
                    "success": false,
                    "error": "VM 'service_name' must be running to attach a public IP"
                }
                
                # No public IPs available
                {
                    "success": false,
                    "error": "No WireGuard IPs available from the pool"
                }
                
                # SSH not ready (VM still booting)
                {
                    "success": false,
                    "error": "SSH unreachable on 10.77.0.14 after 120 s"
                }

✅ One-Click Public IP — This action handles everything automatically: it checks if a public IP is already attached, fetches an available IP from the pool, waits for SSH to be ready, runs the installation script inside the VM, and updates the service metadata. No manual steps required.

⚠️ Prerequisites — The VM must be running and have SSH accessible (cloud-init must have completed). For cloud VMs, this usually takes 30-60 seconds after start. The action will wait up to 120 seconds for SSH to become available.

Complete Actions Reference

Every action value accepted by the Reseller API.

Service Management

create_instance
Create new VPS (cloud or ISO)
list_services
List all services
renew_service
Renew for 1 month
update_service_alias
Set display name
list_tiers
List all pricing tiers
check_stock
Check node capacity

VM Lifecycle

info
Full VM metadata
server_status
Running state + PID
start
Start VM
stop
Graceful stop
force_stop
Immediate kill
reboot
Soft reboot
suspend
Freeze VM (SIGSTOP)
resume
Unfreeze (SIGCONT)

Monitoring

stats
CPU, RAM, disk, network
serial_log
Console output (N lines)
vnc_info
VNC port + WS port
rdp_info
RDP address (Windows)

Disk & Storage

erase_disk
Wipe and recreate root disk
snapshot_create
Create disk snapshot
snapshot_list
List snapshots
snapshot_delete
Delete snapshot
add_extra_disk
Add data volume
list_extra_disks
List data volumes
resize_extra_disk
Grow data volume
remove_extra_disk
Delete data volume

ISO Management

list_isos
List uploaded ISOs
iso_upload_chunk
Chunked ISO upload
change_iso
Mount / eject ISO
delete_iso
Delete ISO file

IP Management

list_ips
All IPs on the VM
add_ip
Add manual IP
allocate_ip
Get IP from Shulker pool
serververse_attach
Auto-attach public IP to VM
set_primary_ip
Promote IP to primary
edit_ip
Update IP settings
remove_ip
Remove + optionally release
swap_ip
Move IP to another VM
set_gateway
Update gateway

OS & Configuration

list_os
List all supported OS
os_image_status
Check image cache
regen_cloudinit
Rebuild cloud-init ISO
change_root_password
Update root password
update_resources
CPU, RAM, disk, bandwidth
set_bandwidth
Set traffic limit

Best Practices

💰 Monitor Wallet Balance — Check your wallet before creating or renewing services. Implement low-balance alerting. A failed wallet deduction leaves the service in a broken partial state.

📝 Use Service Aliases — Tag every service with a customer or project alias using update_service_alias. This maps the internal service name to your own customer IDs, making list_services output meaningful.

⏰ Automate Renewals — The list_services response includes days_remaining. Build a cron job that queries this daily and renews (or notifies customers) when days_remaining drops below 5.

🔐 Secure Your Token — Store reseller_token in environment variables or a secrets manager. Never commit it to version control. Rotate periodically from the Reseller Dashboard.

☁️ Prefer Cloud Images — For Linux VMs, always use cloud OS keys (ubuntu-2404, debian-12, etc.). They provision in seconds with zero manual interaction. Reserve ISOs for Windows or custom OS installs.

💿 Upload ISOs in Advance — ISO uploads are large and take time. Use list_isos to check if an ISO is already on the node before uploading. The same ISO can be shared across multiple VMs.

🔄 Restart After Resource Changes — CPU and RAM changes require a VM restart. Disk changes (disk_gb, extra disk resize) also need a stop/start cycle, plus partition expansion inside the OS. Bandwidth limits apply immediately with no restart.

📸 Snapshot Before Changes — Before a major upgrade or configuration change, create a snapshot with a meaningful tag. Keep snapshots lean — they accumulate actual disk usage over time as the VM writes new data.

Quick Reference

Common operations cheatsheet
## ── SERVICE MANAGEMENT ──────────────────────────────────────────────────────

# Create cloud VM (Ubuntu 24.04, std tier, 4GB/2CPU/50GB)
curl -X POST "https://shulker.in/api/reseller-v1/?action=create_instance&reseller_token=TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"vps","tier":"std","ram_gb":4,"cpu_cores":2,"disk_gb":50,"os":"ubuntu-2404"}'

# Create Windows VM (with pre-uploaded ISO)
curl -X POST "https://shulker.in/api/reseller-v1/?action=create_instance&reseller_token=TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"vps","tier":"perf","ram_gb":8,"cpu_cores":4,"disk_gb":100,"os":"windows-11","iso":"Win11.iso"}'

# List active services
curl "https://shulker.in/api/reseller-v1/?action=list_services&reseller_token=TOKEN&status=active"

# Renew service
curl -X POST "https://shulker.in/api/reseller-v1/?action=renew_service&reseller_token=TOKEN&service_id=12345"

## ── VM LIFECYCLE ─────────────────────────────────────────────────────────────

curl ".../?action=start&reseller_token=TOKEN&service_id=12345"
curl ".../?action=stop&reseller_token=TOKEN&service_id=12345"
curl ".../?action=reboot&reseller_token=TOKEN&service_id=12345"
curl ".../?action=force_stop&reseller_token=TOKEN&service_id=12345"
curl ".../?action=suspend&reseller_token=TOKEN&service_id=12345"
curl ".../?action=resume&reseller_token=TOKEN&service_id=12345"

## ── MONITORING ───────────────────────────────────────────────────────────────

curl ".../?action=stats&reseller_token=TOKEN&service_id=12345"
curl ".../?action=serial_log&reseller_token=TOKEN&service_id=12345&lines=200"
curl ".../?action=vnc_info&reseller_token=TOKEN&service_id=12345"

## ── ISO ──────────────────────────────────────────────────────────────────────

curl ".../?action=list_isos&reseller_token=TOKEN"
# Upload: see ISO Upload section (chunked multipart)
curl -X POST "..." -H "Content-Type: application/json" \
  -d '{"action":"change_iso","reseller_token":"TOKEN","service_id":12345,"iso":"myos.iso"}'

## ── EXTRA DISKS ──────────────────────────────────────────────────────────────

curl ".../?action=list_extra_disks&reseller_token=TOKEN&service_id=12345"
curl -X POST "..." -d '{"action":"add_extra_disk","reseller_token":"TOKEN","service_id":12345,"size_gb":100,"label":"data"}'
curl -X POST "..." -d '{"action":"resize_extra_disk","reseller_token":"TOKEN","service_id":12345,"index":1,"size_gb":200}'
curl -X POST "..." -d '{"action":"remove_extra_disk","reseller_token":"TOKEN","service_id":12345,"index":1}'

## ── SNAPSHOTS ────────────────────────────────────────────────────────────────

curl -X POST ".../?action=snapshot_create&reseller_token=TOKEN&service_id=12345&tag=backup-v1"
curl ".../?action=snapshot_list&reseller_token=TOKEN&service_id=12345"
curl ".../?action=snapshot_delete&reseller_token=TOKEN&service_id=12345&tag=backup-v1"

## ── IP MANAGEMENT ────────────────────────────────────────────────────────────

curl ".../?action=list_ips&reseller_token=TOKEN&service_id=12345"
curl -X POST "..." -d '{"action":"allocate_ip","reseller_token":"TOKEN","service_id":12345}'
curl -X POST "..." -d '{"action":"set_primary_ip","reseller_token":"TOKEN","service_id":12345,"ip":"103.x.x.x"}'
curl -X POST "..." -d '{"action":"remove_ip","reseller_token":"TOKEN","service_id":12345,"ip":"103.x.x.x","release":true}'

## ── CONFIG ───────────────────────────────────────────────────────────────────

curl -X POST "..." -d '{"action":"change_root_password","reseller_token":"TOKEN","service_id":12345,"root_password":"NewPass!"}'
curl -X POST "..." -d '{"action":"update_resources","reseller_token":"TOKEN","service_id":12345,"cpu_cores":4,"ram_mb":8192}'
curl -X POST "..." -d '{"action":"set_bandwidth","reseller_token":"TOKEN","service_id":12345,"bandwidth_mbps":200}'