VMOS Cloud API
  • 简体中文
  • English
  • 简体中文
  • English
  • Product Introduction
  • Product Type
  • Product Billing
  • OpenAPI
    • User Guide
    • V2 Simplified Signature
    • API Documentation
    • LLMs.txt (AI Quick Reference)
    • OpenAPI Spec (for AI & tools)
    • Error Code
    • Instance Property List
    • Android device modification attribute list
    • Callback Task Business Type Codes
    • Changelog
    • Mobile Root Certificate Format Specification
  • Android SDK
    • Example Construction
    • API Documentation
    • Callback Functions
    • Error Code
    • Changelog
  • Web H5 SDK
    • Example Build
    • API Documentation
    • H5 SDK Callback Functions
    • Error Code
    • Change log
  • Windows PC SDK
    • Example Setup
    • API Documentation
    • Callback Functions
    • Changelog
  • Edge-Cloud Communication Development
    • AIDL Integration Method
    • System Service API (AIDL)
  • Similar to XP, LSP Hook framework

    • Xposed-like / LSPosed Framework
    • Sensor Data Dynamic Simulation
  • Related agreements

Overview

V2 is the simplified signing scheme for VMOSCloud OpenAPI. It runs in parallel with the original V4 (HMAC-SHA256 with derived keys); AK / SK are shared across both schemes.

Existing V4 customers do not need to migrate — the legacy SDK is supported indefinitely. New integrations are encouraged to use V2: 3 headers + 1 line of SHA-256, no derived key, no JSON re-ordering, no UTC formatting required.

Get Account (AK/SK)

Prerequisites

Before using the API, obtain an Access Key ID and Secret Access Key for request authentication.

Steps:

  1. Log in to the VMOSCloud Platform.
  2. Navigate to Developer -> API.
  3. Copy the corresponding credentials:
    • AccessKeyID (AK)
    • SecretAccessKey (SK)

Request Headers

Every request must include the following 3 authentication headers:

NameTypeRequiredDescriptionExample
X-Access-KeystringYesAccess Key ID in plain textak_xxxxxxxxxxxx
X-TimestampstringYesunix seconds (10-digit string), ±5-minute window1747555200
X-SignstringYes64-char lowercase hex signature6069dfb4cb3ac57b…
Content-TypestringPOSTResource MIME typeapplication/json

Signing Algorithm

Plain string concatenation, no delimiters:

signString = SK + X-Timestamp + path + bodyOrQuery
X-Sign     = lowerHex( SHA-256( signString_UTF8 ) )
FieldValue
SKSecret Access Key in plain text
X-TimestampSame 10-digit unix-seconds string sent in the header (millisecond timestamps will fail the time window)
pathFull path including the servlet prefix, e.g. /vcpcloud/api/padApi/padInfo
bodyOrQuerySee table below

bodyOrQuery Rules

Request typebodyOrQuery
GETRaw query string, e.g. startDate=2026-05-01&endDate=2026-05-31; empty string when no params
POST / PUT (JSON)Raw body (sign exactly what you send — no re-order, no whitespace strip)
File uploads /uploadFile / /asyncCmd / /syncCmdEmpty string (file body is not signed)

The query-string parameter order and URL encoding must match exactly what is sent on the wire. Do not re-sort or re-encode before signing.

Signing Example

Input:

SK   = 9cucpjoyn4xxmkhj3q9el3ce
ts   = 1747555200
path = /vcpcloud/api/padApi/padInfo
body = {"padCode":"AC32010601132"}

Concatenated signString:

9cucpjoyn4xxmkhj3q9el3ce1747555200/vcpcloud/api/padApi/padInfo{"padCode":"AC32010601132"}

X-Sign = lowerHex( SHA-256( signString ) ) — verify your client implementation against any standard SHA-256 library.

Error Codes

codeMeaning
2019Signature verification failed
2031Invalid key (AccessKey not found)
2032Required header missing
2033Timestamp expired or malformed

Examples

curl

TS=$(date +%s)
AK="ak_xxxxxxxxxxxx"
SK="sk_xxxxxxxxxxxx"
URI="/vcpcloud/api/padApi/padInfo"
BODY='{"padCode":"AC32010601132"}'

SIGN=$(printf "%s" "${SK}${TS}${URI}${BODY}" | openssl dgst -sha256 -hex | awk '{print $2}')

curl -X POST "https://api.vmoscloud.com${URI}" \
  -H "X-Access-Key: ${AK}" \
  -H "X-Timestamp: ${TS}" \
  -H "X-Sign: ${SIGN}" \
  -H "Content-Type: application/json" \
  --data-binary "${BODY}"

