API Guide
Overview
OwlProxy OpenAPI uses V2 SHA-256 signing for request authentication.
Get AK/SK
Before calling the API, contact your technical representative to obtain:
Access Key ID(AK)Secret Access Key(SK)
Keep the SK secure. Do not expose it in frontend code, public repositories, or logs.
Request Headers
Every OpenAPI request must contain these authentication headers:
| Header | Type | Required | Description | Example |
|---|---|---|---|---|
X-Access-Key | string | Yes | Access Key ID | ak_xxxxxxxxxxxx |
X-Timestamp | string | Yes | 10-digit Unix timestamp in seconds | 1747555200 |
X-Sign | string | Yes | 64-character SHA-256 hexadecimal signature | 6069dfb4cb3ac57b... |
Content-Type | string | POST/PUT | Request body type | application/json |
X-Timestamp must be within five minutes of the current time.
Signing Algorithm
Concatenate the fields directly without delimiters:
signString = SK + X-Timestamp + path + bodyOrQuery
X-Sign = lowerHex(SHA-256(signString_UTF8))
| Field | Value |
|---|---|
SK | Secret Access Key |
X-Timestamp | The exact seconds timestamp sent in the request header |
path | Full request path, such as /owlproxy/api/openApi/vcProxy/queryByorderIdList |
bodyOrQuery | Determined by the request type; see below |
bodyOrQuery Rules
| Request type | bodyOrQuery |
|---|---|
| GET | Raw query string; use an empty string when there are no parameters |
| POST/PUT JSON | The exact request body string sent to the API |
| No request body | Empty string |
For GET requests, parameter order and URL encoding must match the actual request. For JSON requests, use a compact single-line body and do not format or serialize it again after signing.
Signing Example
This example calls the order query API:
SK = sk_xxxxxxxxxxxx
X-Timestamp = 1747555200
path = /owlproxy/api/openApi/vcProxy/queryByorderIdList
bodyOrQuery = {"orderIds":["ORDER-00001"]}
String to sign:
sk_xxxxxxxxxxxx1747555200/owlproxy/api/openApi/vcProxy/queryByorderIdList{"orderIds":["ORDER-00001"]}
Calculate SHA-256 over the UTF-8 bytes of this string:
X-Sign = 39c4d29bc564bdfdbffc8fba89bd088d79be40b29eb3a9f5050d007f416ab05e
Examples
curl
timestamp=$(date +%s)
accessKey="ak_xxxxxxxxxxxx"
secretKey="sk_xxxxxxxxxxxx"
path="/owlproxy/api/openApi/vcProxy/queryByorderIdList"
body='{"orderIds":["ORDER-00001"]}'
sign=$(printf "%s" "${secretKey}${timestamp}${path}${body}" | openssl dgst -sha256 -hex | awk '{print $2}')
curl -X POST "https://api.owlproxy.com${path}" \
-H "X-Access-Key: ${accessKey}" \
-H "X-Timestamp: ${timestamp}" \
-H "X-Sign: ${sign}" \
-H "Content-Type: application/json" \
--data-binary "${body}"
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class OwlProxyOpenApiExample {
private static final String BASE_URL = "https://api.owlproxy.com";
private static final String ACCESS_KEY = "ak_xxxxxxxxxxxx";
private static final String SECRET_KEY = "sk_xxxxxxxxxxxx";
private static String sha256Hex(String value) throws Exception {
byte[] digest = MessageDigest.getInstance("SHA-256")
.digest(value.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(digest.length * 2);
for (byte item : digest) {
hex.append(String.format("%02x", item & 0xff));
}
return hex.toString();
}
public static void main(String[] args) throws Exception {
String path = "/owlproxy/api/openApi/vcProxy/queryByorderIdList";
String body = "{\"orderIds\":[\"ORDER-00001\"]}";
String timestamp = String.valueOf(System.currentTimeMillis() / 1000L);
String sign = sha256Hex(SECRET_KEY + timestamp + path + body);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + path))
.header("X-Access-Key", ACCESS_KEY)
.header("X-Timestamp", timestamp)
.header("X-Sign", sign)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
System.out.println(response.body());
}
}
Python
import hashlib
import time
import requests
baseUrl = "https://api.owlproxy.com"
accessKey = "ak_xxxxxxxxxxxx"
secretKey = "sk_xxxxxxxxxxxx"
path = "/owlproxy/api/openApi/vcProxy/queryByorderIdList"
body = '{"orderIds":["ORDER-00001"]}'
timestamp = str(int(time.time()))
sign = hashlib.sha256(
(secretKey + timestamp + path + body).encode("utf-8")
).hexdigest()
response = requests.post(
baseUrl + path,
headers={
"X-Access-Key": accessKey,
"X-Timestamp": timestamp,
"X-Sign": sign,
"Content-Type": "application/json",
},
data=body.encode("utf-8"),
)
print(response.text)
Node.js
const crypto = require("crypto");
const baseUrl = "https://api.owlproxy.com";
const accessKey = "ak_xxxxxxxxxxxx";
const secretKey = "sk_xxxxxxxxxxxx";
const path = "/owlproxy/api/openApi/vcProxy/queryByorderIdList";
const body = '{"orderIds":["ORDER-00001"]}';
const timestamp = Math.floor(Date.now() / 1000).toString();
const sign = crypto
.createHash("sha256")
.update(secretKey + timestamp + path + body, "utf8")
.digest("hex");
async function main() {
const response = await fetch(baseUrl + path, {
method: "POST",
headers: {
"X-Access-Key": accessKey,
"X-Timestamp": timestamp,
"X-Sign": sign,
"Content-Type": "application/json",
},
body,
});
console.log(await response.text());
}
main().catch(console.error);
GET Example
Request:
GET /owlproxy/api/openApi/vcProxyShop/getProxyTypeList?proxyMode=1
Signing fields:
path = /owlproxy/api/openApi/vcProxyShop/getProxyTypeList
bodyOrQuery = proxyMode=1
Common Error Codes
| Code | Description | Suggested action |
|---|---|---|
2019 | Signature verification failed | Check path, parameter order, and request body |
2031 | Invalid key | Check the Access Key ID |
2032 | Authentication headers missing | Add all three V2 headers |
2033 | Timestamp expired or malformed | Use the current Unix timestamp in seconds |
FAQ
How do I sign an empty POST body?
Use an empty string for bodyOrQuery; the string to sign ends with path.
Is X-Sign case-sensitive?
Use a 64-character lowercase hexadecimal string.
Why does verification fail with the same parameters?
Check the full request path, timestamp unit, GET parameter order, URL encoding, and the exact JSON string sent in the request.
Data Encryption/Decryption Example
Java AES GCM Decryption
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class AESUtils {
private static final String AES = "AES";
private static final String AES_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH = 16;
private static final int GCM_IV_LENGTH = 12;
private static SecretKeySpec getKeyFromPassword(String password) throws NoSuchAlgorithmException {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes());
return new SecretKeySpec(key, AES);
}
public static byte[] generateIv() {
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv);
return iv;
}
public static String encrypt(String input, String key) {
try {
SecretKeySpec secretKeySpec = getKeyFromPassword(key);
byte[] iv = generateIv();
Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
byte[] cipherText = cipher.doFinal(input.getBytes());
String ivString = Base64.getEncoder().encodeToString(iv);
String cipherTextString = Base64.getEncoder().encodeToString(cipherText);
return ivString + ":" + cipherTextString;
} catch (Exception e) {
return null;
}
}
public static String decrypt(String encryptedData, String key) {
try {
SecretKeySpec secretKeySpec = getKeyFromPassword(key);
String[] parts = encryptedData.split(":");
byte[] iv = Base64.getDecoder().decode(parts[0]);
byte[] cipherText = Base64.getDecoder().decode(parts[1]);
Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
return new String(cipher.doFinal(cipherText));
} catch (Exception e) {
return null;
}
}
}