Docs

REST API (Pro)

A documented, stable API for MSPs’ own scripts and tools: RMM onboarding, dashboards, approval workflows, change management, billing. Licensed with the api_tokens feature (Pro). Agreed 2026-10-13.

  • Base URL: https://<console>/api/v1.
  • Docs: Swagger UI at /api/v1/docs, for signed-in users of a licensed console.
  • Spec: the OpenAPI document at /api/v1/openapi.json, also committed as api-v1.json, so it can be shown before buying.

Tokens

curl -H "Authorization: Bearer mrsh_..." https://marshal.example.com/api/v1/me
  • Who makes them: every user, under Account › API tokens. Admins also see and revoke everyone’s under Integrations › API tokens.
  • Shown once: only a token’s sha256 is stored. The list shows its prefix (mrsh_xxxxxx…), when and from where it was last used, and its state.
  • Never more than the owner can do: their role and their customers.
    • A token can be narrowed further: read-only, and/or some of the owner’s customers.
    • Disabling the user stops their tokens.
  • Every token expires: after 30, 90, 180 or 365 days.
  • Audit: changes made with a token are audited under its owner, with api_token naming the token.
  • Only on /api/v1: a token works nowhere else, so it can’t reach users, settings or the license.
    • Calls with a token need no X-Requested-With header: a token isn’t a cookie, so cross-site forms can’t send one.
    • Browser sessions still need the header for writes. The docs page adds it.
  • Rate limit: at most 600 requests a minute per token (429 with Retry-After).

What it covers

AreaReadChange
MeGET /me
CustomersGET /customers, /customers/{id}POST /customers/{id}/scan
ServersGET /servers (filters, paged), /servers/{id}, /servers/{id}/updates, /servers/{id}/packages (Linux)POST /servers/{id}/scan
ComplianceGET /compliancePOST /approvals (approve, decline, reset)
RunsGET /runs (paged), /runs/{id}POST /runs, POST /runs/{id}/abort (admins)
Windows, blackoutsGET /windows, GET /blackoutscreate, edit, delete
EnrollmentPOST /enrollment-tokens (returns the install script and one-liners)
ReportsGET /reports, /reports/{id}/pdf

Users, settings, the license and the tokens themselves stay in the console. Writing needs a read-write token and the operator role (admin for runs), as in the console.

Status codes:

CodeMeaning
401no token, or a bad, expired or revoked one
402not licensed
403not allowed (a read-only token, or the role)
404not found, or not one of your customers
409conflict
422invalid input
423Marshal is read-only (expired license): reads still work
429too many requests

Stability

  • v1 only gains fields. Its responses are the models in app/api/v1/routes.py, built from the console’s own answers. The console’s internal /api can change freely.

  • The spec is checked on every test run. tests/test_api_v1.py::test_spec_is_committed compares the generated OpenAPI document with docs/api-v1.json. After a deliberate, additive change, regenerate it:

    docker run --rm -v ./backend:/app marshal:latest python -m app.tools.openapi > docs/api-v1.json
    
  • Breaking changes go into /api/v2.

How it works

  • app/api/v1/ is its own FastAPI app, mounted at /api/v1.
    • auth.py works out the caller: a token, or a signed-in browser for the docs page. It then checks the rate limit, then the license (401 before 402, as for premium routes).
    • routes.py calls the console’s endpoint functions with the caller’s user, role and customers, so the checks and the audit log are the same as in the UI.
  • Tokens: services/api_tokens.py, the api_tokens table, and the console’s API at api/premium/api_tokens.py.
  • Swagger UI is served from the console’s own files (/swagger/, copied from swagger-ui-dist at build), because the CSP allows no outside scripts.