java

import com.alibaba.fastjson.JSONObject;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;

public class V2ApiRequestUtils {

    private static final String BASE_URL = "https://api.vmoscloud.com";
    private static final String ACCESS_KEY = "Access Key ID";
    private static final String SECRET_ACCESS_KEY = "Secret Access Key";

    public static String sendPostRequest(String path, JSONObject params) {
        try {
            String body = params == null ? "" : params.toJSONString();
            String ts = String.valueOf(System.currentTimeMillis() / 1000);

            HttpPost req = new HttpPost(BASE_URL + path);
            req.setHeader("X-Access-Key", ACCESS_KEY);
            req.setHeader("X-Timestamp", ts);
            req.setHeader("X-Sign", sign(ts, path, body));
            req.setHeader("Content-Type", "application/json");
            req.setEntity(new StringEntity(body, StandardCharsets.UTF_8));

            try (CloseableHttpClient client = HttpClients.createDefault();
                 CloseableHttpResponse resp = client.execute(req)) {
                return EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            throw new RuntimeException("V2 POST failed: " + path, e);
        }
    }

    public static String sendGetRequest(String path, JSONObject params) {
        try {
            String query = buildQuery(params);
            String ts = String.valueOf(System.currentTimeMillis() / 1000);

            HttpGet req = new HttpGet(BASE_URL + path + (query.isEmpty() ? "" : "?" + query));
            req.setHeader("X-Access-Key", ACCESS_KEY);
            req.setHeader("X-Timestamp", ts);
            req.setHeader("X-Sign", sign(ts, path, query));

            try (CloseableHttpClient client = HttpClients.createDefault();
                 CloseableHttpResponse resp = client.execute(req)) {
                return EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            throw new RuntimeException("V2 GET failed: " + path, e);
        }
    }

    private static String sign(String ts, String path, String body) throws Exception {
        String signString = SECRET_ACCESS_KEY + ts + path + body;
        byte[] raw = MessageDigest.getInstance("SHA-256")
                .digest(signString.getBytes(StandardCharsets.UTF_8));
        return String.format("%064x", new BigInteger(1, raw));
    }

    private static String buildQuery(JSONObject params) {
        if (params == null || params.isEmpty()) return "";
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Object> e : params.entrySet()) {
            if (sb.length() > 0) sb.append('&');
            sb.append(e.getKey()).append('=').append(e.getValue());
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        // POST
        JSONObject body = new JSONObject();
        body.put("padCode", "AC32010601132");
        System.out.println(sendPostRequest("/vcpcloud/api/padApi/padInfo", body));

        // GET
        JSONObject query = new JSONObject();
        query.put("startDate", "2026-05-01");
        query.put("endDate", "2026-05-31");
        System.out.println(sendGetRequest("/vcpcloud/api/padApi/getOrderEquipmentList", query));
    }
}

python

import hashlib
import time
import json
import requests

BASE_URL = "https://api.vmoscloud.com"
ACCESS_KEY = "Access Key ID"
SECRET_ACCESS_KEY = "Secret Access Key"


def _sign(ts: str, path: str, body_or_query: str) -> str:
    return hashlib.sha256(
        (SECRET_ACCESS_KEY + ts + path + body_or_query).encode("utf-8")
    ).hexdigest()


def send_post(path: str, params: dict | None = None) -> str:
    body = json.dumps(params, separators=(",", ":"), ensure_ascii=False) if params else ""
    ts = str(int(time.time()))
    headers = {
        "X-Access-Key": ACCESS_KEY,
        "X-Timestamp": ts,
        "X-Sign": _sign(ts, path, body),
        "Content-Type": "application/json",
    }
    # Use data=bytes so requests does not rewrite the body
    resp = requests.post(BASE_URL + path, headers=headers, data=body.encode("utf-8"))
    return resp.text


def send_get(path: str, params: dict | None = None) -> str:
    query = "&".join(f"{k}={v}" for k, v in params.items()) if params else ""
    ts = str(int(time.time()))
    url = BASE_URL + path + (f"?{query}" if query else "")
    headers = {
        "X-Access-Key": ACCESS_KEY,
        "X-Timestamp": ts,
        "X-Sign": _sign(ts, path, query),
    }
    return requests.get(url, headers=headers).text


if __name__ == "__main__":
    # POST
    print(send_post("/vcpcloud/api/padApi/padInfo", {"padCode": "AC32010601132"}))
    # GET
    print(send_get("/vcpcloud/api/padApi/getOrderEquipmentList",
                   {"startDate": "2026-05-01", "endDate": "2026-05-31"}))

node

const crypto = require("crypto");

const BASE_URL = "https://api.vmoscloud.com";
const ACCESS_KEY = "Access Key ID";
const SECRET_ACCESS_KEY = "Secret Access Key";

function sign(ts, path, bodyOrQuery) {
    return crypto
        .createHash("sha256")
        .update(SECRET_ACCESS_KEY + ts + path + bodyOrQuery, "utf8")
        .digest("hex");
}

async function sendPost(path, params) {
    const body = params ? JSON.stringify(params) : "";
    const ts = Math.floor(Date.now() / 1000).toString();
    const resp = await fetch(BASE_URL + path, {
        method: "POST",
        headers: {
            "X-Access-Key": ACCESS_KEY,
            "X-Timestamp": ts,
            "X-Sign": sign(ts, path, body),
            "Content-Type": "application/json",
        },
        body,
    });
    return resp.text();
}

async function sendGet(path, params) {
    const query = params
        ? Object.entries(params).map(([k, v]) => `${k}=${v}`).join("&")
        : "";
    const ts = Math.floor(Date.now() / 1000).toString();
    const resp = await fetch(BASE_URL + path + (query ? `?${query}` : ""), {
        headers: {
            "X-Access-Key": ACCESS_KEY,
            "X-Timestamp": ts,
            "X-Sign": sign(ts, path, query),
        },
    });
    return resp.text();
}

(async () => {
    // POST
    console.log(await sendPost("/vcpcloud/api/padApi/padInfo",
        { padCode: "AC32010601132" }));
    // GET
    console.log(await sendGet("/vcpcloud/api/padApi/getOrderEquipmentList",
        { startDate: "2026-05-01", endDate: "2026-05-31" }));
})();

go

package main

import (
    "bytes"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "strconv"
    "strings"
    "time"
)

const (
    baseURL         = "https://api.vmoscloud.com"
    accessKey       = "Access Key ID"
    secretAccessKey = "Secret Access Key"
)

func sign(ts, path, bodyOrQuery string) string {
    sum := sha256.Sum256([]byte(secretAccessKey + ts + path + bodyOrQuery))
    return hex.EncodeToString(sum[:])
}

func sendPost(path string, params map[string]interface{}) (string, error) {
    body := ""
    if params != nil {
        b, _ := json.Marshal(params)
        body = string(b)
    }
    ts := strconv.FormatInt(time.Now().Unix(), 10)

    req, _ := http.NewRequest("POST", baseURL+path, bytes.NewReader([]byte(body)))
    req.Header.Set("X-Access-Key", accessKey)
    req.Header.Set("X-Timestamp", ts)
    req.Header.Set("X-Sign", sign(ts, path, body))
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    bs, _ := io.ReadAll(resp.Body)
    return string(bs), nil
}

func sendGet(path string, params map[string]string) (string, error) {
    parts := make([]string, 0, len(params))
    for k, v := range params {
        parts = append(parts, k+"="+v)
    }
    query := strings.Join(parts, "&")
    ts := strconv.FormatInt(time.Now().Unix(), 10)

    url := baseURL + path
    if query != "" {
        url += "?" + query
    }
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("X-Access-Key", accessKey)
    req.Header.Set("X-Timestamp", ts)
    req.Header.Set("X-Sign", sign(ts, path, query))

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    bs, _ := io.ReadAll(resp.Body)
    return string(bs), nil
}

func main() {
    // POST
    out, _ := sendPost("/vcpcloud/api/padApi/padInfo",
        map[string]interface{}{"padCode": "AC32010601132"})
    fmt.Println(out)

    // GET
    out, _ = sendGet("/vcpcloud/api/padApi/getOrderEquipmentList",
        map[string]string{"startDate": "2026-05-01", "endDate": "2026-05-31"})
    fmt.Println(out)
}

php

<?php

const BASE_URL = "https://api.vmoscloud.com";
const ACCESS_KEY = "Access Key ID";
const SECRET_ACCESS_KEY = "Secret Access Key";

function v2_sign(string $ts, string $path, string $bodyOrQuery): string {
    return hash("sha256", SECRET_ACCESS_KEY . $ts . $path . $bodyOrQuery);
}

function send_post(string $path, ?array $params = null): string {
    $body = $params === null ? "" : json_encode($params, JSON_UNESCAPED_UNICODE);
    $ts = (string) time();

    $ch = curl_init(BASE_URL . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $body,
        CURLOPT_HTTPHEADER     => [
            "X-Access-Key: " . ACCESS_KEY,
            "X-Timestamp: " . $ts,
            "X-Sign: " . v2_sign($ts, $path, $body),
            "Content-Type: application/json",
        ],
    ]);
    $resp = curl_exec($ch);
    curl_close($ch);
    return $resp;
}

function send_get(string $path, ?array $params = null): string {
    $query = $params ? http_build_query($params) : "";
    $ts = (string) time();
    $url = BASE_URL . $path . ($query ? "?$query" : "");

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            "X-Access-Key: " . ACCESS_KEY,
            "X-Timestamp: " . $ts,
            "X-Sign: " . v2_sign($ts, $path, $query),
        ],
    ]);
    $resp = curl_exec($ch);
    curl_close($ch);
    return $resp;
}

