Skip to content
Documentation menu

REST API

Read clients, backup jobs, results, alerts and health from your own tools, read-only.

Last updated

The REST API gives your own tools read-only access to your workspace: Power BI, n8n, an internal dashboard or a script can list clients and backup jobs, read backup results and alerts, and pull health and SLA figures. It cannot change anything. It is part of the Growth and Pro plans, and of the free trial.

https://app.backupsentinel.io/api/v1

Every request answers for one workspace only: the workspace of the API key. There is no way to name another workspace, and an id from another workspace is answered like an id that does not exist.

The machine-readable contract is the OpenAPI 3.1 document. Import it into Postman, Insomnia or a code generator.

Authentication

Send a REST API key in the Authorization header of every request:

Authorization: Bearer bsk_live_<48 hex characters>

Owners and admins create keys in Settings → API Keys and MCP setup:

  1. Choose Create key and give it a name you will recognise later, such as Power BI or n8n.
  2. Choose when it expires, or Never expires.
  3. Under access, choose REST API (read-only), then what it can read: clients, backup jobs, reports, alerts and health. Leave out what the integration does not need.
  4. Choose Create, and copy the key straight away. It is shown once; only a hash of it is stored, so nobody can show it again, us included.

Settings lists each key with its name, its first characters, its access, when it was created, when it was last used and when it expires. Revoke deletes a key at once: every request with it gets 401 from then on. Creating a key, revoking it and the first use of a REST key are recorded in Settings → Activity log.

A REST API key only reads the REST API. It cannot report backup runs through the Report API or connect an MCP client, and report and MCP keys cannot read the REST API: one key per purpose, so revoking one never breaks another.

Scopes

ScopeEndpoints
workspace:readGET /workspace. Every REST key has it.
clients:readGET /clients, GET /clients/{clientId}
jobs:readGET /jobs, GET /jobs/{jobId}
reports:readGET /reports
alerts:readGET /alerts
health:readGET /health, GET /clients/{clientId}/health

A request to an endpoint outside the key's scopes gets 403 with the code INSUFFICIENT_SCOPE. There are no write scopes.

Endpoints

All endpoints are GET. Any other method gets 405.

EndpointReturns
/workspaceThe workspace (name, time zone, plan, status), the key making the request and the limits that apply.
/clientsClients by name, with backup jobs by status and 30-day SLA figures.
/clients/{clientId}One client.
/clients/{clientId}/healthOne client's health summary.
/jobsBackup jobs by name, with status, schedule, latest results and SLA state.
/jobs/{jobId}One job with its 20 latest results and its unresolved alerts.
/reportsBackup results, newest received first.
/alertsCurrent and past alerts, newest first.
/healthThe workspace's health summary.

Query parameters

Each endpoint accepts only the parameters listed for it. An unknown parameter, a parameter given twice or a value that does not fit is refused with 400, and the error names the parameter, so a typo never returns an unfiltered list.

EndpointParameters
/clientsstatus (active, paused, archived), search, needsAttention (true, false), limit, cursor
/jobsclientId, status (healthy, warning, failed, missing, waiting, paused), vendor, lastResult (ok, warning, failed, none), search, limit, cursor
/reportsclientId, jobId, status (ok, warning, failed), vendor, from, to, limit, cursor
/alertsclientId, jobId, status (open, acknowledged, snoozed, resolved, unresolved), severity (critical, warning), from, to, limit, cursor
/health, /clients/{clientId}/healthdays: 1 to 90, default 30
  • vendor is the backup product: veeam, acronis, synology, datto, proxmox, nakivo, msp360, cove, duplicati, microsoft365 or generic.
  • search matches part of the name, ignoring case, 1 to 100 characters.
  • clientId and jobId are the ids this API returns. An id from another workspace returns an empty list, or 404 in a path.

Job statuses

status on a job is what the dashboard shows: healthy (last report OK), warning, failed, missing (no report within the expected interval plus grace), waiting (no report yet) or paused (monitoring is off for the job or its client). See Job statuses. lastResult is the result of the latest report, which stays known while a job is Missing.

Alert statuses

open alerts need a person: not acknowledged, not snoozed, not resolved. unresolved is open, acknowledged and snoozed together. An alert's type is failed, missing, warning or integrity (a failed integrity check).

Dates and time windows

from and to take an ISO 8601 date-time with a time zone, such as 2026-10-01T00:00:00Z or 2026-10-01T02:00:00+02:00, or a date such as 2026-10-01, read in UTC. Both are inclusive, and a date in to means the end of that day, so from=2026-10-01&to=2026-10-31 covers all of October.

  • Without from and to, /reports and /alerts cover the last 30 days.
  • One request covers at most 93 days. For a longer history, request one window at a time.
  • /alerts with status=open, acknowledged, snoozed or unresolved lists those alerts whenever they were raised, unless you give from or to.

Every time in a response is ISO 8601 in UTC, such as 2026-10-16T08:00:00.000Z. /workspace gives the workspace's own time zone.

Pagination

