Production API Documentation

HStore API Docs

Base URLhttps://hstore.site/api/v1 Versionv1 Default Limit60/min
Auth
HMAC-SHA256
Timestamp Window
5 minutes
Idempotency
24 hours
Webhook Attempts
Up to 6

Quick Start

  1. Create one API key per integration from API Keys.
  2. Keep signing on the server only. Never expose the secret in frontend code.
  3. Read /catalog, refresh the selected product with /products/{id}, then create the order.
  4. Always send a stable external_order_id and Idempotency-Key on POST /orders.
  5. Use /orders/{id} or /orders/lookup to reconcile after retries or timeouts.

Authentication and Request Model

Header Required Description
X-API-Key Yes Public key identifier.
X-Timestamp Yes Unix timestamp in seconds. Must be within 5 minutes.
X-Nonce Yes Unique random string, used once per key to block replay.
X-Signature Yes Lowercase hex HMAC-SHA256 over the canonical request.
Idempotency-Key POST /orders Required on order creation. Reuse it when retrying the same purchase.
Origin / Referer Only if allowed domains are configured Must match one configured domain exactly when domain restriction is enabled.
Canonical String
METHOD
PATH
QUERY
TIMESTAMP
NONCE
BODY_HASH
Rules
  • PATH must match the exact request path, including /api/v1.
  • QUERY is the raw query string without ?, in the same order you send.
  • BODY_HASH is empty for GET and SHA256 of the exact raw JSON body for POST.
  • X-Signature must be lowercase hex HMAC-SHA256.
  • If allowed domains are configured, Origin or Referer must match exactly.

Endpoints

Method Path Description
GET /api/v1/ API metadata and supported webhook events.
GET /api/v1/catalog List active products with pagination and filters.
GET /api/v1/products/{id} Get full product details before checkout.
GET /api/v1/balance Read current wallet balance and pending balance.
POST /api/v1/orders Create and charge an order using Main Wallet.
GET /api/v1/orders/{id} Read an order by HStore order ID.
GET /api/v1/orders/lookup?external_order_id=... Recover an order using your own external reference.

Examples

API metadata
curl -X GET 'https://hstore.site/api/v1/'
$response = file_get_contents('https://hstore.site/api/v1/');
echo $response;
const response = await fetch("https://hstore.site/api/v1/");
console.log(await response.json());
import requests

print(requests.get("https://hstore.site/api/v1/").json())
Invoke-RestMethod -Uri "https://hstore.site/api/v1/" -Method Get | ConvertTo-Json -Depth 20
Example response
{
    "success": true,
    "data": {
        "name": "HStore Partner API",
        "version": "v1",
        "docs_url": "https://hstore.site/api-doc",
        "webhooks": {
            "supported_events": [
                "product.stock_changed",
                "product.price_changed",
                "order.created",
                "order.delivered"
            ]
        }
    }
}
List catalog
API_KEY="hs_pk_xxxxxxxxxxxxxxxx"
API_SECRET="hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
METHOD="GET"
PATH="/api/v1/catalog"
QUERY="page=1&limit=20"
TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=""
CANONICAL="$METHOD
$PATH
$QUERY
$TIMESTAMP
$NONCE
$BODY_HASH"
SIGNATURE=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed "s/^.* //")

