Skip to content

System API Documentation ​

System health monitoring and diagnostics endpoints for infrastructure components.

Check System Health ​

GET /api/v1/system-health-check

API Usage Notes

  • Authentication required: Admin API Key (via Bearer token or query parameter), or the dedicated health-check token when SYSTEM_HEALTH_CHECK_AUTH_REQUIRED is on
  • Legacy endpoint access via /api.php is also supported
  • Admin-key calls are subject to ADMIN_API_ENFORCE_ALLOWED_IP; token calls are not

Request Parameters:

ParameterTypeRequiredDescription
CommandStringYesAPI command: system.health.check (only for legacy endpoint)
AdminAPIKeyStringNoAdmin API key, master or per-sub-admin (alternative to Bearer token)
HealthCheckTokenStringNoThe install's SYSTEM_HEALTH_CHECK_TOKEN (alternative to a Bearer token carrying the same value). Honoured only when SYSTEM_HEALTH_CHECK_AUTH_REQUIRED is on

Authentication Methods:

This endpoint supports two authentication methods:

  1. Bearer Token (Recommended): Include the Admin API Key in the Authorization header:

    Authorization: Bearer YOUR_ADMIN_API_KEY
  2. Query Parameter: Pass the Admin API Key as a query parameter:

    ?adminapikey=YOUR_ADMIN_API_KEY

Strict authentication mode (v5.9.6) ​

With SYSTEM_HEALTH_CHECK_AUTH_REQUIRED=true in .oempro_env (the default for fresh installs), the endpoint accepts exactly two credentials:

  1. Admin API key, master or per-sub-admin, as AdminAPIKey or Authorization: Bearer <key>.
  2. Health-check token, the value of SYSTEM_HEALTH_CHECK_TOKEN, as HealthCheckToken or Authorization: Bearer <token>. Use this for external monitors so they never hold the master key; it grants the health report and nothing else.

Any other call answers HTTP 401. With the switch off (upgraded installs that have not enabled it), the historical behaviour is unchanged: the master key only, refused with HTTP 500 and error 100005.

bash
curl -X GET https://example.com/api/v1/system-health-check \
  -H "Authorization: Bearer YOUR_SYSTEM_HEALTH_CHECK_TOKEN"
json
{
  "Success": false,
  "ErrorCode": 99998,
  "ErrorMessage": "Invalid API key"
}
CodeHTTPDescription
99998401Strict mode: no valid admin key or health-check token was presented
100005500Legacy mode: the master admin key was missing or wrong
bash
curl -X GET https://example.com/api/v1/system-health-check \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY"
bash
curl -X GET "https://example.com/api/v1/system-health-check?adminapikey=YOUR_ADMIN_API_KEY"
bash
curl -X GET "https://example.com/api.php?Command=system.health.check&adminapikey=YOUR_ADMIN_API_KEY"
json
{
  "Checks": {
    "MySQL": "OK",
    "ClickHouse": "OK",
    "RabbitMQ": "OK",
    "Redis": "OK",
    "Session": "OK",
    "SystemContainer": "OK",
    "Vector": "OK",
    "WebsiteEventRouting": "OK",
    "Haproxy": "OK",
    "ClientIPResolution": "OK",
    "ListUnsubscribeOneClick": "OK",
    "TrackingDomainTLS": "OK",
    "Cron": "OK",
    "Supervisor": "OK",
    "SendEngine": "OK",
    "AdminFrontend": "OK",
    "UserFrontend": "OK",
    "FilePermissions": "OK"
  }
}
json
{
  "Checks": {
    "MySQL": "OK",
    "ClickHouse": "OK",
    "RabbitMQ": "OK",
    "Redis": "[111] Connection refused",
    "Session": "OK",
    "SystemContainer": "OK",
    "Vector": "Timeout after 3 seconds",
    "WebsiteEventRouting": "HAProxy did not route /hello/message to backend_vector (HTTP 503; \"X-Server: oempro_vector\" response header missing). Check that backend_vector has an available server.",
    "NewUserInterface": "UI_ENABLED is true but the new user interface did not answer /ui/health through HAProxy (HTTP 404). Check: docker logs oempro_ui. If it last started with UI_ENABLED off, run ./cli/octeth.sh docker:up, or docker restart oempro_ui.",
    "Haproxy": "OK",
    "ClientIPResolution": "WARNING: this request carried X-Forwarded-For (\"203.0.113.7\") but the client address resolved to 192.168.99.100, which is inside INTERNAL_PROXY_NETWORKS. The forwarded chain is being discarded ...",
    "ListUnsubscribeOneClick": "WARNING: this install is not advertising RFC 8058 one-click unsubscribe on every path. APP_URL is \"http://mail.example.com/\", which is not https, so campaign and autoresponder messages emit an http: List-Unsubscribe URI and their List-Unsubscribe-Post header is SUPPRESSED ...",
    "TrackingDomainTLS": "WARNING: 2 enabled sender domain(s) have a tracking host that did not complete a TLS handshake when probed from this server: example.com (handshake_failed), example.org (expired). Their click, open and List-Unsubscribe links may not work over https. Check the certificate Caddy issued for each tracking host; the probe repeats on the hourly re-verification sweep.",
    "Cron": "App container cron not executing (last run: 120 seconds ago)",
    "Supervisor": "# campaign_delivery_worker: STOPPED # journey_worker: FATAL ",
    "SendEngine": "No send engine containers running",
    "AdminFrontend": "OK",
    "UserFrontend": "Backend user login form action is not found",
    "FilePermissions": "logs/campaign_delivery.log: expected 0777, 0666 or 0644, found 0640"
  }
}
json
{
  "Errors": [
    {
      "Code": 1,
      "Message": "Authentication failed. Invalid admin API key."
    }
  ]
}
txt
0: Success - All health checks passed
1: Authentication failed. Invalid admin API key.

