$ CS4 Cloud API

# the REST control plane for the CS4 Cloud backup & DR platform

Authentication

No session or bearer token — every request is a POST whose body carries the credentials it needs. The data API (/Connection, /WebQuery) authenticates the way the legacy Apx clients do; the VM power API (/vms/*) authenticates with an API key — apiKeyName + apiKeySecret, created in the UserConsole "API Keys" screen — which scopes every operation to its tenant. HTTPS only; the caller IP is forwarded upstream.

Endpoints

Routes carry no /api prefix — they match the legacy Apx surface exactly, so existing clients keep working.

How an action runs

This isn't CRUD. You dispatch an action; CS4.CloudCore.Engine drains the queue and drives the hypervisor; you watch the task to a terminal state:

pending running ok / failed

# set "wait": true to block for the terminal state, or poll /vms/task-status by GUID.

Response shape

Data-API operations return the same envelope at HTTP 200 — check the payload, not the status code. VM power operations return a small result object:

First call

Dispatch a power action and read the result inline with wait:

curl -X POST https://api.cloud.example.com/vms/change-state \
  -H "Content-Type: application/json" \
  -d '{
    "apiKeyName":   "reporting-bot",
    "apiKeySecret": "<shown once>",
    "vmName":       "web-prod-01",
    "action":       "shutdown",
    "wait":         true
  }'
# → { "taskId":"3f1c8a20-…", "status":"ok", "powerState":"poweredOff" }

Sample (C#)

Plain HttpClient + System.Text.Json on .NET 8 — no extra packages. Records model the request and result; branch on Status, not the HTTP code:

using System.Net.Http.Json;

record ChangeVmState(string ApiKeyName, string ApiKeySecret,
                     string VmName, string Action, bool Wait = true);
record VmStateResult(string? TaskId, string Status, string Message, string? PowerState);

using var http = new HttpClient { BaseAddress = new("https://api.cloud.example.com") };

var rqst = new ChangeVmState(
    ApiKeyName:   "reporting-bot",
    ApiKeySecret: Environment.GetEnvironmentVariable("CS4_API_SECRET")!,
    VmName:       "web-prod-01",
    Action:       "shutdown");

var res  = await http.PostAsJsonAsync("/vms/change-state", rqst);
var body = await res.Content.ReadFromJsonAsync<VmStateResult>();

Console.WriteLine(body switch
{
    { Status: "ok" }     => $"[ok]  {body.Message} — now {body.PowerState}",
    { Status: "failed" } => $"[err] {body.Message}",
    null                 => "[err] empty response",
    _                    => $"[..] {body.Status} (task {body.TaskId})",
});

Non-.NET callers POST the same JSON bodies directly — see /swagger.

Help