curl -X GET 'https://hstore.site/api/v1/catalog?page=1&limit=20' \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGNATURE"
$apiKey = 'hs_pk_xxxxxxxxxxxxxxxx';
$apiSecret = 'hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method = 'GET';
$path = '/api/v1/catalog';
$query = 'page=1&limit=20';
$body = '';
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$bodyHash = $body === '' ? '' : hash('sha256', $body);
$canonical = implode("\n", [$method, $path, $query, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $canonical, $apiSecret);

$headers = [
    'X-API-Key: ' . $apiKey,
    'X-Timestamp: ' . $timestamp,
    'X-Nonce: ' . $nonce,
    'X-Signature: ' . $signature,
];

$ch = curl_init('https://hstore.site/api/v1/catalog?page=1&limit=20');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => $headers,
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
import crypto from "node:crypto";

const apiKey = "hs_pk_xxxxxxxxxxxxxxxx";
const apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const method = "GET";
const path = "/api/v1/catalog";
const query = "page=1&limit=20";
const rawBody = "";
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const bodyHash = rawBody ? crypto.createHash("sha256").update(rawBody).digest("hex") : "";
const canonical = [method, path, query, timestamp, nonce, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", apiSecret).update(canonical).digest("hex");

const headers = {
  "X-API-Key": apiKey,
  "X-Timestamp": timestamp,
  "X-Nonce": nonce,
  "X-Signature": signature,
};

const response = await fetch("https://hstore.site/api/v1/catalog?page=1&limit=20", {
  method,
  headers,
});

console.log(await response.json());
import hashlib
import hmac
import json
import secrets
import time
import requests

api_key = "hs_pk_xxxxxxxxxxxxxxxx"
api_secret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
method = "GET"
path = "/api/v1/catalog"
query = "page=1&limit=20"
raw_body = ""
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body_hash = hashlib.sha256(raw_body.encode()).hexdigest() if raw_body else ""
canonical = "\n".join([method, path, query, timestamp, nonce, body_hash])
signature = hmac.new(api_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

headers = {
    "X-API-Key": api_key,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
}

response = requests.request(
    method,
    "https://hstore.site/api/v1/catalog?page=1&limit=20",
    headers=headers,
)

print(response.json())
$apiKey = "hs_pk_xxxxxxxxxxxxxxxx"
$apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$method = "GET"
$path = "/api/v1/catalog"
$query = "page=1&limit=20"
$body = ""
$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$nonce = [guid]::NewGuid().ToString("N")
$bodyHash = if ($body -ne "") {
    [System.BitConverter]::ToString(([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($body)))).Replace("-", "").ToLower()
} else {
    ""
}
$canonical = "$method`n$path`n$query`n$timestamp`n$nonce`n$bodyHash"
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($apiSecret))
$signature = -join ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($canonical)) | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "X-API-Key" = $apiKey
    "X-Timestamp" = "$timestamp"
    "X-Nonce" = $nonce
    "X-Signature" = $signature
}

$params = @{
    Uri = "https://hstore.site/api/v1/catalog?page=1&limit=20"
    Method = $method
    Headers = $headers
}

Invoke-RestMethod @params | ConvertTo-Json -Depth 20
Example response
{
    "success": true,
    "data": {
        "items": [
            {
                "id": 381,
                "name": "High-Quality Long-Term (Hotmail) Usable after skipping the first 7 days.",
                "slug": "high-quality-long-term-hotmail-usable-after-skipping-the-first-7-days",
                "short_description": "High-Quality Long-Term (Hotmail) Usable after skipping the first 7 days.",
                "price": 0.02,
                "currency": "USD",
                "delivery_type": "auto",
                "stock_available": 7722,
                "product_url": "https://hstore.site/product/381",
                "updated_at": "2026-04-10 15:15:29"
            }
        ],
        "pagination": {
            "page": 1,
            "limit": 20,
            "total": 726,
            "pages": 37
        }
    }
}
Get one product
API_KEY="hs_pk_xxxxxxxxxxxxxxxx"
API_SECRET="hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
METHOD="GET"
PATH="/api/v1/products/381"
QUERY=""
TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=""
CANONICAL="$METHOD
$PATH
$QUERY
$TIMESTAMP
$NONCE
$BODY_HASH"
SIGNATURE=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed "s/^.* //")