HTTP Status Codes:
200: All health checks passed
403: Authentication failed
503: One or more health checks failed (see Checks object for details), or MySQL could not be reached (plain-text body, see below)

Health Check Components:

The endpoint performs comprehensive health checks on the following components:

  • MySQL: Database connectivity and admin user existence. The check fails with There is no registered admin user. when the admins table is empty Changed in v6.0.1

  • ClickHouse: Analytics database connectivity

  • RabbitMQ: Message queue connectivity

  • Redis: Cache server connectivity

  • Session: PHP session functionality

  • SystemContainer: Laravel backend container health (/system/ping)

  • Vector: Log aggregation service health (probed directly)

  • WebsiteEventRouting New in v5.9.3: The full load-balancer → Vector path used by the public website-event tracker

  • NewUserInterface New in v6.0.1: Whether the new user interface answers through the load balancer. Present only when UI_ENABLED is true

  • Haproxy: Load balancer connectivity

  • ClientIPResolution New in v6.0.0: Whether the real visitor IP is being resolved, or a forwarded chain is being discarded and a container address recorded instead

  • ListUnsubscribeOneClick New in v6.0.0: Whether this install can advertise RFC 8058 one-click unsubscribe, which requires https from both APP_URL and TRACKING_URL_PROTOCOL

  • TrackingDomainTLS New in v6.0.1: Whether every enabled sender domain's tracking host completed a TLS handshake at its last out-of-band probe. A warning only, it never makes the endpoint fail

  • Cron: App and system container cron job execution (heartbeat checks)

  • Supervisor: Process manager status for all managed processes

  • SendEngine: Send engine container discovery and supervisor process status

  • AdminFrontend: Admin login page renders on the origin server, with the expected login form Changed in v6.0.1

  • UserFrontend: User login page renders on the origin server, with the expected login form Changed in v6.0.1

  • FilePermissions: Directory and log file permissions (expects 0777 for directories, 0777/0666/0644 for log files)

Response Details:

  • Each check returns "OK" if successful
  • Failed checks return detailed error messages explaining the failure
  • HTTP status code 200 indicates all checks passed
  • HTTP status code 503 indicates one or more checks failed (response still includes all check results)
  • When MySQL cannot be reached at all (connection refused, unknown host, too many connections, authentication failure or a missing database), the request ends before the handler runs. The response is HTTP 503 with a Retry-After: 30 header and a plain-text body starting MySQL Error:, not the JSON Checks object Changed in v6.0.1. Earlier versions answered this case with HTTP 200, so monitors must key on the status code, not on the body. Every other page and API command returns the same 503 during a MySQL outage.
  • The Cron check monitors heartbeat files updated every minute; considers cron failed if > 90 seconds since last update
  • The Supervisor check reports processes not in RUNNING state with format: # process_name: STATE
  • The SendEngine check discovers containers dynamically using Docker Compose project prefix detection

