Console
Guide

CloudStore

Per-user JSON cloud storage — perfect for game saves, settings, and inventory. Data is isolated by AppID + cloudkey per user.

Overview

EndpointAction
POST /api/cloudstore/set.phpSave or overwrite a JSON value
POST /api/cloudstore/get.phpRetrieve one key or all keys
POST /api/cloudstore/delete.phpDelete a key
POST /api/cloudstore/merge.phpApply partial updates atomically, without overwriting
POST /api/cloudstore/share.phpGrant or revoke another account's access to your CloudStore
CloudStore vs Database: CloudStore data is per-user — each user has their own isolated storage. The Database is per-app (shared across all users).

1. Save data

Call POST /api/cloudstore/set.php with a cloudkey and any JSON value as dataobject. If the key already exists it is overwritten completely.

await fetch('https://vaneltonmedia.com/api/cloudstore/set.php', {
  method: 'POST',
  headers: {
    'Authorization': `${APP_ID}:${APP_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    token: localStorage.getItem('vm_token'),
    cloudkey: 'save-slot-1',
    dataobject: { level: 15, score: 4800, inventory: ['sword', 'shield'] }
  })
});
interface CloudSetBody {
  token: string;
  cloudkey: string;
  dataobject: unknown;
}

async function cloudSet(cloudkey: string, data: unknown): Promise {
  await fetch('https://vaneltonmedia.com/api/cloudstore/set.php', {
    method: 'POST',
    headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: localStorage.getItem('vm_token'), cloudkey, dataobject: data })
  });
}

await cloudSet('save-slot-1', { level: 15, score: 4800 });
function cloudSet(string $cloudkey, mixed $data): array {
    $ch = curl_init('https://vaneltonmedia.com/api/cloudstore/set.php');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: ' . APP_ID . ':' . APP_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'token'      => $_SESSION['vm_token'],
            'cloudkey'   => $cloudkey,
            'dataobject' => $data,
        ]),
    ]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result;
}

cloudSet('save-slot-1', ['level' => 15, 'score' => 4800]);
import requests

def cloud_set(token, cloudkey, data):
    return requests.post(
        'https://vaneltonmedia.com/api/cloudstore/set.php',
        headers={'Authorization': f'{APP_ID}:{APP_KEY}', 'Content-Type': 'application/json'},
        json={'token': token, 'cloudkey': cloudkey, 'dataobject': data}
    ).json()

cloud_set(token, 'save-slot-1', {'level': 15, 'score': 4800})
var _headers = ds_map_create();
ds_map_add(_headers, "Authorization", global.app_id + ":" + global.app_key);
ds_map_add(_headers, "Content-Type",  "application/json");

http_request(
    "https://vaneltonmedia.com/api/cloudstore/set.php",
    "POST", _headers,
    json_stringify({
        token:      global.user_token,
        cloudkey:   "save-slot-1",
        dataobject: { level: 15, score: 4800 }
    })
);
ds_map_destroy(_headers);
APP_ID="com.myapp.game"
APP_KEY="MyAppKey1234567890ABCDE"
TOKEN="YOUR_SESSION_TOKEN"

curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/set.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$TOKEN\",\"cloudkey\":\"save-slot-1\",\"dataobject\":{\"level\":15,\"score\":4800}}"
using System.Net.Http;
using System.Text;
using System.Text.Json;

const string APP_ID  = "com.myapp.game";
const string APP_KEY = "MyAppKey1234567890ABCDE";

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"{APP_ID}:{APP_KEY}");

var payload = JsonSerializer.Serialize(new {
    token      = token,
    cloudkey   = "save-slot-1",
    dataobject = new { level = 15, score = 4800 }
});
using var res = await client.PostAsync(
    "https://vaneltonmedia.com/api/cloudstore/set.php",
    new StringContent(payload, Encoding.UTF8, "application/json"));

using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
bool success = doc.RootElement.GetProperty("success").GetBoolean();
-- Requires: luarocks install luasocket luasec lua-cjson
local http  = require("socket.http")
local ltn12 = require("ltn12")
local json  = require("cjson")

local APP_ID  = "com.myapp.game"
local APP_KEY = "MyAppKey1234567890ABCDE"

local body   = json.encode({
    token      = token,
    cloudkey   = "save-slot-1",
    dataobject = { level = 15, score = 4800 },
})
local chunks = {}
http.request {
    url    = "https://vaneltonmedia.com/api/cloudstore/set.php",
    method = "POST",
    headers = {
        ["Authorization"]  = APP_ID .. ":" .. APP_KEY,
        ["Content-Type"]   = "application/json",
        ["Content-Length"] = #body,
    },
    source = ltn12.source.string(body),
    sink   = ltn12.sink.table(chunks),
}
local result = json.decode(table.concat(chunks))
print(result.success) -- true

Success response:

{ "success": true, "message": "Cloud data saved successfully.", "cloudkey": "save-slot-1" }

2. Load data

Call POST /api/cloudstore/get.php. Omit cloudkey to fetch all keys for this user + app.

// Load a specific key
const res = await fetch('https://vaneltonmedia.com/api/cloudstore/get.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ token: localStorage.getItem('vm_token'), cloudkey: 'save-slot-1' })
});
const { dataobject } = await res.json();
console.log(dataobject.level); // 15

// Load ALL keys
const allRes = await fetch('https://vaneltonmedia.com/api/cloudstore/get.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ token: localStorage.getItem('vm_token') })
});
const { data, count } = await allRes.json();
// data = [{ cloudkey: 'save-slot-1', dataobject: {...} }, ...]
async function cloudGet(cloudkey: string): Promise {
  const res = await fetch('https://vaneltonmedia.com/api/cloudstore/get.php', {
    method: 'POST',
    headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: localStorage.getItem('vm_token'), cloudkey })
  });
  const { dataobject } = await res.json();
  return dataobject as T;
}

const save = await cloudGet<{ level: number; score: number }>('save-slot-1');
function cloudGet(string $cloudkey): mixed {
    $ch = curl_init('https://vaneltonmedia.com/api/cloudstore/get.php');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Authorization: ' . APP_ID . ':' . APP_KEY, 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode(['token' => $_SESSION['vm_token'], 'cloudkey' => $cloudkey]),
    ]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result['dataobject'] ?? null;
}

$save = cloudGet('save-slot-1');
def cloud_get(token, cloudkey=None):
    body = {'token': token}
    if cloudkey:
        body['cloudkey'] = cloudkey
    return requests.post(
        'https://vaneltonmedia.com/api/cloudstore/get.php',
        headers={'Authorization': f'{APP_ID}:{APP_KEY}', 'Content-Type': 'application/json'},
        json=body
    ).json()

save = cloud_get(token, 'save-slot-1')['dataobject']
var _headers = ds_map_create();
ds_map_add(_headers, "Authorization", global.app_id + ":" + global.app_key);
ds_map_add(_headers, "Content-Type",  "application/json");

global.load_req = http_request(
    "https://vaneltonmedia.com/api/cloudstore/get.php",
    "POST", _headers,
    json_stringify({ token: global.user_token, cloudkey: "save-slot-1" })
);
ds_map_destroy(_headers);

// --- In Async HTTP Event ---
if (async_load[? "id"] == global.load_req) {
    var _d = json_parse(async_load[? "result"]);
    var _save = _d.dataobject;
    show_debug_message("Level: " + string(_save.level));
}
TOKEN="YOUR_SESSION_TOKEN"

# Load a specific key
curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/get.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$TOKEN\",\"cloudkey\":\"save-slot-1\"}"

# Load ALL keys (omit cloudkey)
curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/get.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$TOKEN\"}"
async Task CloudGet(string cloudkey)
{
    var payload = JsonSerializer.Serialize(new { token, cloudkey });
    using var res = await client.PostAsync(
        "https://vaneltonmedia.com/api/cloudstore/get.php",
        new StringContent(payload, Encoding.UTF8, "application/json"));

    using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
    return doc.RootElement.GetProperty("dataobject").Clone();
}

var save = await CloudGet("save-slot-1");
local function cloud_get(tok, cloudkey)
    local body   = json.encode({ token = tok, cloudkey = cloudkey })
    local chunks = {}
    http.request {
        url    = "https://vaneltonmedia.com/api/cloudstore/get.php",
        method = "POST",
        headers = {
            ["Authorization"]  = APP_ID .. ":" .. APP_KEY,
            ["Content-Type"]   = "application/json",
            ["Content-Length"] = #body,
        },
        source = ltn12.source.string(body),
        sink   = ltn12.sink.table(chunks),
    }
    return json.decode(table.concat(chunks)).dataobject
end

local save = cloud_get(token, "save-slot-1")
print(save.level) -- 15

3. Delete data

await fetch('https://vaneltonmedia.com/api/cloudstore/delete.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ token: localStorage.getItem('vm_token'), cloudkey: 'save-slot-1' })
});
await fetch('https://vaneltonmedia.com/api/cloudstore/delete.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ token: localStorage.getItem('vm_token'), cloudkey: 'save-slot-1' })
});
$ch = curl_init('https://vaneltonmedia.com/api/cloudstore/delete.php');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Authorization: ' . APP_ID . ':' . APP_KEY, 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['token' => $_SESSION['vm_token'], 'cloudkey' => 'save-slot-1']),
]);
curl_exec($ch);
curl_close($ch);
requests.post(
    'https://vaneltonmedia.com/api/cloudstore/delete.php',
    headers={'Authorization': f'{APP_ID}:{APP_KEY}', 'Content-Type': 'application/json'},
    json={'token': token, 'cloudkey': 'save-slot-1'}
)
var _headers = ds_map_create();
ds_map_add(_headers, "Authorization", global.app_id + ":" + global.app_key);
ds_map_add(_headers, "Content-Type",  "application/json");

http_request(
    "https://vaneltonmedia.com/api/cloudstore/delete.php",
    "POST", _headers,
    json_stringify({ token: global.user_token, cloudkey: "save-slot-1" })
);
ds_map_destroy(_headers);
TOKEN="YOUR_SESSION_TOKEN"

curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/delete.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$TOKEN\",\"cloudkey\":\"save-slot-1\"}"
var payload = JsonSerializer.Serialize(new { token, cloudkey = "save-slot-1" });
using var res = await client.PostAsync(
    "https://vaneltonmedia.com/api/cloudstore/delete.php",
    new StringContent(payload, Encoding.UTF8, "application/json"));
local body   = json.encode({ token = token, cloudkey = "save-slot-1" })
local chunks = {}
http.request {
    url    = "https://vaneltonmedia.com/api/cloudstore/delete.php",
    method = "POST",
    headers = {
        ["Authorization"]  = APP_ID .. ":" .. APP_KEY,
        ["Content-Type"]   = "application/json",
        ["Content-Length"] = #body,
    },
    source = ltn12.source.string(body),
    sink   = ltn12.sink.table(chunks),
}

4. Merge data without overwriting

Call POST /api/cloudstore/merge.php with a list of ops instead of a full dataobject. Each operation (set, increment, append, remove) is applied on the server against the latest value — never against a copy the client might be holding — so concurrent writers to the same cloudkey never stomp on each other.

const res = await fetch('https://vaneltonmedia.com/api/cloudstore/merge.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    token: localStorage.getItem('vm_token'),
    cloudkey: 'item-42',
    ops: [
      { op: 'increment', path: 'qty', delta: -1 },
      { op: 'set', path: 'last_moved_at', value: new Date().toISOString() }
    ]
  })
});
const { dataobject } = await res.json();
console.log(dataobject.qty); // decremented, safe even with concurrent writers
interface MergeOp {
  op: 'set' | 'increment' | 'append' | 'remove';
  path: string;
  value?: unknown;
  delta?: number;
}

async function cloudMerge(cloudkey: string, ops: MergeOp[]): Promise {
  const res = await fetch('https://vaneltonmedia.com/api/cloudstore/merge.php', {
    method: 'POST',
    headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: localStorage.getItem('vm_token'), cloudkey, ops })
  });
  const { dataobject } = await res.json();
  return dataobject as T;
}

await cloudMerge('item-42', [{ op: 'increment', path: 'qty', delta: -1 }]);
function cloudMerge(string $cloudkey, array $ops): array {
    $ch = curl_init('https://vaneltonmedia.com/api/cloudstore/merge.php');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: ' . APP_ID . ':' . APP_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'token'    => $_SESSION['vm_token'],
            'cloudkey' => $cloudkey,
            'ops'      => $ops,
        ]),
    ]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result['dataobject'] ?? [];
}

cloudMerge('item-42', [['op' => 'increment', 'path' => 'qty', 'delta' => -1]]);
def cloud_merge(token, cloudkey, ops):
    return requests.post(
        'https://vaneltonmedia.com/api/cloudstore/merge.php',
        headers={'Authorization': f'{APP_ID}:{APP_KEY}', 'Content-Type': 'application/json'},
        json={'token': token, 'cloudkey': cloudkey, 'ops': ops}
    ).json()

cloud_merge(token, 'item-42', [{'op': 'increment', 'path': 'qty', 'delta': -1}])
var _headers = ds_map_create();
ds_map_add(_headers, "Authorization", global.app_id + ":" + global.app_key);
ds_map_add(_headers, "Content-Type",  "application/json");

var _ops = [{ op: "increment", path: "qty", delta: -1 }];

http_request(
    "https://vaneltonmedia.com/api/cloudstore/merge.php",
    "POST", _headers,
    json_stringify({ token: global.user_token, cloudkey: "item-42", ops: _ops })
);
ds_map_destroy(_headers);
TOKEN="YOUR_SESSION_TOKEN"

curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/merge.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$TOKEN\",\"cloudkey\":\"item-42\",\"ops\":[{\"op\":\"increment\",\"path\":\"qty\",\"delta\":-1}]}"
var payload = JsonSerializer.Serialize(new {
    token = token,
    cloudkey = "item-42",
    ops = new object[] {
        new { op = "increment", path = "qty", delta = -1 }
    }
});
using var res = await client.PostAsync(
    "https://vaneltonmedia.com/api/cloudstore/merge.php",
    new StringContent(payload, Encoding.UTF8, "application/json"));

using var doc = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
var newQty = doc.RootElement.GetProperty("dataobject").GetProperty("qty").GetInt32();
local body   = json.encode({
    token    = token,
    cloudkey = "item-42",
    ops      = { { op = "increment", path = "qty", delta = -1 } },
})
local chunks = {}
http.request {
    url    = "https://vaneltonmedia.com/api/cloudstore/merge.php",
    method = "POST",
    headers = {
        ["Authorization"]  = APP_ID .. ":" .. APP_KEY,
        ["Content-Type"]   = "application/json",
        ["Content-Length"] = #body,
    },
    source = ltn12.source.string(body),
    sink   = ltn12.sink.table(chunks),
}
local result = json.decode(table.concat(chunks))
print(result.dataobject.qty)

Success response:

{
  "success": true,
  "message": "Cloud data merged successfully.",
  "cloudkey": "item-42",
  "dataobject": { "qty": 6, "last_moved_at": "2026-08-07T12:00:00Z" }
}
The response already returns the resulting dataobject — no need to call get.php right after to see the new value. Use merge.php instead of set.php whenever more than one user or device might write to the same cloudkey at the same time.

5. Share access with a collaborator

Call POST /api/cloudstore/share.php (authenticated with the owner's token) to authorize another email. Once authorized, that person's own account can read, write, merge, and delete the owner's CloudStore by passing owner_userid on the other endpoints.

// Owner grants access
await fetch('https://vaneltonmedia.com/api/cloudstore/share.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    token: ownerToken,
    action: 'grant',
    email: 'collaborator@email.com'
  })
});

// Collaborator writes to the owner's CloudStore with their OWN token
await fetch('https://vaneltonmedia.com/api/cloudstore/set.php', {
  method: 'POST',
  headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    token: collaboratorToken,
    owner_userid: ownerUserId,
    cloudkey: 'shared-inventory',
    dataobject: { items: 42 }
  })
});
async function grantAccess(ownerToken: string, email: string): Promise {
  await fetch('https://vaneltonmedia.com/api/cloudstore/share.php', {
    method: 'POST',
    headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: ownerToken, action: 'grant', email })
  });
}

async function listAuthorized(ownerToken: string): Promise {
  const res = await fetch('https://vaneltonmedia.com/api/cloudstore/share.php', {
    method: 'POST',
    headers: { 'Authorization': `${APP_ID}:${APP_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: ownerToken, action: 'list' })
  });
  const { authorized_emails } = await res.json();
  return authorized_emails;
}
function shareGrant(string $ownerToken, string $email): array {
    $ch = curl_init('https://vaneltonmedia.com/api/cloudstore/share.php');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Authorization: ' . APP_ID . ':' . APP_KEY, 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode([
            'token'  => $ownerToken,
            'action' => 'grant',
            'email'  => $email,
        ]),
    ]);
    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result;
}