curl -X GET 'https://hstore.site/api/v1/products/381' \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGNATURE"
$apiKey = 'hs_pk_xxxxxxxxxxxxxxxx';
$apiSecret = 'hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method = 'GET';
$path = '/api/v1/products/381';
$query = '';
$body = '';
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$bodyHash = $body === '' ? '' : hash('sha256', $body);
$canonical = implode("\n", [$method, $path, $query, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $canonical, $apiSecret);

$headers = [
    'X-API-Key: ' . $apiKey,
    'X-Timestamp: ' . $timestamp,
    'X-Nonce: ' . $nonce,
    'X-Signature: ' . $signature,
];

$ch = curl_init('https://hstore.site/api/v1/products/381');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => $headers,
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
import crypto from "node:crypto";

const apiKey = "hs_pk_xxxxxxxxxxxxxxxx";
const apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const method = "GET";
const path = "/api/v1/products/381";
const query = "";
const rawBody = "";
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const bodyHash = rawBody ? crypto.createHash("sha256").update(rawBody).digest("hex") : "";
const canonical = [method, path, query, timestamp, nonce, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", apiSecret).update(canonical).digest("hex");

const headers = {
  "X-API-Key": apiKey,
  "X-Timestamp": timestamp,
  "X-Nonce": nonce,
  "X-Signature": signature,
};

const response = await fetch("https://hstore.site/api/v1/products/381", {
  method,
  headers,
});

console.log(await response.json());
import hashlib
import hmac
import json
import secrets
import time
import requests

api_key = "hs_pk_xxxxxxxxxxxxxxxx"
api_secret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
method = "GET"
path = "/api/v1/products/381"
query = ""
raw_body = ""
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body_hash = hashlib.sha256(raw_body.encode()).hexdigest() if raw_body else ""
canonical = "\n".join([method, path, query, timestamp, nonce, body_hash])
signature = hmac.new(api_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

headers = {
    "X-API-Key": api_key,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
}

response = requests.request(
    method,
    "https://hstore.site/api/v1/products/381",
    headers=headers,
)

print(response.json())
$apiKey = "hs_pk_xxxxxxxxxxxxxxxx"
$apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$method = "GET"
$path = "/api/v1/products/381"
$query = ""
$body = ""
$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$nonce = [guid]::NewGuid().ToString("N")
$bodyHash = if ($body -ne "") {
    [System.BitConverter]::ToString(([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($body)))).Replace("-", "").ToLower()
} else {
    ""
}
$canonical = "$method`n$path`n$query`n$timestamp`n$nonce`n$bodyHash"
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($apiSecret))
$signature = -join ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($canonical)) | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "X-API-Key" = $apiKey
    "X-Timestamp" = "$timestamp"
    "X-Nonce" = $nonce
    "X-Signature" = $signature
}

$params = @{
    Uri = "https://hstore.site/api/v1/products/381"
    Method = $method
    Headers = $headers
}

Invoke-RestMethod @params | ConvertTo-Json -Depth 20
Example response
{
    "success": true,
    "data": {
        "id": 381,
        "name": "High-Quality Long-Term (Hotmail) Usable after skipping the first 7 days.",
        "slug": "high-quality-long-term-hotmail-usable-after-skipping-the-first-7-days",
        "short_description": "High-Quality Long-Term (Hotmail) Usable after skipping the first 7 days.",
        "price": 0.02,
        "currency": "USD",
        "delivery_type": "auto",
        "stock_available": 7722,
        "product_url": "https://hstore.site/product/381",
        "updated_at": "2026-04-10 15:15:29",
        "description": "Format includes Hotmail:password. Only supports web login.",
        "price_tiers": [
            {
                "min_quantity": 100,
                "unit_price": 0.019
            },
            {
                "min_quantity": 500,
                "unit_price": 0.018
            }
        ],
        "rules": {
            "delivery_type": "auto",
            "instant_delivery": true,
            "delivery_data_exposed": false
        }
    }
}
Read balance
API_KEY="hs_pk_xxxxxxxxxxxxxxxx"
API_SECRET="hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
METHOD="GET"
PATH="/api/v1/balance"
QUERY=""
TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=""
CANONICAL="$METHOD
$PATH
$QUERY
$TIMESTAMP
$NONCE
$BODY_HASH"
SIGNATURE=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed "s/^.* //")