The ClientIPResolution check ​

New in v6.0.0

This check reports whether the install is recording the real visitor IP, or a container address.

Forwarded headers are only honoured when the request's immediate peer is trusted, which is loopback, the bundled composition's own network (INTERNAL_PROXY_NETWORKS), or a proxy you listed in TRUSTED_PROXIES. If you put your own load balancer, reverse proxy or CDN in front of Octeth at an address that is not in those sets, the X-Forwarded-For chain is discarded and that proxy's address is recorded as the visitor for every request.

The symptoms are wide and none of them look like an IP problem:

  • the admin Authorized IP Addresses allow-list compares the wrong address, so it either locks everyone out or admits everyone;
  • per-IP rate limits treat all traffic as a single visitor;
  • geo attribution resolves every open and click to one place;
  • SubscriptionIP, OptInIP and UnsubscriptionIP, which exist as the consent audit trail, hold a container address. That one is not recoverable, because the real address was never stored.

The check fires only on the combination that actually proves the fault: a forwarded chain arrived and the address resolution still settled on an internal one. A caller with no forwarded chain is a direct internal caller, which says nothing either way and is reported as OK, so a health check run from inside the install (a monitor container, the CLI, a cron) does not produce a false warning.

If it warns, add your proxy's address or subnet to TRUSTED_PROXIES, or set TRUST_CLOUDFLARE_CONNECTING_IP=true if you are behind Cloudflare. If you changed the compose network, correct INTERNAL_PROXY_NETWORKS instead.

The ListUnsubscribeOneClick check ​

New in v6.0.0

This check reports whether the install is able to advertise RFC 8058 one-click unsubscribe, which Gmail and Yahoo require of bulk senders.

RFC 8058 requires the List-Unsubscribe URI paired with List-Unsubscribe-Post to be https. Where that URI's scheme comes from depends on the send path, and two independent settings decide it:

SettingGoverns
APP_URLCampaign and autoresponder messages, which build the URI from APP_URL and replace only the host with the tracking domain
TRACKING_URL_PROTOCOLEmail Gateway and opt-in confirmation messages sent on a sender domain's own tracking host, which do not consult APP_URL at all

Wherever the resulting URI would be http:, Octeth suppresses the List-Unsubscribe-Post header on that path rather than advertise one-click against a URI that does not meet the specification, because a receiver that notices may distrust the header pair entirely.

So an https APP_URL on its own does not guarantee one-click everywhere. The check inspects both settings and names whichever is not https, along with the paths that setting governs, so a mismatch between the two cannot hide.

That suppression is invisible from outside the install: mail still delivers, recipients can still unsubscribe through the List-Unsubscribe URI, and nothing reports an error. This check is the only place an operator finds out it is happening.

It reads configuration rather than the current request, so it reports the same result on every scrape.

A custom tracking domain takes its certificate from Caddy on-demand TLS, and an https URI whose TLS handshake fails is worse than an http one. That case is reported by the separate TrackingDomainTLS check.

The TrackingDomainTLS check ​

New in v6.0.1

Each sender domain's tracking host gets its certificate from Caddy on-demand TLS. If issuance fails for one domain (the customer's DNS record was repointed, the host was put behind a CDN, an ACME rate limit was hit), every click, open and List-Unsubscribe URL in that customer's mail points at an https host whose handshake fails. Mail still delivers and nothing else reports it.

The sender_domain_verifier worker probes each enabled sender domain's tracking host out of band: it opens a TLS connection to port 443 with certificate and host name verification, and stores the outcome on the sender domain. It runs on the hourly re-verification sweep (SENDER_DOMAIN_REVERIFY_STALENESS_HOURS decides how often each domain is probed, and probes share the SENDER_DOMAIN_REVERIFY_BATCH_SIZE cap with DNS re-verification) and once about two minutes after a domain is verified. A failure is stored only when a confirmation probe about two minutes later fails too.