// POST
echo send_post("/vcpcloud/api/padApi/padInfo", ["padCode" => "AC32010601132"]) . PHP_EOL;
// GET
echo send_get("/vcpcloud/api/padApi/getOrderEquipmentList",
    ["startDate" => "2026-05-01", "endDate" => "2026-05-31"]) . PHP_EOL;

.net

using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

public static class V2ApiRequestUtils
{
    private const string BaseUrl = "https://api.vmoscloud.com";
    private const string AccessKey = "Access Key ID";
    private const string SecretAccessKey = "Secret Access Key";

    private static readonly HttpClient Http = new HttpClient();

    private static string Sign(string ts, string path, string bodyOrQuery)
    {
        using var sha = SHA256.Create();
        byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(SecretAccessKey + ts + path + bodyOrQuery));
        var sb = new StringBuilder(64);
        foreach (var b in hash) sb.Append(b.ToString("x2"));
        return sb.ToString();
    }

    public static async Task<string> SendPostAsync(string path, object? body)
    {
        string bodyJson = body == null ? "" : JsonSerializer.Serialize(body);
        string ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

        var req = new HttpRequestMessage(HttpMethod.Post, BaseUrl + path)
        {
            Content = new StringContent(bodyJson, Encoding.UTF8, "application/json")
        };
        req.Headers.TryAddWithoutValidation("X-Access-Key", AccessKey);
        req.Headers.TryAddWithoutValidation("X-Timestamp", ts);
        req.Headers.TryAddWithoutValidation("X-Sign", Sign(ts, path, bodyJson));

        var resp = await Http.SendAsync(req);
        return await resp.Content.ReadAsStringAsync();
    }

    public static async Task<string> SendGetAsync(string path, Dictionary<string, string>? query)
    {
        string queryStr = query == null ? ""
            : string.Join("&", query.Select(kv => $"{kv.Key}={kv.Value}"));
        string ts = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

        var url = BaseUrl + path + (queryStr.Length > 0 ? "?" + queryStr : "");
        var req = new HttpRequestMessage(HttpMethod.Get, url);
        req.Headers.TryAddWithoutValidation("X-Access-Key", AccessKey);
        req.Headers.TryAddWithoutValidation("X-Timestamp", ts);
        req.Headers.TryAddWithoutValidation("X-Sign", Sign(ts, path, queryStr));

        var resp = await Http.SendAsync(req);
        return await resp.Content.ReadAsStringAsync();
    }

    public static async Task Main()
    {
        // POST
        Console.WriteLine(await SendPostAsync("/vcpcloud/api/padApi/padInfo",
            new { padCode = "AC32010601132" }));
        // GET
        Console.WriteLine(await SendGetAsync("/vcpcloud/api/padApi/getOrderEquipmentList",
            new Dictionary<string, string> { ["startDate"] = "2026-05-01", ["endDate"] = "2026-05-31" }));
    }
}

FAQ

Q1: Do existing customers need to migrate to V2? No. V4 stays supported indefinitely. See V4 signing mechanism.

Q2: Can the same AK/SK be used for both schemes? Yes. The AK/SK is shared; the protocol is chosen per request.

Q3: What goes in bodyOrQuery when the POST body is empty? An empty string. signString = SK + ts + path — nothing appended.

Q4: How do I sign multipart file-upload endpoints? Pass an empty string for bodyOrQuery. The file body is not signed. Affected endpoints: /uploadFile, /asyncCmd, /syncCmd.

Q5: Can I use millisecond timestamps? No. Must be 10-digit unix seconds. A 13-digit millisecond value falls outside the ±5-minute window and is rejected as 2033.

Q6: Is X-Sign case-sensitive? The server compares case-insensitively; either case works. Lowercase is recommended for consistency.

Q7: How is the protocol selected on the server? If X-Sign is present, V2 is used; otherwise the request falls back to V4 (Authorization header). Both schemes are active simultaneously.

Prev
User Guide
Next
API Documentation