Skip to content
API Reference

API Reference

All PLC-scoped endpoints use the prefix /api/plcs/{plcId}/ where plcId is the configured alias (e.g., Line1, MyPLC).

Response Envelope

Every response uses a standard envelope:

// Success
{ "data": { ... }, "error": null }

// Error
{ "data": null, "error": { "code": "ADS_TIMEOUT", "message": "..." } }

Error Codes

CodeHTTP StatusDescription
SYMBOL_NOT_FOUND404PLC symbol does not exist
PLC_NOT_FOUND404PLC alias not configured
PLC_UNAVAILABLE503PLC is not connected
ADS_TIMEOUT504ADS operation timed out
ADS_ERROR502Generic ADS protocol error
TYPE_MISMATCH400Value type doesn’t match what’s expected — e.g. a non-scalar (object/array) RPC parameter, see RPC
BATCH_LIMIT_EXCEEDED400Batch request exceeds 100 symbols
SUBSCRIPTION_LIMIT_EXCEEDED400Exceeded max symbols per client
RATE_LIMITED429Lifecycle rate limit hit
FEATURE_DISABLED404 (501 for Alarms, Rpc, TypeMetadata)Feature slice is disabled — most slices answer 404; those three answer 501 so a fleet-aggregating gateway can tell “genuinely absent” from “transiently failing”
UNAUTHORIZED401Missing or invalid JWT
FORBIDDEN403Insufficient role or scope
PLC_ACCESS_DENIED403plc_access claim denies this PLC
MASTER_NOT_FOUND404EtherCAT master ID not found
SLAVE_NOT_FOUND404EtherCAT slave address not found
NOT_IMPLEMENTED501Endpoint not implemented by the underlying ADS library (all /files routes)

Symbol Path Format

TwinCAT dot notation: MAIN.nCounter, MAIN.stMotor.bEnabled, GVL.arr[0]

Array indices with brackets must be URL-encoded in path segments: MAIN.arr%5B0%5D

System Endpoints

GET  /api/plcs                   List all PLCs + connection status
GET  /api/plcs/{plcId}/status    Detailed status for one PLC
GET  /health/live                Liveness probe
GET  /health/ready               Readiness probe (PLC connectivity)
GET  /health/alarms              Worst outstanding alarm severity (when Alarms is enabled)
GET  /openapi/v1.json            OpenAPI spec
GET  /swagger                    Scalar API UI (Development only)

Health probes

The /health/* routes are the only anonymous surface adsify exposes — /api/plcs and /api/plcs/{plcId}/status both require a token, which a load balancer or uptime checker generally does not hold.

/health/live answers 200 Healthy for as long as the process runs. It consults no PLC, so it stays a plain-text body.

/health/ready runs the checks that say whether adsify can serve what it was configured to serve — plc-connectivity, plus ethercat-diagnostics when that feature is enabled — and answers JSON naming each configured PLC:

{
  "status": "Degraded",
  "checks": [
    {
      "name": "plc-connectivity",
      "status": "Degraded",
      "description": "1/2 PLCs connected",
      "data": { "Line1": "Connected", "Line2": "Disconnected" }
    }
  ]
}
ConditionStatusHTTP
All configured PLCs connectedHealthy200
Some connected, some notDegraded200
No PLCs connected, or none configuredUnhealthy503
An EtherCAT master’s diagnostics are unreadableDegraded200

When EtherCAT diagnostics are enabled, a second check reports whether adsify can still read each master — a different question from whether the PLC is connected, and the two come apart exactly when it matters: an ADS connection can be healthy while the master behind it has been unreadable for minutes.

{
  "name": "ethercat-diagnostics",
  "status": "Degraded",
  "description": "1/2 EtherCAT masters have unreadable diagnostics",
  "data": { "Line1/1": "Ok", "Line1/2": "DiagnosticsDegraded" }
}

A master adsify cannot read at all reports DiagnosticsDegraded and degrades the check. One it read but not completely reports IncompleteReads: … and does not — a host whose frame-counter index group is unsupported reports frameStatistics incomplete on every cycle for the life of the deployment, and that is not a fault. See EtherCAT for both signals.

A partial outage answers 200, deliberately. A host serving several targets can still serve the ones that are up, so failing the probe over one unreachable line would pull the working PLCs out of rotation too. Alert on the body — data names the PLC — rather than on the status code.

/health/ready never reflects alarm severity. See Alarms for /health/alarms and why the two are separate.