This check reads those stored results. It never probes during the request, so it is cheap to scrape. It names up to 20 affected domains with the reason in parentheses, then a count of the rest:

ReasonMeaning
dns_failedThe tracking host does not resolve
blocked_addressIt resolves to a private or reserved address, so it was not probed
unreachableNo TCP connection on port 443
handshake_failedPort 443 answered but the handshake failed, typically no certificate issued
name_mismatchThe certificate does not cover the tracking host
expired / not_yet_validThe certificate is outside its validity period
self_signed / untrustedThe certificate does not chain to a trusted authority

The value is a warning and never sets the overall result to failed. The probe runs from the Octeth server, and a recipient's network can see a different answer, for example when a CDN blocks the origin's address. The same per-domain warning, with the full error text, is shown to the domain's owner on the sender domain screen.

Only hosts listed in the domain's DNS record template are probed, because only those are used by the send paths. The default sender domain is never probed. A failing probe never disables a domain or changes where its links point.

If the check reports that the results could not be read, the database migration that adds the probe columns has not run yet.

The WebsiteEventRouting check ​

New in v5.9.3

The Vector check talks to the Vector container directly, so it cannot see a broken load-balancer route. WebsiteEventRouting exercises the routed path instead, the same path the public website-event tracker uses, so that failure mode is actually detected. Both checks are kept, so a genuine Vector outage stays distinguishable from broken routing.

This exists because of a real, long-lived outage: a startup DNS race left the Vector backend with no available server for roughly four weeks. The load balancer resolved the container name once at boot, failed while the container was still starting, and permanently disabled the server. Vector itself stayed healthy the whole time, so the health check reported OK while the public website-event endpoint returned HTTP 503 to every visitor.

How it probes:

  • It issues a GET to the website-event path through the load balancer. GET is deliberate: Vector's HTTP source only accepts POST, so a GET is rejected before ingestion. A synthetic POST would be forwarded downstream and inject a junk website event on every health-check tick.
  • It is also deliberately not an OPTIONS request: OPTIONS on the website-event paths is answered by a static backend that never touches Vector, so an OPTIONS probe would have stayed green throughout the outage described above.
  • The assertion is the presence of the X-Server: oempro_vector response header, which only the Vector backend adds. The load balancer's internally generated "no server available" 503 is produced before those backend response rules run and therefore carries no such header. Matching the header rather than the status code proves the response really came from Vector through the intended route.

On failure the check reports the observed HTTP status and states that the X-Server: oempro_vector header was missing, pointing the operator at the Vector backend's server availability.

The NewUserInterface check ​

New in v6.0.1

This check reports whether the new user interface is actually serving pages. It runs only when UI_ENABLED is true in .oempro_env. With the flag off, neither Checks nor Timings has a NewUserInterface entry, so the response is unchanged for installs that do not use the new interface.

The interface container decides once, when it starts, whether to serve the interface or answer "not found" for every page. Before v6.0.1, turning UI_ENABLED on could leave it serving "not found" while the load balancer, recreated with the new flag, sent /user/ and /ui/ to it, and nothing reported the problem.

How it probes:

  • It issues a GET to http://oempro_haproxy:80/ui/health, through the load balancer, so one request covers both the routing and the container's state. The request carries the host of APP_URL as its Host header.
  • It passes only when the response is HTTP 200 and carries the X-Server: oempro_ui response header. The status matters because the interface container adds that header to its "not found" answers too. The header matters because without the routing rules /ui/health falls through to the classic application. When the request is redirected, only the final response's headers count.

On failure the check reports the observed HTTP status, states whether the X-Server: oempro_ui header was missing, and names the fix: read docker logs oempro_ui, and if the interface last started with UI_ENABLED off, run ./cli/octeth.sh docker:up or docker restart oempro_ui. A transport error (for example a timeout after 3 seconds) is reported as the error text. Either way the check counts as a failure of the whole call: the response answers HTTP 503 instead of 200, like any other failed check, and the failure is written to data/logs/health_check_errors.log. See The interface container is unhealthy.

The AdminFrontend and UserFrontend checks ​

Changed in v6.0.1

These checks load the admin and user login pages and confirm each page contains its login form. They test the origin server only. They do not test whether the login pages are reachable through your CDN, WAF or load balancer.

