The OpenPLC Runtime v4 provides an internal REST API over HTTPS used by the OpenPLC Editor desktop application. The API is not intended for direct end-user interaction but can be used for advanced integration or diagnostics.
All endpoints are accessible at https://<host>:8443/api/<endpoint>.
https://localhost:8443/api
The runtime uses JWT-based authentication:
- First User Creation:
POST /api/create-user(no authentication required for the first user) - Login:
POST /api/loginreturns a JWT access token - Authenticated Requests: All other endpoints require
Authorization: Bearer <token>header
Note: The OpenPLC Editor handles authentication automatically. Advanced integrators must implement the authentication flow manually.
All API responses are JSON objects. Successful responses typically include a status field, while errors include descriptive error messages.
Create a new user account. The first user can be created without authentication. Subsequent users require JWT authentication.
Request:
POST /api/create-user
Content-Type: application/json
{
"username": "admin",
"password": "your_password",
"role": "admin"
}Response (Success):
{
"msg": "User created",
"id": 1
}Response (Error):
{
"msg": "Username already exists"
}Status Codes:
201 Created- User created successfully400 Bad Request- Missing username or password401 Unauthorized- User already exists and no valid JWT provided409 Conflict- Username already exists
Authenticate and receive a JWT access token.
Request:
POST /api/login
Content-Type: application/json
{
"username": "admin",
"password": "your_password"
}Response (Success):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Response (Error):
"Wrong username or password"Status Codes:
200 OK- Login successful401 Unauthorized- Invalid credentials500 Internal Server Error- Database error
Notes:
- The access token should be included in the
Authorization: Bearer <token>header for all subsequent requests - Tokens expire after a configured duration (default: 24 hours)
All PLC control endpoints require JWT authentication.
Start the PLC program execution.
Request:
GET /api/start-plc
Authorization: Bearer <token>Response:
{
"status": "PLC started successfully"
}Possible Status Values:
"PLC started successfully"- PLC transitioned to RUNNING state"PLC is already running"- PLC was already in RUNNING state"No PLC program loaded"- No compiled program available"No response from runtime"- Runtime process not responding
Stop the PLC program execution.
Request:
GET /api/stop-plc
Authorization: Bearer <token>Response:
{
"status": "PLC stopped successfully"
}Possible Status Values:
"PLC stopped successfully"- PLC transitioned to STOPPED state"PLC is already stopped"- PLC was already in STOPPED state"No response from runtime"- Runtime process not responding
Query the current PLC state.
Request:
GET /api/status
Authorization: Bearer <token>Response:
{
"status": "RUNNING"
}Possible Status Values:
"EMPTY"- No PLC program loaded"INIT"- Program loaded, initializing"RUNNING"- Actively executing scan cycles"STOPPED"- Program loaded but not executing"ERROR"- Recoverable error state"No response from runtime"- Runtime process not responding
Check if the runtime process is responsive.
Request:
GET /api/ping
Authorization: Bearer <token>Response:
{
"status": "pong"
}Possible Status Values:
"pong"- Runtime is responsivenull- Runtime not responding
Upload a ZIP file containing the PLC program source files generated by OpenPLC Editor v4.
Request:
POST /api/upload-file
Authorization: Bearer <token>
Content-Type: multipart/form-data
file: <ZIP file>Success Response:
{
"UploadFileFail": "",
"CompilationStatus": "COMPILING"
}Error Response:
{
"UploadFileFail": "Error message",
"CompilationStatus": "FAILED"
}Compilation Status Values:
"IDLE"- No build in progress"UNZIPPING"- Extracting ZIP file"COMPILING"- Running compilation scripts"SUCCESS"- Build completed successfully"FAILED"- Build failed
Error Conditions:
- No file in request
- File too large (>10 MB per file, >50 MB total)
- ZIP validation failed (path traversal, compression ratio, disallowed extensions)
- Another compilation in progress
- File system error
Notes:
- Compilation runs asynchronously in a background thread
- Use the
/api/compilation-statusendpoint to monitor progress - The PLC is automatically stopped during compilation
- The OpenPLC Editor compiles the program locally (JSON → XML → ST → C) and uploads the source files as a ZIP
Query the status of the most recent compilation.
Request:
GET /api/compilation-status
Authorization: Bearer <token>Response:
{
"status": "SUCCESS",
"logs": [
"[INFO] Starting compilation",
"[INFO] Compiling Config0.c...",
"[INFO] Compiling Res0.c...",
"[INFO] Compiling debug.c...",
"[INFO] Compiling glueVars.c...",
"[INFO] Compiling c_blocks_code.cpp...",
"[INFO] Compiling shared library...",
"[INFO] Build finished successfully"
],
"exit_code": 0
}Response Fields:
status- Current build status (IDLE, UNZIPPING, COMPILING, SUCCESS, FAILED)logs- Array of log messages from the build processexit_code- Exit code of the compilation script (0 = success, non-zero = error, null = in progress)
Notes:
- The OpenPLC Editor polls this endpoint to monitor compilation progress
- Logs are accumulated during compilation
- Error messages are prefixed with
[ERROR] - Exit code is
nulluntil compilation completes
Retrieve logs from the PLC runtime process.
Request:
GET /api/runtime-logs
GET /api/runtime-logs?id=<min_id>
GET /api/runtime-logs?level=<log_level>
Authorization: Bearer <token>Query Parameters:
id(optional) - Minimum log ID to retrieve (for pagination)level(optional) - Filter by log level (DEBUG, INFO, WARNING, ERROR)
Response:
{
"runtime-logs": [
{
"id": 1,
"timestamp": "2024-01-01T12:00:00.000Z",
"level": "INFO",
"message": "PLC started successfully"
},
{
"id": 2,
"timestamp": "2024-01-01T12:00:05.000Z",
"level": "DEBUG",
"message": "Scan Count: 100"
}
]
}Log Levels:
DEBUG- Detailed diagnostic informationINFO- General informational messagesWARNING- Warning messagesERROR- Error messages
200 OK- Request successful201 Created- Resource created successfully400 Bad Request- Invalid request parameters401 Unauthorized- Authentication required or invalid token409 Conflict- Resource conflict (e.g., username already exists)500 Internal Server Error- Server error
{
"error": "Error message description"
}Or for some endpoints:
{
"msg": "Error message description"
}cURL Examples:
# Step 1: Create first user
curl -k -X POST https://localhost:8443/api/create-user \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123","role":"admin"}'
# Step 2: Login and get JWT token
TOKEN=$(curl -k -X POST https://localhost:8443/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' \
| jq -r '.access_token')
echo "Token: $TOKEN"
# Step 3: Use token for authenticated requests
# Get PLC status
curl -k https://localhost:8443/api/status \
-H "Authorization: Bearer $TOKEN"
# Start PLC
curl -k https://localhost:8443/api/start-plc \
-H "Authorization: Bearer $TOKEN"
# Stop PLC
curl -k https://localhost:8443/api/stop-plc \
-H "Authorization: Bearer $TOKEN"
# Upload program
curl -k -X POST https://localhost:8443/api/upload-file \
-H "Authorization: Bearer $TOKEN" \
-F "file=@program.zip"
# Get compilation status
curl -k https://localhost:8443/api/compilation-status \
-H "Authorization: Bearer $TOKEN"
# Get runtime logs
curl -k https://localhost:8443/api/runtime-logs \
-H "Authorization: Bearer $TOKEN"
# Ping runtime
curl -k https://localhost:8443/api/ping \
-H "Authorization: Bearer $TOKEN"Note: The -k flag bypasses certificate verification for self-signed certificates.
Python Example:
import requests
import urllib3
import time
# Disable SSL warnings for self-signed certificates
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
base_url = "https://localhost:8443/api"
# Step 1: Create first user (no auth required)
response = requests.post(
f"{base_url}/create-user",
json={"username": "admin", "password": "admin123", "role": "admin"},
verify=False
)
print(f"User creation: {response.json()}")
# Step 2: Login to get JWT token
response = requests.post(
f"{base_url}/login",
json={"username": "admin", "password": "admin123"},
verify=False
)
token = response.json()["access_token"]
print(f"Received token: {token[:50]}...")
# Step 3: Use token for authenticated requests
headers = {"Authorization": f"Bearer {token}"}
# Get PLC status
response = requests.get(f"{base_url}/status", headers=headers, verify=False)
print(f"PLC Status: {response.json()}")
# Start PLC
response = requests.get(f"{base_url}/start-plc", headers=headers, verify=False)
print(f"Start PLC: {response.json()}")
# Upload program
with open("program.zip", "rb") as f:
files = {"file": f}
response = requests.post(
f"{base_url}/upload-file",
files=files,
headers=headers,
verify=False
)
print(f"Upload: {response.json()}")
# Monitor compilation
while True:
response = requests.get(
f"{base_url}/compilation-status",
headers=headers,
verify=False
)
status = response.json()
print(f"Compilation Status: {status['status']}")
if status["status"] in ["SUCCESS", "FAILED"]:
print("Compilation Logs:")
for log in status["logs"]:
print(f" {log}")
break
time.sleep(1)JavaScript/Node.js Example:
const https = require('https');
const fetch = require('node-fetch');
const FormData = require('form-data');
const fs = require('fs');
// Disable SSL verification for self-signed certificates
const agent = new https.Agent({ rejectUnauthorized: false });
const baseUrl = "https://localhost:8443/api";
async function main() {
// Step 1: Create first user
let response = await fetch(`${baseUrl}/create-user`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: 'admin123',
role: 'admin'
}),
agent
});
console.log('User creation:', await response.json());
// Step 2: Login to get JWT token
response = await fetch(`${baseUrl}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: 'admin123'
}),
agent
});
const { access_token } = await response.json();
console.log('Received token:', access_token.substring(0, 50) + '...');
// Step 3: Use token for authenticated requests
const headers = { 'Authorization': `Bearer ${access_token}` };
// Get PLC status
response = await fetch(`${baseUrl}/status`, { headers, agent });
console.log('PLC Status:', await response.json());
// Start PLC
response = await fetch(`${baseUrl}/start-plc`, { headers, agent });
console.log('Start PLC:', await response.json());
// Upload program
const formData = new FormData();
formData.append('file', fs.createReadStream('program.zip'));
response = await fetch(`${baseUrl}/upload-file`, {
method: 'POST',
headers: { ...headers },
body: formData,
agent
});
console.log('Upload:', await response.json());
// Monitor compilation
while (true) {
response = await fetch(`${baseUrl}/compilation-status`, { headers, agent });
const status = await response.json();
console.log('Compilation Status:', status.status);
if (status.status === 'SUCCESS' || status.status === 'FAILED') {
console.log('Compilation Logs:');
status.logs.forEach(log => console.log(' ' + log));
break;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
main();Currently, there are no rate limits enforced. However, be mindful of:
- Only one compilation can run at a time
- Frequent start/stop commands may cause state transition issues
- Log queries can be resource-intensive with large log volumes
- HTTPS Only: All communication must use HTTPS (port 8443)
- JWT Authentication: All endpoints except first user creation and login require JWT authentication
- Self-Signed Certificates: Default installation uses self-signed certificates (OpenPLC Editor handles this automatically)
- File Upload Validation: ZIP files undergo comprehensive security checks before extraction
- Size Limits: Files are limited to prevent resource exhaustion (10 MB per file, 50 MB total)
- Path Validation: All file paths are validated to prevent traversal attacks
- Password Security: Passwords are hashed with PBKDF2-SHA256 (600,000 iterations), salt, and pepper
For real-time debugging and variable inspection, the OpenPLC Editor uses a WebSocket interface at https://<host>:8443/api/debug. See Debug Protocol for details.
- Editor Integration - How OpenPLC Editor uses this API
- Architecture - System overview
- Debug Protocol - WebSocket debug interface
- Compilation Flow - Build process details
- Security - Security features
- Troubleshooting - Common issues