Create Accountor Login
Docs

The Reporting API

Part of the MountDev Ops Workspace documentation. Updated October 29, 2026

MountDev Ops Workspace documentation | Updated October 29, 2026

The Reporting API lets your own systems read everything MountDev keeps about your websites: each website and its services, email delivery, uptime, backups, updates, security, speed and Core Web Vitals by day, each website's activity log, the issues MountDev found, your monthly reports, and your website events. Use it to build your own dashboards, send the numbers to your own tools, or keep your own copy. Your data is yours: it is never locked in, and nothing about your calls is tracked.

It is part of your workspace's REST API: the same address, the same keys, and the same Activity Log. Every address here is read only (GET), and its keys can read only. Changing what you pay for (adding a service or a website) is the Billing API's, with keys of its own.

MountDev's Three Services

Every website can have any of MountDev's three services, and the API names them the same way everywhere:

ServiceWhat the API shows for it
hostingHosting, with managed email delivery for the website (email_delivery)
maintenanceMaintenance: updates, backups, security, and uptime
performancePerformance: speed history and Core Web Vitals

Scopes

A key reads websites only with the scopes you give it when you make or edit it on Settings, Connections, API Keys (see API Keys). A key starts with none, and every scope is read only:

ScopeWhat it lets the key read
sites:readThe websites, and each one's Hosting, Maintenance, and Performance
stats:readHosting's email delivery, Maintenance's uptime, backups, updates, and security, and Performance's speed and Core Web Vitals, by day
reports:readThe monthly reports
events:readThe website events (a website down or back up, a backup failed, a report ready)
export:readEverything above for every website, a few websites a page
issues:readThe issues found on your websites: fixed by us, needing your team, or handled by MountDev
activity:readEach website's activity log: what changed and who changed it

A call without its scope is refused with 403 and the scope it needs, such as { "error": "This key does not have the stats:read scope. ...", "code": "forbidden" }.

The Addresses

Each address follows your workspace's API address, such as https://mountdev.app/account/api/v1/.

Method and addressScopeWhat it answers
GET sitessites:readYour websites, a page at a time (limit up to 100, 50 at first; cursor)
GET sites/<id>sites:readOne website, with its last 30 days in short
GET sites/<id>/hosting/emailstats:readEmail delivery by day for the website's own domain
GET sites/<id>/maintenance/uptimestats:readUptime by day
GET sites/<id>/maintenance/backupsstats:readBackups by day
GET sites/<id>/maintenance/updatesstats:readUpdates applied by day, with their names
GET sites/<id>/maintenance/securitystats:readSecurity scans and the firewall by day
GET sites/<id>/performancestats:readSpeed scores and Core Web Vitals by day (strategy: mobile, the default, or desktop)
GET sites/<id>/activityactivity:readThe website's activity log, newest first (limit up to 500, 100 at first)
GET issuesissues:readIssues on every website (bucket, status, limit up to 100, 50 at first; cursor)
GET sites/<id>/issuesissues:readIssues on one website, with the same filters
GET reportsreports:readYour monthly reports, newest first (limit up to 36, 12 at first; cursor)
GET reports/<YYYY-MM>reports:readOne month's report as data (site: just one website's part)
GET eventsevents:readYour website events, oldest first, past after (the id of the last one you have)
GET exportexport:readEverything for every website, five websites a page (cursor)

A website's <id> is the id GET sites gives it. A website of another company answers 404, the same as one that does not exist.

Days, Ranges, and Pages

  • The daily addresses take from and to (days such as 2026-10-01, Pacific time). Without them you get the last 30 days to today. One call covers up to 400 days, and pages hold up to 400 days (90 at first, limit).
  • Every list comes as { "items": [...], "next_cursor": "..." }. When next_cursor is not null, send it back as cursor, with the same other values, for the next page. A cursor is opaque: use it as it is.
  • Everything comes from the daily readings MountDev already keeps, so the answers are quick and the same each time you ask. A day's numbers can still grow until the day ends.

What Each Answer Holds

A website (GET sites and sites/<id>):

{ "id": "1042", "host": "example.com", "client": "Example Bakery", "since": "2026-03-02T17:00:00.000Z", "status": "ok", "uptime": { "state": "up", "down_since": null, "checked_at": "2026-10-29T18:15:00.000Z" }, "services": { "hosting": { "status": "ready", "note": "", "email_delivery": "ready" }, "maintenance": { "status": "ready", "note": "" }, "performance": null }, "attention": [] }

  • status is one word for the website as a whole: ok, attention (it is down or a check is failing), waiting (a step is waiting on you), setting_up, paused, or no_services.
  • Each service is null when the website does not have it, or its status (ready, setting_up, waiting, paused, or attention) with a note in plain words. Hosting's email_delivery is ready, waiting, setting_up, or not_set_up.
  • uptime.state is up, down (two checks in a row did not answer), or unknown (not checked yet).
  • GET sites/<id> adds last_30_days: maintenance (uptime_pct, last_backup, last_scan, updates) and performance (the newest mobile and desktop reading: score, vitals, ttfb_ms).

Email delivery by day: day, sent, delivered, bounced, soft_bounced, complained, held, declined, and suppressed, every kind of mail from the website's domain added together, with the domain they are for.

