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.
Introduction
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
https://shulker.in/api/reseller-v1/# 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.
| Tier | RAM / GB | CPU / Core | Disk / GB | Node Type |
|---|---|---|---|---|
| eco | ₹26 | ₹16 | ₹0.26 | VPS-Eco |
| std | ₹37 | ₹26 | ₹0.37 | VPS-Std |
| perf | ₹53 | ₹42 | ₹0.53 | VPS-Perf |
| pwr | ₹74 | ₹58 | ₹0.79 | VPS-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.
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.
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 } } }
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
#!/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.
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 } }
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 } }
| Parameter | Type | Required | Description |
|---|---|---|---|
| tier | string | optional | Filter 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.
| OS Key | Label | Boot Mode | Family |
|---|---|---|---|
| ubuntu-2404 | Ubuntu 24.04 LTS (Noble) | ☁ cloud | ubuntu |
| ubuntu-2204 | Ubuntu 22.04 LTS (Jammy) | ☁ cloud | ubuntu |
| ubuntu-2004 | Ubuntu 20.04 LTS (Focal) | ☁ cloud | ubuntu |
| debian-12 | Debian 12 (Bookworm) | ☁ cloud | debian |
| debian-11 | Debian 11 (Bullseye) | ☁ cloud | debian |
| fedora-40 | Fedora 40 | ☁ cloud | fedora |
| centos-9 | CentOS Stream 9 | ☁ cloud | centos |
| almalinux-9 | AlmaLinux 9 | ☁ cloud | almalinux |
| rockylinux-9 | Rocky Linux 9 | ☁ cloud | rockylinux |
| opensuse-15 | openSUSE Leap 15.6 | ☁ cloud | opensuse |
| arch-linux | Arch Linux (latest) | ☁ cloud | arch |
| alpine-319 | Alpine Linux 3.19 | ☁ cloud | alpine |
| windows-11 | Windows 11 (ISO) | 💿 iso | windows |
| iso-custom | Custom ISO (any OS) | 💿 iso | custom |
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:
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
POSTCreate a new VPS. Cost is deducted from your Purple Wallet immediately. Supports both cloud images and ISO installs.
https://shulker.in/api/reseller-v1/?action=create_instance&reseller_token=YOUR_TOKEN| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | required | Must be vps |
| tier | string | required | eco, std, perf, or pwr |
| ram_gb | integer | required | RAM in GB (1–96) |
| cpu_cores | integer | required | vCPU cores (1–32) |
| disk_gb | integer | optional | Root disk in GB (default: 20, max: 1000) |
| os | string | optional | OS key from the catalog (default: ubuntu-2404) |
| iso | string | optional | ISO filename for ISO-type OS (e.g. windows11.iso). Must already be uploaded. |
| region | string | optional | Location string from list_regions (e.g. Mumbai, India). Omit to auto-select the best available node. |
| ssh_key | string | optional | Public SSH key to inject (cloud images only) |
Example A — Cloud Linux VM (Ubuntu 24.04)
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 -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": 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.
- 1Disk 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.
- 2cloud-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. - 3QEMU launches — The VM boots with both the OS disk and cloud-init ISO attached. The OS detects the ISO as a
NoClouddatasource and applies the configuration on first boot. - 4cloud-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. - 5SSH ready in ~15 seconds — Connect as
root@IP_ADDRESSusing theroot_passwordfrom the creation response, or your SSH key.
# 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.
https://shulker.in/api/reseller-v1/?action=iso_upload_chunk&reseller_token=YOUR_TOKEN Content-Type: multipart/form-data
| Form Field | Type | Required | Description |
|---|---|---|---|
| filename | string | required | Full ISO filename including .iso extension (e.g. Win11_x64.iso) |
| chunk_index | integer | required | Zero-based index of this chunk (0, 1, 2, …) |
| total_chunks | integer | required | Total number of chunks in this upload |
| upload_id | string | required | Unique identifier for this upload session (alphanumeric, no spaces) |
| file | binary | required | Raw 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.
■ sent ■ pending ■ final chunk → triggers assembly
JavaScript Upload Example
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
#!/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)"
{ "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
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.
# 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
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
GETGet all services on your reseller account, with optional status filtering.
https://shulker.in/api/reseller-v1/?action=list_services&reseller_token=TOKEN&status=all| Parameter | Values | Description |
|---|---|---|
| status | all · active · expired | Filter returned services by expiry status |
{
"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
POSTExtend a service by one month. Works on both active and recently-expired services.
curl -X POST "https://shulker.in/api/reseller-v1/?action=renew_service&reseller_token=TOKEN&service_id=12345"
{
"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
POSTAttach a human-readable label to a service — useful for mapping internal customer IDs, project names, or domains.
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 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"
{
"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 (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
curl "https://shulker.in/api/reseller-v1/?action=stats&reseller_token=TOKEN&service_id=12345"
{
"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.
# 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
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 } }
ws://host:WS_PORT (vnc_port + 100). websockify runs automatically.RDP (Windows VMs)
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.
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" } }
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" } ] } }
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
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" } }
| Parameter | Type | Description |
|---|---|---|
| size_gb | integer | Disk size in GB (1–4000) |
| label | string | Optional human-readable label for the disk |
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
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
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).
10.77.x.x IP. Traffic exits via host NAT masquerade. Suitable for most workloads.br-public.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.
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
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
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
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
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.
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.
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" } }
| Parameter | Notes |
|---|---|
| cpu_cores | Applied after VM restart. Min: 1. |
| ram_mb | Applied after VM restart. Min: 128 MB. |
| disk_gb | Grow-only. VM must be stopped. After resize, use growpart/resize2fs inside the OS to claim new space. |
| bandwidth_mbps | Applied immediately using tc tbf on the TAP interface. 0 removes the limit. Affects both inbound and outbound. |
Set Bandwidth Only
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 Status | Error | Description |
|---|---|---|
| 400 | Missing parameter | Required parameter (action, service_id, etc.) is missing |
| 400 | Insufficient balance | Wallet balance too low for the operation |
| 400 | Unknown OS | os key not found in the OS catalog |
| 400 | Shrink not supported | Attempt to resize disk to smaller value |
| 401 | Invalid token | reseller_token is invalid or expired |
| 403 | Not verified | Reseller account not yet verified |
| 403 | Service expired | VM expired — renew first |
| 404 | Service not found | No service with that service_id |
| 404 | ISO not found | Referenced ISO filename not on the node |
| 404 | Snapshot not found | Snapshot tag does not match any file |
| 405 | Method not allowed | POST required but another method used |
| 409 | VM already running | Start called on a running VM |
| 409 | VM not running | Stop/reboot called on a stopped VM |
| 409 | VM running | Operation requires VM to be stopped (snapshot, disk resize, etc.) |
| 409 | Already exists | VM name or IP already in use |
| 500 | Disk create failed | QEMU image creation error — check node disk space |
| 500 | cloud-init failed | ISO generation failed — cloud-image-utils may not be installed |
| 502 | Node unreachable | Backend node failed to respond |
| 503 | No available nodes | No nodes available for the requested tier |
| 503 | No IPs available | IP pool exhausted — contact support |
{
"success": false,
"message": "Insufficient balance. Required: ₹218.50, Available: ₹100.00"
}Attach Public IP
POSTAutomatically 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.
https://shulker.in/api/reseller-v1/?action=serververse_attach&reseller_token=YOUR_TOKEN&service_id=12345| Parameter | Type | Required | Description |
|---|---|---|---|
| service_id | integer | required | ID of the VM service |
curl -X POST \ "https://shulker.in/api/reseller-v1/?action=serververse_attach&reseller_token=TOKEN&service_id=12345"
{
"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
}
}# 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
VM Lifecycle
Monitoring
Disk & Storage
ISO Management
IP Management
OS & Configuration
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
## ── 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}'