Skip to content

Powered by Grav + Helios

System

System

Endpoints for system-level operations including information, cache management, environments, logs, backups, translations, and reports.

Endpoints for system-level operations including information, cache management, environments, logs, backups, translations, and reports.

System Info

GET /system/info
Rich system information: Grav / PHP versions, loaded extensions, server software, current environment, installed plugins and themes, and a structured PHP configuration summary (uploads, memory, error handling, paths, sessions, OPcache, security, locale). Intended to populate the Admin2 "System Info" / support pages. Requires `api.system.read`.
JSON
{"data": {"grav_version": "2.1.0", "php_version": "8.3.14", "php_extensions": ["Core", "date", "pcre"], "server_software": "Apache/2.4.58", "environment": "localhost", "plugins": [{"name": "API", "version": "1.0.38", "enabled": true}], "themes": [{"name": "Quark", "version": "2.0.4", "active": true}], "php_config": {"Upload & POST": {"file_uploads": "1", "upload_max_filesize": "64M", "max_file_uploads": "20", "post_max_size": "64M"}}}}

Response Codes

200 Info returned.
401 Unauthorized.
403 Missing `api.system.read` permission.

For demo accounts, server_software and the path values in php_config (error log, open_basedir, temp dir, document root, include path, session save path) are replaced with (hidden in demo mode).

Clear Cache

DELETE /cache
Clear the system cache. Defaults to `standard` scope (the same as `bin/grav cache clear`), matching the typical "flush cache" button. Use `scope=all` to also drop images/assets caches. Requires `api.system.write`.

Parameters

Name Type Description
scope optional string One of `all`, `standard`, `images`, `assets`, `tmp`. Defaults to `standard`. `images`, `assets` and `tmp` clear only that folder.
JSON
{"data": {"scope": "standard", "message": "Cache cleared successfully (scope: standard).", "details": ["Cleared:  /var/www/grav/cache/*"]}}

Response Codes

200 Cache cleared.
401 Unauthorized.
403 Missing `api.system.write` permission.
422 Invalid cache scope.

details is the list of per-folder result lines from Grav's cache clear.

Get Translations

GET /translations/{lang}
Get all translation strings for a language as a flat key/value map. This is a public route: no authentication is needed. Keys the language does not translate are filled in from English (`en-US`), so the map is complete for any language.

Parameters

Name Type Description
lang required string Language code (e.g. `en`, `fr`, `pt-BR`). Short codes are normalized to a full locale (`en` becomes `en-US`, `fr` becomes `fr-FR`); a value that is not a language code falls back to the site's default language. The resolved code is returned in `lang`.
prefix optional string Only return keys starting with this prefix followed by a dot (case-insensitive), e.g. `PLUGIN_ADMIN`.
JSON
{"data": {"lang": "fr-FR", "dir": "ltr", "count": 1868, "checksum": "5d41402abc4b2a76b9719d911017c592", "strings": {"PLUGIN_ADMIN.SAVE": "Enregistrer"}}}

Response Codes

200 Success

dir is the text direction (ltr or rtl), and checksum is an MD5 of the returned strings, so a client can cache the map and only refetch it when the checksum changes. Strings contributed only by disabled plugins are left out.

Ping

GET /ping
Lightweight health check. This is a public route: no credentials are needed and no permission is checked, so it only confirms the API is reachable.
JSON
{"data": {"pong": true}}

Response Codes

200 Success

List Environments

GET /system/environments
List the environments that can be chosen as configuration write targets, plus the environment Grav detected for this request. Requires `api.system.read`.
JSON
{"data": {"detected": "localhost", "environments": [{"name": "", "label": "Default", "exists": true, "hasOverrides": false}, {"name": "staging.example.com", "label": "staging.example.com", "exists": true, "hasOverrides": true}]}}

Response Codes

200 Success
401 Unauthorized
403 Missing `api.system.read` permission

The first entry is always the base configuration (user/config), with name: "" and label: "Default"; its hasOverrides is always false. Every other entry is an existing user/env/<name>/ folder, and legacy user/<host>/config/ layouts are included too. hasOverrides says whether the environment holds any configuration files.