Activity: at, kind (such as plugin, theme, core, user, or content), action (such as updated or activated), object, name, from and to (such as versions), user (the WordPress user, when there is one), and by_us (true when MountDev made the change).

Uptime by day: day, checks (one every 15 minutes), fails, uptime_pct, minutes_down, and last_fail_at.

Backups by day: day, status (such as succeeded or failed), bytes, files, and tables.

Updates by day: day, plugins, themes, core (how many of each were updated), and names.

Security by day: day, scan (such as clean), firewall (on or off), and blocked (requests the firewall stopped).

Performance by day: day, optimized (each day can have a reading with and without optimization), scores (performance, accessibility, best_practices, seo, each 0 to 100), vitals (lcp_ms, fcp_ms, cls, tbt_ms), ttfb_ms, and bytes.

An issue (GET issues and sites/<id>/issues):

{ "id": "2", "site": { "id": "1042", "host": "example.com" }, "bucket": "needs_agency", "category": "license", "level": "action", "title": "Renew the slider plugin's license", "why": "Without a license the plugin gets no security updates.", "evidence_summary": "The plugin says its license expired on October 20.", "steps": [ "Renew the license in your account with the plugin's maker.", "Paste the new key in the plugin's settings." ], "status": "open", "opened_at": "2026-10-21T10:00:00.000Z", "updated_at": "2026-10-21T10:00:00.000Z", "fixed_at": null, "resolved_at": null }

  • bucket is who has it: fixed_by_us (MountDev fixed it), needs_agency (your team has a step to take, listed in steps), or handled_by_mountdev (MountDev is on it). Filter with bucket=.
  • status is open, in_progress, fixed, or resolved. Filter with status=.
  • category is one of hosting, uptime, updates, backups, security, performance, email, dns, forms, links, license, plugins, content, access, billing, or other. level is urgent, action, or fyi.
  • why says why it matters and evidence_summary what was seen, each in plain words. steps (up to 12) is empty unless the bucket is needs_agency.
  • Issues come newest first. Only the issues your team may see are listed.

A report (GET reports/<YYYY-MM>): month, label, sent_at, account, summary, tiles, attention, and sites, each website's sections as the report shows them. It is white-labeled: it carries the words and numbers only, never our name, a link to the report page, the people it was sent to, or a DNS record, so you can show it your own way.

An event (GET events): id, type, at, site (id and host, or null for the whole account), and data. The answer also has last (send it as after next time) and more (true when there are more right now).

The export (GET export): format (mountdev-export-v1), exported_at, from and to (the last 400 days), sites, each website as above with its hosting (email), maintenance (uptime, backups, updates, security), and performance (mobile, desktop) by day, its newest 500 activity entries, and next_cursor. The first page also has account, reports, and sites_total. Page through it with cursor until next_cursor is null to have everything. This is everything MountDev keeps about your websites; your workspace's own records and files are in Export Everything, which stays with owners.

Website Events

The same events can be sent to your systems the moment MountDev sees them, as signed webhooks: choose them under Websites when you add a webhook (see Webhooks).

EventWhen
site.downA website did not answer two checks in a row, 15 minutes apart
site.upA website answers again after being down (data.minutes: about how long it was down)
backup.failedA website's backup did not finish (data.day, data.kind)
report.readyThe month's report was sent and can be read with GET reports/<YYYY-MM> (data.month)
issue.needs_agencyAn issue needs your team, with its steps (data.issue)
issue.fixedAn issue was fixed (data.issue)
issue.resolvedAn issue was resolved (data.issue)
notice.urgentAn urgent notice, only for a website down past your threshold, a hacked website, a failed payment, or a security hole in a plugin we are holding (data.notice: id, kind (site_down, site_hacked, payment_failed, or plugin_vulnerable), title, summary, level, and, when there is one, issue_id, since, and minutes)
notice.digestThe daily digest, one for each group (data.digest: id, date (the day in your company's own time zone, from Business Hours), group (Action Needed or For Your Information), counts, and up to 50 tasks, each with title, category, bucket (needs_agency), status (open), site, and issue_id). In counts, needs_agency is the tasks open for your team, fixed_by_us what MountDev fixed in the last 24 hours, and handled_by_mountdev what MountDev is handling now

Limits

Each key may make 120 calls a minute; an export page counts as 10. Every answer says where the key is in its X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers, and a call over the limit is refused with 429, "code": "rate_limited", and Retry-After (seconds until the next minute).

Privacy

  • A key reads only its own company's websites. A website of another company answers exactly as one that does not exist.
  • Answers never carry a DNS record's name or value, a password, a key, a secret, or the private link to a report.
  • Nothing about your calls is tracked. Each call is written in your own Activity Log with the key's name.

For Example

GET /account/api/v1/sites/1042/maintenance/uptime?from=2026-10-01&to=2026-10-28 Authorization: Bearer mdo_...

{ "result": { "from": "2026-10-01", "to": "2026-10-28", "items": [ { "day": "2026-10-01", "checks": 96, "fails": 0, "uptime_pct": 100, "minutes_down": 0, "last_fail_at": null } ], "next_cursor": null } }

Coming Next Planned

These are planned and not part of the Reporting API yet:

  • Hosting's storage and bandwidth by day.
  • A report as a PDF with your own brand.
  • A shorter address for the API of its own.
  • Choosing which websites a key may read.

Ask Us Anything

We’d love to hear from you!