Before v6.0.1 the checks requested the login pages through the public APP_URL. The request left the server, resolved the app domain through public DNS and came back in through any CDN in front of the install. A firewall rule that restricted /app/admin* to your own IP address, or a short CDN outage, therefore failed the check and turned the whole response into HTTP 503 while the application was healthy.

How they probe now:

  • Each check requests http://127.0.0.1/app/admin/ or http://127.0.0.1/app/user/ from inside the application container, with the host of APP_URL as the Host header. No DNS lookup is made for the app domain.
  • Redirects are not followed, so the request cannot be sent back out through the public URL. A redirect answer counts as a failure.
  • The request comes from 127.0.0.1, which the admin Authorized IP Addresses allow-list always permits, so an allow-list does not block the check.
  • The check passes when the page contains the login form, whose action is built from APP_URL. On failure it reports Backend admin login form action is not found (or the user equivalent), or the connection error text.

The check names, the failure messages and the 200/503 behaviour are unchanged.

To monitor whether the login pages are reachable from the internet, point an external uptime monitor at your public login URL, for example https://example.com/app/admin/. That monitor sees the CDN, DNS and TLS path that these checks deliberately skip.

Process PowerMTA Log File ​

POST /api/v1/process_pmta_log_file

API Usage Notes

  • Authentication required: Admin API Key (via Bearer token)
  • Rate limit: 100 requests per 60 seconds
  • This endpoint is designed for PowerMTA integration to process email delivery logs
  • Accepts batch processing of email events (delivery, bounce, etc.)
  • Legacy endpoint access via /api.php is also supported

Request Body Parameters:

ParameterTypeRequiredDescription
AdminAPIKeyStringYesAdmin API key for authentication (via Bearer token)
LogDataArrayYesArray of PowerMTA log entries (JSON format in POST body)

PowerMTA Log Entry Format:

Each log entry in the array should contain PowerMTA event data following the PowerMTA log format specification.

bash
curl -X POST https://example.com/api/v1/process_pmta_log_file \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ADMIN_API_KEY" \
  -d '[
    {
      "type": "delivery",
      "timeLogged": "2025-01-15T10:30:00Z",
      "recipient": "user@example.com",
      "messageId": "550e8400-e29b-41d4-a716-446655440000"
    },
    {
      "type": "bounce",
      "timeLogged": "2025-01-15T10:31:00Z",
      "recipient": "invalid@example.com",
      "bounceType": "hard"
    }
  ]'
json
{
  "Success": true,
  "ProcessedCount": 2,
  "ErrorCount": 0
}
json
{
  "Errors": [
    {
      "Code": 1,
      "Message": "Invalid AdminAPIKey"
    }
  ]
}
json
{
  "Errors": [
    {
      "Code": 2,
      "Message": "Invalid or empty log data"
    }
  ]
}
txt
200: Success - Log data processed successfully
400: Bad Request - Invalid or empty log data
401: Unauthorized - Invalid Admin API Key
429: Too many requests - Rate limit exceeded
txt
1: Invalid AdminAPIKey
2: Invalid or empty log data

Processing Behavior:

  • Accepts both JSON and form-encoded POST data
  • Processes events in batch for improved performance
  • Returns processing results with success/error counts
  • Failed events within a batch do not cause the entire batch to fail

Get System Settings ​

GET /api/v1/system-settings

API Usage Notes

  • Authentication required: Admin API Key (or logged-in admin session)
  • Legacy endpoint access via /api.php is also supported

Returns a unified snapshot of every Octeth configuration value the running instance can see. Settings are grouped by source so callers can drill into a specific layer without merging four separate views themselves.

The endpoint surfaces four sources:

  1. ConfigFiles: every *.php file under config/global/ (each returns an array). The app.php file is intentionally skipped because it declares the SystemConfig class and has side effects; its values are already represented in EnvSettings.octeth and DefinedConstants.
  2. EnvSettings: SystemConfig::$Config, populated from the six .oempro_*_env files: octeth, mysql, redis, rabbitmq, clickhouse, supervisor.
  3. RuntimeOptions: every row in the oempro_options table (the runtime-editable options used by OemproOptions::get()/set()). JSON-encoded values are decoded automatically when they are arrays.
  4. DefinedConstants: get_defined_constants(true)['user']: every constant registered by ConfigLoader plus library define() calls.