Get Logs

GET /system/logs
Read log entries from a log file (default `grav.log`) with pagination, level filtering and text search. Entries are returned newest first. Requires `api.system.read`; demo accounts get 403.

Parameters

Name Type Description
file optional string Log file to read (default `grav.log`). Must be one of the names listed by `GET /system/logs/files`.
page optional integer Page number for pagination (default: 1)
per_page optional integer Number of results per page (default: 20, max: 1000)
level optional string Filter by log level, case-insensitive (e.g. DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL)
search optional string Case-insensitive text the message must contain
JSON
{"data": [{"date": "2026-09-21T10:30:00.123456+00:00", "logger": "grav", "level": "WARNING", "message": "Plugin X deprecated method"}], "meta": {"pagination": {"page": 1, "per_page": 20, "total": 150, "total_pages": 8}}, "links": {"self": "/api/v1/system/logs?page=1&per_page=20", "next": "/api/v1/system/logs?page=2&per_page=20", "last": "/api/v1/system/logs?page=8&per_page=20"}}

Response Codes

200 Success. A registered log that has not been written yet returns an empty page.
401 Unauthorized
403 Missing `api.system.read` permission, or a demo account
422 Unknown log file

Reports

GET /reports
Get plugin-extensible diagnostic reports. Built-in reports are a Security Check (XSS scan), Twig in Content, and a YAML Linter. Plugins can add their own reports via the onApiGenerateReports event. Requires `api.reports.read`.
JSON
{
    "data": [
        {
            "id": "security-check",
            "title": "Grav Security Check",
            "provider": "core",
            "component": null,
            "status": "success",
            "message": "Security Scan complete: No issues found.",
            "items": []
        },
        {
            "id": "twig-content",
            "title": "Twig in Content",
            "provider": "core",
            "component": null,
            "status": "success",
            "message": "Twig in Content is disabled. No pages are leaking raw Twig.",
            "meta": { "gate": false, "sandbox": true, "editor_enabled": false, "leak_count": 0, "event_count": 0, "global_request_gated": false, "frontmatter_request_gated": false },
            "items": []
        },
        {
            "id": "yaml-linter",
            "title": "Grav Yaml Linter",
            "provider": "core",
            "component": null,
            "status": "success",
            "message": "YAML Linting: No errors found.",
            "items": []
        },
        {
            "id": "problems",
            "title": "Grav Potential Problems",
            "provider": "problems",
            "component": "problems-report",
            "status": "success",
            "message": "No critical problems detected.",
            "items": [
                {
                    "id": "PHP Minimum Version",
                    "level": "critical",
                    "status": true,
                    "msg": "Your PHP 8.5.4 is greater than the minimum of 8.3.0 required"
                }
            ]
        }
    ]
}

Response Codes

200 Reports returned successfully
401 Unauthorized
403 Forbidden - requires `api.reports.read` permission

Report Structure

Each report in the response array contains:

Field Type Description
id string Unique report identifier
title string Human-readable report title
provider string Source — core for built-in, or plugin slug
component string|null Web component ID for custom rendering, or null for default
status string Overall status: success, warning, or error
message string Summary message
meta object Optional report-specific summary values
items array Report-specific detail items

Built-in Reports

  • Security Check — Scans all pages for potential XSS vulnerabilities. Items contain route and field for each issue found.
  • Twig in Content (twig-content): Shows whether Twig in page content is switched on and sandboxed, pages whose raw Twig markers render to visitors, and recent tokens the sandbox blocked. Its meta carries gate, sandbox, editor_enabled, leak_count, event_count, global_request_gated and frontmatter_request_gated; its items are kind: leak and kind: event rows. The endpoints under /reports/twig-content/ act on this report.
  • YAML Linter — Checks all YAML files for syntax errors. Items contain file and error for each issue found.

Plugin Reports

Plugins add reports by listening for the onApiGenerateReports event. When a report specifies a component, the admin frontend loads the plugin's web component from GET /gpm/plugins/{provider}/report-script/{component}.