curl -X GET 'https://hstore.site/api/v1/balance' \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGNATURE"
$apiKey = 'hs_pk_xxxxxxxxxxxxxxxx';
$apiSecret = 'hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method = 'GET';
$path = '/api/v1/balance';
$query = '';
$body = '';
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$bodyHash = $body === '' ? '' : hash('sha256', $body);
$canonical = implode("\n", [$method, $path, $query, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $canonical, $apiSecret);

$headers = [
    'X-API-Key: ' . $apiKey,
    'X-Timestamp: ' . $timestamp,
    'X-Nonce: ' . $nonce,
    'X-Signature: ' . $signature,
];

$ch = curl_init('https://hstore.site/api/v1/balance');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => $headers,
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
import crypto from "node:crypto";

const apiKey = "hs_pk_xxxxxxxxxxxxxxxx";
const apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const method = "GET";
const path = "/api/v1/balance";
const query = "";
const rawBody = "";
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const bodyHash = rawBody ? crypto.createHash("sha256").update(rawBody).digest("hex") : "";
const canonical = [method, path, query, timestamp, nonce, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", apiSecret).update(canonical).digest("hex");

const headers = {
  "X-API-Key": apiKey,
  "X-Timestamp": timestamp,
  "X-Nonce": nonce,
  "X-Signature": signature,
};

const response = await fetch("https://hstore.site/api/v1/balance", {
  method,
  headers,
});

console.log(await response.json());
import hashlib
import hmac
import json
import secrets
import time
import requests

api_key = "hs_pk_xxxxxxxxxxxxxxxx"
api_secret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
method = "GET"
path = "/api/v1/balance"
query = ""
raw_body = ""
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body_hash = hashlib.sha256(raw_body.encode()).hexdigest() if raw_body else ""
canonical = "\n".join([method, path, query, timestamp, nonce, body_hash])
signature = hmac.new(api_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

headers = {
    "X-API-Key": api_key,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
}

response = requests.request(
    method,
    "https://hstore.site/api/v1/balance",
    headers=headers,
)

print(response.json())
$apiKey = "hs_pk_xxxxxxxxxxxxxxxx"
$apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$method = "GET"
$path = "/api/v1/balance"
$query = ""
$body = ""
$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$nonce = [guid]::NewGuid().ToString("N")
$bodyHash = if ($body -ne "") {
    [System.BitConverter]::ToString(([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($body)))).Replace("-", "").ToLower()
} else {
    ""
}
$canonical = "$method`n$path`n$query`n$timestamp`n$nonce`n$bodyHash"
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($apiSecret))
$signature = -join ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($canonical)) | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "X-API-Key" = $apiKey
    "X-Timestamp" = "$timestamp"
    "X-Nonce" = $nonce
    "X-Signature" = $signature
}

$params = @{
    Uri = "https://hstore.site/api/v1/balance"
    Method = $method
    Headers = $headers
}

Invoke-RestMethod @params | ConvertTo-Json -Depth 20
Example response
{
    "success": true,
    "data": {
        "balance": 4.887,
        "pending_balance": 0,
        "currency": "USD"
    }
}
Create order
API_KEY="hs_pk_xxxxxxxxxxxxxxxx"
API_SECRET="hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
METHOD="POST"
PATH="/api/v1/orders"
QUERY=""
BODY='{"product_id":381,"quantity":1,"external_order_id":"shop-order-100045"}'
TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=$(printf "%s" "$BODY" | openssl dgst -sha256 -hex | sed "s/^.* //")
CANONICAL="$METHOD
$PATH
$QUERY
$TIMESTAMP
$NONCE
$BODY_HASH"
SIGNATURE=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed "s/^.* //")
IDEMPOTENCY_KEY="idem-shop-order-100045"