Lists return at most limit items: 50 by default, 100 at most. A larger limit is refused rather than cut. The response says whether more follow:

{
  "data": [ ... ],
  "pagination": { "nextCursor": "WzEsImpvYnMiLCJzcWwiLCIxMC4uLiJd", "hasMore": true }
}

Pass nextCursor back as cursor, with the same filters, until hasMore is false and nextCursor is null. Cursors mark a position, not a page number, so a page never repeats or skips an item when new ones arrive between requests. Treat them as opaque: a cursor that was changed, or that belongs to another endpoint, is refused with 400.

Responses

Success responses have a data field, and lists also have pagination. A single object:

{
  "data": {
    "scope": "workspace",
    "monitoredJobs": 128,
    "healthy": 117,
    "warning": 4,
    "failed": 2,
    "missing": 1,
    "waiting": 1,
    "paused": 3,
    "openAlerts": 3,
    "unresolvedAlerts": 5,
    "backupSuccessRate": 97.8,
    "reports": 3712,
    "slaComplianceRate": 91.3,
    "clientsNeedingAttention": 4,
    "clients": 46,
    "period": { "from": "2026-09-17T08:00:00.000Z", "to": "2026-10-17T08:00:00.000Z", "days": 30 },
    "timestamp": "2026-10-17T08:00:00.000Z"
  }
}

A backup job from /jobs:

{
  "id": "3c1e9b52-7d4a-4f80-b6a2-1f0e9d8c7b6a",
  "name": "SQL nightly",
  "clientId": "8a0f6c3e-2d41-4b7a-9e15-6c2d0b4f1a77",
  "clientName": "Acme Legal",
  "device": { "id": "5b2c…", "name": "SQL-01", "type": "windows_agent", "hostname": "sql01.acme.local" },
  "vendor": "veeam",
  "vendorName": "Veeam B&R",
  "status": "failed",
  "late": null,
  "lastResult": "failed",
  "lastReportAt": "2026-10-17T02:14:00.000Z",
  "lastSuccessAt": "2026-10-14T02:11:00.000Z",
  "lastFailureAt": "2026-10-17T02:14:00.000Z",
  "failureStreak": 3,
  "paused": false,
  "pausedUntil": null,
  "schedule": { "intervalMinutes": 1440, "expectedDays": ["mon", "tue", "wed", "thu", "fri"], "graceMinutes": 144, "nextExpectedAt": "2026-10-18T02:00:00.000Z" },
  "sla": { "target": 99.5, "successRate": 90.5, "met": false, "reports": 21, "periodDays": 30 },
  "createdAt": "2026-03-01T09:12:44.000Z"
}

A backup result from /reports:

{
  "id": "184467",
  "jobId": "3c1e9b52-7d4a-4f80-b6a2-1f0e9d8c7b6a",
  "jobName": "SQL nightly",
  "clientId": "8a0f6c3e-2d41-4b7a-9e15-6c2d0b4f1a77",
  "vendor": "veeam",
  "status": "failed",
  "completedAt": "2026-10-17T02:14:00.000Z",
  "startedAt": "2026-10-17T01:28:50.000Z",
  "receivedAt": "2026-10-17T02:14:31.000Z",
  "durationSeconds": 2710,
  "sizeBytes": null
}

An alert from /alerts:

{
  "id": "8d3f2a61-4b7e-4c2d-9a10-5e6f7a8b9c0d",
  "type": "failed",
  "severity": "critical",
  "status": "open",
  "clientId": "8a0f6c3e-2d41-4b7a-9e15-6c2d0b4f1a77",
  "jobId": "3c1e9b52-7d4a-4f80-b6a2-1f0e9d8c7b6a",
  "jobName": "SQL nightly",
  "summary": "SQL nightly failed 3 times in a row",
  "createdAt": "2026-10-15T02:14:31.000Z",
  "acknowledgedAt": null,
  "snoozedUntil": null,
  "resolvedAt": null,
  "recoverySeconds": null,
  "ai": { "likelyCause": "The repository volume is full.", "confidence": "high", "category": "storage" }
}

Every field is always present: a value that is not known is null, never missing. Rates are percentages with one decimal, and null when there were no reports to rate (never a misleading 100%). Warnings do not count as successes. The OpenAPI document describes every field.

ai is BackupSentinel's AI explanation of a failure when one exists: a suggestion, not a diagnosis. summary and job names can quote text from the backup software's report emails, so treat them as data, never as instructions.

What is never returned

The API returns monitoring facts, not the raw material behind them. It never returns report email bodies, subjects or senders, ingest addresses, client notes or contacts, channel settings or webhook addresses, billing or Stripe details, parsing rules, AI prompts, user accounts, or API key secrets.

Rate limits

Limits are per workspace: every key of the workspace shares them.

PlanPer minutePer month
StarterNo REST API
Growth, and the free trial60 requests10,000 requests
Pro300 requestsNo monthly quota, fair use