See the Plugin API Integration guide for implementation details.

Create Backup

POST /system/backup
Trigger a backup of the site. Uses Grav's built-in `Backups` class, writes a dated zip to the configured backup location, and returns the filename, path, size, and creation timestamp. Admin2 uses this for the "Backup now" action on the dashboard. Requires `api.system.backup`, since archives hold account password hashes and config secrets; demo accounts get 403.
JSON
{"data": {"filename": "default_site_backup--20260917120000.zip", "path": "/var/www/grav/backup/default_site_backup--20260917120000.zip", "size": 1048576, "date": "2026-09-17T12:00:00+00:00"}}

Response Codes

201 Backup created; Location header points to `/system/backups`.
401 Unauthorized.
403 Missing `api.system.backup` permission, or a demo account.

List Backups

GET /system/backups
List existing backup files (filename, title, date, size), plus the configured purge policy and the number of configured backup profiles. Requires `api.system.backup`; demo accounts get 403.
JSON
{"data": {"backups": [{"filename": "default_site_backup--20260917120000.zip", "title": "Default Site Backup", "date": "2026-09-17T12:00:00+00:00", "size": 1048576}], "purge": {"trigger": "space", "max_backups_count": 25, "max_backups_space": 5, "max_backups_time": 365}, "profiles_count": 1}}

Response Codes

200 Backup list returned.
401 Unauthorized.
403 Missing `api.system.backup` permission, or a demo account.

date is an ISO 8601 timestamp, the same format Create Backup returns. purge is the site's backups.purge configuration.

Delete Backup

DELETE /system/backups/{filename}
Delete a backup zip. The filename is validated to prevent path traversal — must be a bare basename ending in `.zip`. Requires `api.system.backup`; demo accounts get 403.

Parameters

Name Type Description
filename required string The backup filename (bare basename, e.g. `default_site_backup--20260917120000.zip`).

Response Codes

204 Backup deleted.
401 Unauthorized.
403 Missing `api.system.backup` permission, or a demo account.
404 Backup not found.
422 Invalid filename.

Download Backup

GET /system/backups/{filename}/download
Stream a backup zip file to the caller. Returns `application/zip` with a `Content-Disposition: attachment` header. Requires `api.system.backup`; demo accounts get 403.

Parameters

Name Type Description
filename required string The backup filename (bare basename ending in `.zip`).

Response Codes

200 Backup streamed.
401 Unauthorized.
403 Missing `api.system.backup` permission, or a demo account.
404 Backup not found.
422 Invalid filename.

System Info Report

GET /systeminfo
Generate a compact system report: PHP info (version, SAPI, extensions, memory/upload limits), Grav version, disk free/total space, plugin counts and cache status. Lighter than `/system/info`, and intended for the dashboard "System Health" widget rather than the full Admin2 System Info page. Requires `api.system.read`.
JSON
{"data": {"php": {"version": "8.3.14", "sapi": "fpm-fcgi", "extensions": ["Core", "date"], "memory_limit": "256M", "max_execution_time": "60", "upload_max_filesize": "64M", "post_max_size": "64M"}, "grav": {"version": "2.1.0", "php_version": "8.3.14"}, "disk": {"free_space": 10737418240, "total_space": 53687091200}, "plugins": {"total": 30, "enabled": 24, "disabled": 6}, "cache": {"enabled": true, "driver": "auto"}}}

Response Codes

200 Report returned.
401 Unauthorized.
403 Missing `api.system.read` permission.

disk.free_space and disk.total_space are in bytes, or false when PHP cannot read them.

Create Environment

POST /system/environments
Create an empty environment folder at `user/env//config`, which can then be chosen as a configuration write target. Requires `api.config.write`.

Parameters

Name Type Description
name required string Environment name, usually a hostname. It must start with a letter or digit and contain only letters, digits, `.`, `_` and `-`. `default` and `base` are reserved.
JSON
{"name": "staging.example.com"}
JSON
{"data": {"name": "staging.example.com", "label": "staging.example.com", "exists": true, "hasOverrides": false}}