Sensitive value redaction: any key whose name contains a known secret substring (case-insensitive: PASSWORD, PASSWD, _PASS, SECRET, SALT, TOKEN, API_KEY, APIKEY, CLIENT_SECRET, ENCRYPTION_KEY, HMAC, ERLANG_COOKIE, LICENSE_KEY, AUTH_CODE, BUGSNAG_API, SENTRY_API, PRIVATE_KEY, WEBHOOK_SECRET) has its value replaced with the literal string "***REDACTED***". The key itself remains visible. Empty values pass through untouched so callers can distinguish "configured but blank" from "configured with a value, redacted". Never post the literal ***REDACTED*** back as a value: settings.update refuses it with ErrorCode 13 (see Update System Settings), and no other write command treats it specially, so writing it anywhere else stores the marker as the real value. Omit a field to keep its stored secret.

Request Parameters:

ParameterTypeRequiredDescription
CommandStringYesAPI command: system.getsettings (only for legacy endpoint)
AdminAPIKeyStringNoAdmin API key for authentication (alternative to logged-in admin session). Sent as adminapikey query parameter.
SectionStringNoRestrict the response to a single source. Possible values: ConfigFiles, EnvSettings, RuntimeOptions, DefinedConstants. If omitted, all four sections are returned.
bash
curl -X GET "https://example.com/api/v1/system-settings?adminapikey=YOUR_ADMIN_API_KEY"
bash
curl -X GET "https://example.com/api.php?Command=System.GetSettings&adminapikey=YOUR_ADMIN_API_KEY"
bash
curl -X GET "https://example.com/api/v1/system-settings?Section=EnvSettings&adminapikey=YOUR_ADMIN_API_KEY"
json
{
  "Success": true,
  "Settings": {
    "ConfigFiles": {
      "email_delivery": { "EMAIL_DELIVERY_BATCH_SIZE": 100 },
      "sms": { "SMS_GATEWAY_TIMEOUT": 30 }
    },
    "EnvSettings": {
      "octeth": {
        "APP_URL": "https://example.com/",
        "PRODUCT_VERSION": "5.9.1",
        "MYSQL_PASSWORD": "***REDACTED***",
        "LICENSE_KEY": "***REDACTED***",
        "OEMPRO_PASSWORD_SALT": "***REDACTED***"
      },
      "mysql":      { "MYSQL_HOST": "oempro_mysql", "MYSQL_PASSWORD": "***REDACTED***" },
      "redis":      { "REDIS_HOST": "oempro_redis" },
      "rabbitmq":   { "RABBITMQ_DEFAULT_USER": "oempro", "RABBITMQ_DEFAULT_PASS": "***REDACTED***" },
      "clickhouse": { "CLICKHOUSE_HOST": "oempro_clickhouse" },
      "supervisor": { "SUPERVISOR_USERNAME": "admin" }
    },
    "RuntimeOptions": {
      "FailedWebhookHandler": "email",
      "DisableUserPasswordReset": ""
    },
    "DefinedConstants": {
      "APP_PATH": "/var/www/html",
      "CHARSET": "utf-8",
      "OEMPRO_DEBUG": true,
      "PRODUCT_VERSION": "5.9.1",
      "ADMIN_API_KEY": "***REDACTED***"
    }
  }
}
json
{
  "Success": false,
  "ErrorCode": 1,
  "ErrorMessage": "Invalid Section. Allowed: ConfigFiles, EnvSettings, RuntimeOptions, DefinedConstants"
}
txt
1: Invalid Section value (must be one of ConfigFiles, EnvSettings, RuntimeOptions, DefinedConstants)
99998: Authentication required - missing or invalid admin credentials

Use cases:

  • Diagnosing configuration drift between environments
  • Support escalations where the operator needs to share a redacted config snapshot
  • Verifying that a setting (env var, runtime option, or constant) is actually present and reaching the application
  • Auditing which secrets are configured without exposing their values

List Installed Language Packs ​

GET /api/v1/system.languages.get