curl -X POST 'https://hstore.site/api/v1/orders' \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGNATURE" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data "$BODY"
$apiKey = 'hs_pk_xxxxxxxxxxxxxxxx';
$apiSecret = 'hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method = 'POST';
$path = '/api/v1/orders';
$query = '';
$body = '{"product_id":381,"quantity":1,"external_order_id":"shop-order-100045"}';
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$bodyHash = $body === '' ? '' : hash('sha256', $body);
$canonical = implode("\n", [$method, $path, $query, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $canonical, $apiSecret);

$headers = [
    'X-API-Key: ' . $apiKey,
    'X-Timestamp: ' . $timestamp,
    'X-Nonce: ' . $nonce,
    'X-Signature: ' . $signature,
    'Content-Type: application/json',
    'Idempotency-Key: idem-shop-order-100045',
];

$ch = curl_init('https://hstore.site/api/v1/orders');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $body,
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
import crypto from "node:crypto";

const apiKey = "hs_pk_xxxxxxxxxxxxxxxx";
const apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const method = "POST";
const path = "/api/v1/orders";
const query = "";
const body = {"product_id":381,"quantity":1,"external_order_id":"shop-order-100045"};
const rawBody = JSON.stringify(body);
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const bodyHash = rawBody ? crypto.createHash("sha256").update(rawBody).digest("hex") : "";
const canonical = [method, path, query, timestamp, nonce, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", apiSecret).update(canonical).digest("hex");

const headers = {
  "X-API-Key": apiKey,
  "X-Timestamp": timestamp,
  "X-Nonce": nonce,
  "X-Signature": signature,
  "Content-Type": "application/json",
  "Idempotency-Key": "idem-shop-order-100045",
};

const response = await fetch("https://hstore.site/api/v1/orders", {
  method,
  headers,
  body: rawBody,
});

console.log(await response.json());
import hashlib
import hmac
import json
import secrets
import time
import requests

api_key = "hs_pk_xxxxxxxxxxxxxxxx"
api_secret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
method = "POST"
path = "/api/v1/orders"
query = ""
body = {"product_id":381,"quantity":1,"external_order_id":"shop-order-100045"}
raw_body = json.dumps(body, separators=(",", ":"))
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body_hash = hashlib.sha256(raw_body.encode()).hexdigest() if raw_body else ""
canonical = "\n".join([method, path, query, timestamp, nonce, body_hash])
signature = hmac.new(api_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

headers = {
    "X-API-Key": api_key,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
    "Content-Type": "application/json",
    "Idempotency-Key": "idem-shop-order-100045",
}

response = requests.request(
    method,
    "https://hstore.site/api/v1/orders",
    headers=headers,
    data=raw_body,
)

print(response.json())
$apiKey = "hs_pk_xxxxxxxxxxxxxxxx"
$apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$method = "POST"
$path = "/api/v1/orders"
$query = ""
$body = '{"product_id":381,"quantity":1,"external_order_id":"shop-order-100045"}'
$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$nonce = [guid]::NewGuid().ToString("N")
$bodyHash = if ($body -ne "") {
    [System.BitConverter]::ToString(([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($body)))).Replace("-", "").ToLower()
} else {
    ""
}
$canonical = "$method`n$path`n$query`n$timestamp`n$nonce`n$bodyHash"
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($apiSecret))
$signature = -join ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($canonical)) | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "X-API-Key" = $apiKey
    "X-Timestamp" = "$timestamp"
    "X-Nonce" = $nonce
    "X-Signature" = $signature
    "Idempotency-Key" = "idem-shop-order-100045"
}

$params = @{
    Uri = "https://hstore.site/api/v1/orders"
    Method = $method
    Headers = $headers
    Body = $body
    ContentType = "application/json"
}

Invoke-RestMethod @params | ConvertTo-Json -Depth 20
Example response
{
    "success": true,
    "data": {
        "id": 16591,
        "order_number": "ORD-CFFD69-16591",
        "external_order_id": "shop-order-100045",
        "status": "delivered",
        "quantity": 1,
        "unit_price": 0.02,
        "total_amount": 0.02,
        "currency": "USD",
        "delivery_type": "auto",
        "delivery": {
            "available": true,
            "items": [
                "[email protected]:password123"
            ]
        },
        "links": {
            "web_url": "https://hstore.site/order/16591",
            "api_url": "https://hstore.site/api/v1/orders/16591"
        }
    }
}
Lookup order by external ID
API_KEY="hs_pk_xxxxxxxxxxxxxxxx"
API_SECRET="hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
METHOD="GET"
PATH="/api/v1/orders/lookup"
QUERY="external_order_id=shop-order-100045"
TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 16)
BODY_HASH=""
CANONICAL="$METHOD
$PATH
$QUERY
$TIMESTAMP
$NONCE
$BODY_HASH"
SIGNATURE=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | sed "s/^.* //")

curl -X GET 'https://hstore.site/api/v1/orders/lookup?external_order_id=shop-order-100045' \
  -H "X-API-Key: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGNATURE"
