CloudStore
Per-user JSON cloud storage — perfect for game saves, settings, and inventory. Data is isolated by AppID + cloudkey per user.
Overview
| Endpoint | Action |
|---|---|
POST /api/cloudstore/set.php | Save or overwrite a JSON value |
POST /api/cloudstore/get.php | Retrieve one key or all keys |
POST /api/cloudstore/delete.php | Delete a key |
POST /api/cloudstore/merge.php | Apply partial updates atomically, without overwriting |
POST /api/cloudstore/share.php | Grant or revoke another account's access to your CloudStore |
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" }
}
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),
}
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
| Code | Message | Cause |
|---|---|---|
| 401 | Invalid or expired token. | Bad or missing user token |
| 404 | Data 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 |
| 404 | Data 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 |
| 404 | User not found. | share.php — the email in grant/revoke has no matching account, or the check on a collaborator call failed |