Response Codes

201 Environment created
401 Unauthorized
403 Missing `api.config.write` permission
422 Invalid or reserved name, or the environment already exists

Delete Environment

DELETE /system/environments/{name}
Delete the environment folder `user/env//` and everything in it. Requires a super admin, since the folder can hold `system` and `security` overrides that only a super admin may write.

Parameters

Name Type Description
name required string Environment name (path parameter)

Response Codes

204 Environment deleted
401 Unauthorized
403 Not a super admin
404 No `user/env/<name>/` folder exists
422 Invalid name, the environment Grav is using for this request, or a legacy `user/<name>/` layout (remove that one by hand)

List Log Files

GET /system/logs/files
List the log files the admin log viewer can open. These names are the only values `GET /system/logs` and `DELETE /system/logs` accept for `file`. Requires `api.system.read`; demo accounts get 403.
JSON
{"data": {"files": [{"file": "grav.log", "label": "Grav System Log"}, {"file": "security.log", "label": "Security Log"}, {"file": "email.log", "label": "Email Log"}, {"file": "scheduler.log", "label": "Scheduler Log"}, {"file": "custom-plugin.log", "label": "Custom Plugin Log"}], "default": "grav.log"}}

Response Codes

200 Success
401 Unauthorized
403 Missing `api.system.read` permission, or a demo account

The four core logs are always listed first, even before they have been written. Any other *.log file in the log:// folder follows, then files plugins add through the onApiLogFiles event. Entries are de-duplicated by file name.

Clear Log

DELETE /system/logs
Empty one of the log files listed by `GET /system/logs/files`. The file is truncated rather than deleted, and one entry recording who cleared it is left in its place. Requires a super admin; demo accounts get 403. Fires `onApiLogCleared`.

Parameters

Name Type Description
file optional string Log file to clear (default `grav.log`). Can be sent in the JSON body or as a `?file=` query parameter.
JSON
{"file": "grav.log"}
JSON
{"data": {"file": "grav.log", "cleared_bytes": 450507, "message": "Log cleared: grav.log"}}

Response Codes

200 Log cleared, or already empty (`cleared_bytes: 0`)
401 Unauthorized
403 Not a super admin, or a demo account
422 Unknown log file, or the file is not writable

List Admin Languages

GET /admin/languages
List the languages the admin interface itself can be shown in, one per `languages/*.yaml` file shipped by the Admin2 plugin. This is different from `GET /languages`, which lists the site's content languages. Sorted by native name; empty when Admin2 is not installed. Requires `api.system.read`.
JSON
{"data": {"languages": [{"code": "en-US", "name": "English", "native_name": "English", "rtl": false}, {"code": "fr-FR", "name": "French", "native_name": "Français", "rtl": false}]}}

Response Codes

200 Success
401 Unauthorized
403 Missing `api.system.read` permission

Allow a Blocked Twig Token

POST /reports/twig-content/allowlist
Add one token that the page-content Twig sandbox blocked to the matching `security.twig_sandbox.allowed_*` list, saved in `user/config/security.yaml`. Recent block events for that token are removed from the Twig in Content report. Requires a super admin (an API key needs `admin.super` in its scopes).

Parameters

Name Type Description
rule required string The sandbox rule that blocked the token: `tag`, `filter`, `function`, `method` or `property`.
token required string The tag, filter, function, method or property name to allow.
class optional string Owning class. Required for `method` and `property`, ignored otherwise.
JSON
{"rule": "filter", "token": "markdown"}
JSON
{"data": {"rule": "filter", "key": "allowed_filters", "value": ["markdown"], "resolved": 2}}

Response Codes

200 Token added (or already present) and saved
401 Unauthorized
403 Not a super admin
422 Unknown rule, empty token, or a missing class for `method`/`property`

key is the security.twig_sandbox key that was written and value its full new value: a list of names for tag, filter and function, or a list of {class, methods} rows for method and property. resolved is the number of recorded block events this removed. The shipped defaults live in code, so the file only holds the site's own additions. Fires onApiConfigUpdated with scope security.