$apiKey = 'hs_pk_xxxxxxxxxxxxxxxx';
$apiSecret = 'hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$method = 'GET';
$path = '/api/v1/orders/lookup';
$query = 'external_order_id=shop-order-100045';
$body = '';
$timestamp = (string) time();
$nonce = bin2hex(random_bytes(16));
$bodyHash = $body === '' ? '' : hash('sha256', $body);
$canonical = implode("\n", [$method, $path, $query, $timestamp, $nonce, $bodyHash]);
$signature = hash_hmac('sha256', $canonical, $apiSecret);

$headers = [
    'X-API-Key: ' . $apiKey,
    'X-Timestamp: ' . $timestamp,
    'X-Nonce: ' . $nonce,
    'X-Signature: ' . $signature,
];

$ch = curl_init('https://hstore.site/api/v1/orders/lookup?external_order_id=shop-order-100045');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => $headers,
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
import crypto from "node:crypto";

const apiKey = "hs_pk_xxxxxxxxxxxxxxxx";
const apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const method = "GET";
const path = "/api/v1/orders/lookup";
const query = "external_order_id=shop-order-100045";
const rawBody = "";
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = crypto.randomBytes(16).toString("hex");
const bodyHash = rawBody ? crypto.createHash("sha256").update(rawBody).digest("hex") : "";
const canonical = [method, path, query, timestamp, nonce, bodyHash].join("\n");
const signature = crypto.createHmac("sha256", apiSecret).update(canonical).digest("hex");

const headers = {
  "X-API-Key": apiKey,
  "X-Timestamp": timestamp,
  "X-Nonce": nonce,
  "X-Signature": signature,
};

const response = await fetch("https://hstore.site/api/v1/orders/lookup?external_order_id=shop-order-100045", {
  method,
  headers,
});

console.log(await response.json());
import hashlib
import hmac
import json
import secrets
import time
import requests

api_key = "hs_pk_xxxxxxxxxxxxxxxx"
api_secret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
method = "GET"
path = "/api/v1/orders/lookup"
query = "external_order_id=shop-order-100045"
raw_body = ""
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body_hash = hashlib.sha256(raw_body.encode()).hexdigest() if raw_body else ""
canonical = "\n".join([method, path, query, timestamp, nonce, body_hash])
signature = hmac.new(api_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

headers = {
    "X-API-Key": api_key,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
}

response = requests.request(
    method,
    "https://hstore.site/api/v1/orders/lookup?external_order_id=shop-order-100045",
    headers=headers,
)

print(response.json())
$apiKey = "hs_pk_xxxxxxxxxxxxxxxx"
$apiSecret = "hs_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$method = "GET"
$path = "/api/v1/orders/lookup"
$query = "external_order_id=shop-order-100045"
$body = ""
$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
$nonce = [guid]::NewGuid().ToString("N")
$bodyHash = if ($body -ne "") {
    [System.BitConverter]::ToString(([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($body)))).Replace("-", "").ToLower()
} else {
    ""
}
$canonical = "$method`n$path`n$query`n$timestamp`n$nonce`n$bodyHash"
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($apiSecret))
$signature = -join ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($canonical)) | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "X-API-Key" = $apiKey
    "X-Timestamp" = "$timestamp"
    "X-Nonce" = $nonce
    "X-Signature" = $signature
}

$params = @{
    Uri = "https://hstore.site/api/v1/orders/lookup?external_order_id=shop-order-100045"
    Method = $method
    Headers = $headers
}

Invoke-RestMethod @params | ConvertTo-Json -Depth 20
Example response
{
    "success": true,
    "data": {
        "id": 16591,
        "order_number": "ORD-CFFD69-16591",
        "external_order_id": "shop-order-100045",
        "status": "delivered",
        "quantity": 1,
        "unit_price": 0.02,
        "total_amount": 0.02,
        "currency": "USD",
        "delivery_type": "auto",
        "delivery": {
            "available": true,
            "items": [
                "[email protected]:password123"
            ]
        },
        "links": {
            "web_url": "https://hstore.site/order/16591",
            "api_url": "https://hstore.site/api/v1/orders/16591"
        }
    }
}
Order Safety
  • Every POST /orders must include both external_order_id and Idempotency-Key.
  • Reusing the same idempotency key with the same payload returns the same order response.
  • Reusing the same key or external order ID with a different payload returns 409.
  • After an uncertain retry, reconcile with GET /orders/lookup.