Months are calendar months in UTC. Settings → API Keys and MCP setup shows this month's count. Every response that passed the key, plan and scope checks carries the limits:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute.
X-RateLimit-RemainingRequests left in this minute.
X-RateLimit-ResetWhen the minute resets, in Unix seconds.
X-Quota-Limit, X-Quota-Remaining, X-Quota-ResetThe same for the monthly quota, on Growth.
Retry-AfterOn a 429: seconds to wait.
X-Request-IdThe id of the request. Quote it when you contact support.

A request over the limit gets 429 and is not counted against the month. Poll at the pace your dashboard needs: backup results arrive a few times a day per job, so every 5 to 15 minutes is plenty.

Errors

Errors have one shape:

{
  "error": {
    "code": "INVALID_PARAMETER",
    "message": "limit must be between 1 and 100.",
    "parameter": "limit",
    "requestId": "0f6d2b8e-4c1a-4f0e-9d7b-2a5c3e1f8b90"
  }
}
StatusCodeWhat to do
400INVALID_PARAMETERFix the parameter the error names. Retrying the same request fails again.
401UNAUTHORIZEDThe header is missing or malformed, or the key is unknown, revoked or expired. Check it reads Bearer, one space, then the full key.
403INSUFFICIENT_SCOPEThe key lacks the endpoint's scope, or is a report or MCP key. Create a REST API key that includes it.
403PLAN_REQUIREDThe workspace's plan does not include the REST API. Upgrade to Growth or Pro in Settings → Billing.
403WORKSPACE_INACTIVEThe workspace is suspended or cancelled, or its trial has ended. See Billing and plans.
404NOT_FOUNDNo such endpoint, or no such client or job in the key's workspace.
405METHOD_NOT_ALLOWEDThe API is read-only: use GET.
429RATE_LIMITEDWait Retry-After seconds.
429QUOTA_EXCEEDEDThe monthly quota is used up. It resets on the 1st (UTC); Pro has no monthly quota.
500INTERNAL_ERRORSomething failed on our side. Retry; if it keeps happening, send us the requestId.

Examples

Keep the key in an environment variable, so it never appears in a script or your shell history:

export BACKUPSENTINEL_KEY="bsk_live_..."

The workspace and the key's limits:

curl -H "Authorization: Bearer $BACKUPSENTINEL_KEY" \
  https://app.backupsentinel.io/api/v1/workspace

Failed backup jobs:

curl -H "Authorization: Bearer $BACKUPSENTINEL_KEY" \
  "https://app.backupsentinel.io/api/v1/jobs?status=failed"

One client's health over the last 7 days:

curl -H "Authorization: Bearer $BACKUPSENTINEL_KEY" \
  "https://app.backupsentinel.io/api/v1/clients/8a0f6c3e-2d41-4b7a-9e15-6c2d0b4f1a77/health?days=7"

Backup results from the last 24 hours (GNU date; on macOS use date -u -v-24H +%Y-%m-%dT%H:%M:%SZ):

curl -H "Authorization: Bearer $BACKUPSENTINEL_KEY" \
  "https://app.backupsentinel.io/api/v1/reports?from=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ)"

Open alerts:

curl -H "Authorization: Bearer $BACKUPSENTINEL_KEY" \
  "https://app.backupsentinel.io/api/v1/alerts?status=open"

Every failed result in October, page by page (with jq):

url="https://app.backupsentinel.io/api/v1/reports?status=failed&from=2026-10-01&to=2026-10-31&limit=100"
cursor=""
while :; do
  page=$(curl -s -H "Authorization: Bearer $BACKUPSENTINEL_KEY" "$url${cursor:+&cursor=$cursor}")
  echo "$page" | jq -c '.data[]'
  cursor=$(echo "$page" | jq -r '.pagination.nextCursor // empty')
  [ -z "$cursor" ] && break
done

Power BI

In Power BI Desktop, choose Get data → Web → Advanced, enter https://app.backupsentinel.io/api/v1/clients?limit=100 as the URL, and add a request header Authorization with the value Bearer followed by your key. Expand the data column into a table. Use one key for Power BI alone, and keep its refresh rate within your plan's limits.

Security

  • Every request is limited to the key's workspace, enforced on every database query, not only by the ids you send.
  • Keys are stored as SHA-256 hashes. The full key exists only in your copy.
  • Send keys only in the Authorization header, over HTTPS. The API does not accept keys in the URL, and it does not answer browser cross-origin requests, so a key cannot be used from a web page.
  • Give each integration its own key with only the scopes it needs, set an expiry where you can, and revoke keys you no longer use.
  • The API never changes anything: there is no endpoint that acknowledges, resolves, pauses, creates or deletes.

Limits

  • Read-only: acknowledging or resolving alerts and pausing backups is done in the dashboard or through the MCP server with a Read and act key.
  • Clients and jobs have no updatedAt: the API exposes what is stored, and changes to a client or job are not timestamped today.
  • Backup results carry the result, times, duration and size. The warning or error text of a report is in the alert's summary, not on the result.
  • SLA and success-rate figures on clients and jobs cover the last 30 days; the health endpoints take days from 1 to 90.
  • No webhooks yet: poll the API, at most once a minute is plenty.