shareGrant($ownerToken, 'collaborator@email.com');
def share_grant(owner_token, email):
    return requests.post(
        'https://vaneltonmedia.com/api/cloudstore/share.php',
        headers={'Authorization': f'{APP_ID}:{APP_KEY}', 'Content-Type': 'application/json'},
        json={'token': owner_token, 'action': 'grant', 'email': email}
    ).json()

share_grant(owner_token, 'collaborator@email.com')
var _headers = ds_map_create();
ds_map_add(_headers, "Authorization", global.app_id + ":" + global.app_key);
ds_map_add(_headers, "Content-Type",  "application/json");

http_request(
    "https://vaneltonmedia.com/api/cloudstore/share.php",
    "POST", _headers,
    json_stringify({ token: global.owner_token, action: "grant", email: "collaborator@email.com" })
);
ds_map_destroy(_headers);
OWNER_TOKEN="OWNER_SESSION_TOKEN"

# Grant access
curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/share.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$OWNER_TOKEN\",\"action\":\"grant\",\"email\":\"collaborator@email.com\"}"

# List authorized emails
curl -s -X POST "https://vaneltonmedia.com/api/cloudstore/share.php" \
  -H "Authorization: $APP_ID:$APP_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$OWNER_TOKEN\",\"action\":\"list\"}"