Webhooks

Webhooks are configured per key from API Keys. Each key has its own URL, secret, subscribed events, and retry queue.

Delivery headers
  • X-HStore-Event-Id stable event ID.
  • X-HStore-Delivery-Id unique delivery attempt ID.
  • X-HStore-Webhook-Event event name.
  • X-HStore-Webhook-Timestamp Unix timestamp.
  • X-HStore-Signature-Version currently v1.
  • X-HStore-Webhook-Signature lowercase hex HMAC-SHA256.
Delivery rules
  • Only public https:// endpoints are allowed.
  • Localhost, loopback, and private IP destinations are rejected.
  • Any 2xx response marks a delivery successful.
  • Non-2xx responses and network failures are retried automatically.
  • Your receiver should be idempotent by X-HStore-Delivery-Id.
Supported events
  • product.created - Product became available
  • product.updated - Product details updated
  • product.deleted - Product removed or hidden
  • product.stock_changed - Product stock changed
  • product.price_changed - Product price changed
  • product.tiers_changed - Bulk price tiers changed
  • order.created - Order created
  • order.updated - Order updated
  • order.delivered - Order delivered
  • order.completed - Order completed
  • order.refunded - Order refunded
  • order.disputed - Order disputed
Example webhook payload
{
    "id": "evt_9f2d4f7e5c2a4d2abf6f19cd7ad91234",
    "type": "product.stock_changed",
    "created_at": "2026-04-10T12:30:15Z",
    "livemode": true,
    "api_version": "v1",
    "data": {
        "product": {
            "id": 381,
            "name": "High-Quality Long-Term (Hotmail) Usable after skipping the first 7 days.",
            "price": 0.02,
            "stock_available": 7700
        },
        "previous": {
            "id": 381,
            "stock_available": 7722
        },
        "changes": {
            "stock_available": {
                "before": 7722,
                "after": 7700
            }
        },
        "meta": {
            "source": "purchase"
        }
    }
}
Verification canonical string
TIMESTAMP
DELIVERY_ID
EVENT_ID
EVENT_TYPE
BODY_SHA256
# Webhook signature verification must run inside your app.
# Use this cURL command only to replay a payload into your receiver.
curl -X POST 'https://your-app.example.com/hstore-webhook' \
  -H "Content-Type: application/json" \
  -H "X-HStore-Webhook-Timestamp: 1712750000" \
  -H "X-HStore-Delivery-Id: dlv_xxxxxxxxxxxxxxxx" \
  -H "X-HStore-Event-Id: evt_xxxxxxxxxxxxxxxx" \
  -H "X-HStore-Webhook-Event: product.stock_changed" \
  -H "X-HStore-Webhook-Signature: <hex-hmac-sha256>" \
  --data "{\"id\":\"evt_xxx\",\"type\":\"product.stock_changed\"}"
$webhookSecret = 'whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$timestamp = $_SERVER['HTTP_X_HSTORE_WEBHOOK_TIMESTAMP'] ?? '';
$deliveryId = $_SERVER['HTTP_X_HSTORE_DELIVERY_ID'] ?? '';
$eventId = $_SERVER['HTTP_X_HSTORE_EVENT_ID'] ?? '';
$eventType = $_SERVER['HTTP_X_HSTORE_WEBHOOK_EVENT'] ?? '';
$signature = strtolower($_SERVER['HTTP_X_HSTORE_WEBHOOK_SIGNATURE'] ?? '');
$rawBody = file_get_contents('php://input') ?: '';

$bodyHash = hash('sha256', $rawBody);
$canonical = implode("\n", [$timestamp, $deliveryId, $eventId, $eventType, $bodyHash]);
$expected = hash_hmac('sha256', $canonical, $webhookSecret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid webhook signature');
}
import crypto from "node:crypto";