API Usage Notes

  • Authentication required: Admin API Key (privilege Settings)
  • v1 REST alias: GET /api/v1/system.languages.get. Legacy access via /api.php is also supported
  • The only values settings.update accepts for DEFAULT_LANGUAGE and USER_SIGNUP_LANGUAGE.

Request Body Parameters:

ParameterTypeRequiredDescription
CommandStringYesAPI command: system.languages.get
SessionIDStringNoSession ID obtained from login
APIKeyStringNoAdmin API key for authentication
bash
curl -X POST https://example.com/api.php \
  -H "Content-Type: application/json" \
  -d '{"Command": "system.languages.get", "APIKey": "your-admin-api-key"}'
json
{
  "Success": true,
  "ErrorCode": 0,
  "Languages": [{"Code": "en", "Name": "English"}],
  "TotalLanguages": 1
}
json
{
  "Success": false,
  "ErrorCode": 99998
}
txt
0: Success

Run the System Check ​

GET /api/v1/admin.system.checkNew in v5.9.6

API Usage Notes

  • Authentication required: Admin API Key (privilege System)
  • Rate limit: 100 requests per 60 seconds
  • Legacy endpoint access via /api.php is also supported

Runs the requirements and settings check shown on the admin About page: PHP ini flags (register_globals, magic_quotes_*, safe_mode, max_execution_time), the required PHP extensions, the PHP version, write access to the data/ directories, and whether MySQL runs with a STRICT sql_mode. Directory messages name the path relative to the application root; the response never carries an absolute path.

Errors is a map of category (PHP Errors, Directory Errors, MySQL Errors) to the list of messages in that category. Categories with no message are omitted, so a clean install returns an empty map ([] in JSON) and Passed: true.

Request Body Parameters:

ParameterTypeRequiredDescription
CommandStringYesAPI command: admin.system.check
AdminAPIKeyStringYesAdmin API key
bash
curl -X GET "https://example.com/api/v1/admin.system.check?AdminAPIKey=your-admin-api-key"
json
{
  "Success": true,
  "ErrorCode": 0,
  "ErrorText": "",
  "Passed": false,
  "Errors": {
    "PHP Errors": [
      "PHP IMAP extension is disabled. Please enable it."
    ],
    "MySQL Errors": [
      "MySQL runs in STRICT mode. Please disable STRICT option in sql_mode parameter in the MySQL configuration file."
    ]
  },
  "TotalErrors": 2
}
json
{
  "Success": false,
  "ErrorCode": 99998
}
txt
0: Success
99998: Authentication failure
99999: Not enough privileges (sub-admin without the System privilege, when ADMIN_API_ENFORCE_PRIVILEGES is on)

Get System Information ​

GET /api/v1/admin.system.infoNew in v5.9.6

API Usage Notes

  • Authentication required: Admin API Key (privilege System)
  • Rate limit: 100 requests per 60 seconds
  • Legacy endpoint access via /api.php is also supported

Returns the version and runtime facts the admin About page shows: the Octeth product version, the PHP version, the PHP ini values the page lists, and the MySQL server version. Nothing else is included: no filesystem paths, no environment values and no phpinfo() output (the About page's PHP settings dump and the database export stay UI-only). For the configuration itself use system.getsettings.

PHPSettings reports an empty ini value as the string "false", exactly as the About page renders it.

Request Body Parameters:

ParameterTypeRequiredDescription
CommandStringYesAPI command: admin.system.info
AdminAPIKeyStringYesAdmin API key
bash
curl -X GET "https://example.com/api/v1/admin.system.info?AdminAPIKey=your-admin-api-key"
json
{
  "Success": true,
  "ErrorCode": 0,
  "ErrorText": "",
  "ProductVersion": "6.0.0",
  "PHPVersion": "5.6.40",
  "PHPSettings": {
    "memory_limit": "512M",
    "max_execution_time": "0",
    "safe_mode": "false",
    "magic_quotes_gpc": "false",
    "register_globals": "false",
    "magic_quotes_runtime": "false"
  },
  "MySQLVersion": "8.0.41"
}
json
{
  "Success": false,
  "ErrorCode": 99998
}
txt
0: Success
99998: Authentication failure
99999: Not enough privileges

Any questions? Contact us.