var payload = JsonSerializer.Serialize(new {
    token = ownerToken,
    action = "grant",
    email = "collaborator@email.com"
});
await client.PostAsync(
    "https://vaneltonmedia.com/api/cloudstore/share.php",
    new StringContent(payload, Encoding.UTF8, "application/json"));
local body   = json.encode({ token = owner_token, action = "grant", email = "collaborator@email.com" })
local chunks = {}
http.request {
    url    = "https://vaneltonmedia.com/api/cloudstore/share.php",
    method = "POST",
    headers = {
        ["Authorization"]  = APP_ID .. ":" .. APP_KEY,
        ["Content-Type"]   = "application/json",
        ["Content-Length"] = #body,
    },
    source = ltn12.source.string(body),
    sink   = ltn12.sink.table(chunks),
}
Sharing is all-or-nothing — a collaborator gets read/write/merge/delete on every cloudkey the owner has in this app. There's no per-key or read-only permission yet, so only grant access to emails you fully trust with that data.

Error reference

CodeMessageCause
401Invalid or expired token.Bad or missing user token
404Data not found for the specified cloudkey.Key doesn't exist for this user + app
400'ops' must contain between 1 and 25 operations.merge.php — empty or oversized ops list
404Data not found for the specified cloudkey and create_if_missing is false.merge.php called with create_if_missing: false on a key that doesn't exist
404User not found.share.php — the email in grant/revoke has no matching account, or the check on a collaborator call failed