const webhookSecret = "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const rawBody = await getRawBody(req);
const timestamp = req.get("X-HStore-Webhook-Timestamp") ?? "";
const deliveryId = req.get("X-HStore-Delivery-Id") ?? "";
const eventId = req.get("X-HStore-Event-Id") ?? "";
const eventType = req.get("X-HStore-Webhook-Event") ?? "";
const signature = (req.get("X-HStore-Webhook-Signature") ?? "").toLowerCase();

const bodyHash = crypto.createHash("sha256").update(rawBody).digest("hex");
const canonical = [timestamp, deliveryId, eventId, eventType, bodyHash].join("\n");
const expected = crypto.createHmac("sha256", webhookSecret).update(canonical).digest("hex");

if (expected !== signature) {
  res.status(401).send("Invalid webhook signature");
  return;
}
import hashlib
import hmac

webhook_secret = "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
raw_body = request.get_data(as_text=True)
timestamp = request.headers.get("X-HStore-Webhook-Timestamp", "")
delivery_id = request.headers.get("X-HStore-Delivery-Id", "")
event_id = request.headers.get("X-HStore-Event-Id", "")
event_type = request.headers.get("X-HStore-Webhook-Event", "")
signature = request.headers.get("X-HStore-Webhook-Signature", "").lower()

body_hash = hashlib.sha256(raw_body.encode()).hexdigest()
canonical = "\n".join([timestamp, delivery_id, event_id, event_type, body_hash])
expected = hmac.new(webhook_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

if not hmac.compare_digest(expected, signature):
    abort(401, "Invalid webhook signature")
$webhookSecret = "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$timestamp = $Request.Headers["X-HStore-Webhook-Timestamp"]
$deliveryId = $Request.Headers["X-HStore-Delivery-Id"]
$eventId = $Request.Headers["X-HStore-Event-Id"]
$eventType = $Request.Headers["X-HStore-Webhook-Event"]
$signature = ($Request.Headers["X-HStore-Webhook-Signature"] ?? "").ToLower()
$rawBody = $Body

$bodyHash = [System.BitConverter]::ToString(([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($rawBody)))).Replace("-", "").ToLower()
$canonical = "$timestamp`n$deliveryId`n$eventId`n$eventType`n$bodyHash"
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes($webhookSecret))
$expected = -join ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($canonical)) | ForEach-Object { $_.ToString("x2") })

if ($expected -ne $signature) {
    throw "Invalid webhook signature"
}

Errors and Rate Limits

{
    "success": false,
    "error": {
        "code": "invalid_signature",
        "message": "Signature does not match the canonical request.",
        "status": 401
    }
}
Rate limit headers
  • X-RateLimit-Limit maximum requests per minute for the key.
  • X-RateLimit-Remaining remaining requests in the current window.
  • X-RateLimit-Reset Unix timestamp when the current window resets.
  • Retry-After is returned only on 429.
  • Header names are case-insensitive even if an intermediary lowercases them.
HTTP Meaning Recommended action
200 Request succeeded. Use the payload as current truth.
201 Order created and current payload returned. Persist order IDs and delivery state.
202 Order accepted but still processing. Poll the order endpoint or wait for a webhook.
400 Validation or request format error. Fix the request before retrying.
401 Auth, signature, nonce, or timestamp failure. Regenerate headers and verify signing logic.
402 Insufficient wallet balance. Top up Main Wallet before retrying.
409 Replay, request in progress, or payload conflict. Retry safely using the same purchase identifiers only.
429 Rate limit reached. Back off until Retry-After or X-RateLimit-Reset.
Error code HTTP Meaning
missing_auth 401 One or more required auth headers are missing.
request_expired 401 Timestamp is too old or too far from server time.
invalid_signature 401 The canonical string or secret does not match the request.
origin_not_allowed 403 Origin or Referer host did not match allowed_domains.
missing_idempotency_key 400 POST /orders was sent without Idempotency-Key.
request_in_progress 409 The same purchase is already being processed.
idempotency_conflict 409 The same key or external_order_id was reused with a different payload.
insufficient_balance 402 Wallet balance is too low for the purchase.
product_unavailable 404 Product is inactive, missing, or seller is not active.
insufficient_stock 409 Auto-delivery stock is not sufficient right now.
rate_limited 429 Too many requests for this key in the current window.