Clear Twig Content Events

DELETE /reports/twig-content/events
Empty the list of recently blocked or blanked content-Twig tokens that the Twig in Content report and the page editor banner read. `logs/security.log` is not touched. Requires `api.system.write`.
JSON
{"data": {"cleared": true}}

Response Codes

200 Cleared. `cleared` is false only if the stored list could not be deleted.
401 Unauthorized
403 Missing `api.system.write` permission

Twig Content Page Status

GET /reports/twig-content/page
Twig in Content status for a single page, used by the page editor banner: whether the page would show raw `{{ }}` / `{% %}` markers to visitors, and the recent block events recorded for its route. Requires `api.reports.read`.

Parameters

Name Type Description
route required string Page route, with leading slash.
JSON
{"data": {"route": "/blog/my-first-post", "gate": false, "sandbox": true, "leak": {"route": "/blog/my-first-post", "requested": true, "gate": false, "reason": "gate_off"}, "events": []}}

Response Codes

200 Success
401 Unauthorized
403 Missing `api.reports.read` permission
422 Missing `route` parameter

gate is security.twig_content.process_enabled and sandbox is security.twig_sandbox.enabled. leak is null when the page has no Twig markers, uses modular or theme Twig, will render its Twig normally, or cannot be found (a missing page is not an error). Its reason is gate_off when the site-wide gate is off, or page_off when the gate is on but the page does not ask for Twig processing. events lists recent blocks for exactly this route, newest first.

Scan Twig Content

GET /reports/twig-content/scan
Scan every page (skipping modular and theme Twig) for Twig tags, filters and functions in content that the current sandbox allowlists do not permit, that is, what content would need before the gate is switched on. Requires `api.reports.read`.
JSON
{"data": {"tags": {"include": ["/blog/my-first-post"]}, "filters": {"raw": ["/about"]}, "functions": {"source": ["/about", "/contact"]}}}

Response Codes

200 Success
401 Unauthorized
403 Missing `api.reports.read` permission

Each list maps a token to the routes that use it. A list with no findings arrives as an empty array ([]) rather than {}. This is a text scan and an approximation; the block events in the report, recorded at render time, are the reliable signal. The cost grows with the number of pages.

Twig Sandbox Policy

GET /reports/twig-content/sandbox-policy
Read-only breakdown of the page-content Twig sandbox: for each list, the built-in defaults, the site's own `allowed_*` additions, its `denied_*` removals, and the effective result. Requires `api.reports.read`.
JSON
{"data": {"enabled": true, "config_access": false, "lists": {"tags": {"defaults": ["if", "for", "set"], "added": [], "denied": [], "effective": ["if", "for", "set"]}, "filters": {"defaults": ["escape", "upper"], "added": ["markdown"], "denied": [], "effective": ["escape", "upper", "markdown"]}, "functions": {"defaults": ["range"], "added": [], "denied": [], "effective": ["range"]}, "methods": {"defaults": {"Grav\\Common\\Page\\Page": ["title", "url"]}, "added": [], "denied": [], "effective": {"Grav\\Common\\Page\\Page": ["title", "url"]}}, "properties": {"defaults": [], "added": [], "denied": [], "effective": []}}, "config_denied_paths": {"defaults": ["plugins", "streams", "security", "backups", "scheduler", "system.cache.redis.password", "system.debugger.token"], "added": [], "effective": ["plugins", "streams", "security", "backups", "scheduler", "system.cache.redis.password", "system.debugger.token"]}}}

Response Codes

200 Success
401 Unauthorized
403 Missing `api.reports.read` permission

The lists in the example are shortened. enabled is security.twig_sandbox.enabled and config_access is security.twig_content.config_access. In methods and properties each entry maps a class to its allowed member names, and an empty map arrives as []. config_denied_paths lists the configuration paths hidden from content Twig. The policy is built from configuration only; additions plugins make through onBuildTwigSandboxPolicy are not included.