HTTPServer
The HTTPServer API allows scripts to register HTTP endpoints on the device, effectively turning your Shelly into a mini web server. This enables custom integrations, webhooks, and REST APIs directly on the device.
Overview
HTTP endpoints created by scripts are accessible at:
http://<device_ip>/script/<script_id>/<endpoint_name>
Where:
<device_ip>is your Shelly device's IP address<script_id>is the numeric ID of the script<endpoint_name>is the name you register
Quick Example
// Register a simple status endpoint
HTTPServer.registerEndpoint("status", function(request, response) {
// Set response body
response.body = JSON.stringify({
message: "Device is online",
timestamp: Date.now(),
uptime: Shelly.getUptimeMs()
});
// Set response headers
response.headers = [
["Content-Type", "application/json"],
["X-Device-Id", Shelly.getDeviceInfo().id]
];
// Set HTTP status code
response.code = 200;
// Send the response
response.send();
});
console.log("Endpoint registered at /script/<id>/status");
API Reference
HTTPServer Global Object
The HTTPServer is a global object available in all scripts that provides HTTP server functionality.
HTTPServer.registerEndpoint()
Registers an HTTP endpoint with a handler function.
HTTPServer.registerEndpoint(endpoint_name, callback[, userdata]) -> string
Parameters
| Property | Type | Description | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| string | The name of the endpoint to register. This becomes part of the URL path. | ||||||||||||||||||||||||||||||||||||||||||
| function | Function called when a request arrives at the endpoint
| ||||||||||||||||||||||||||||||||||||||||||
| any | Optional data passed to the callback |
Return Value
Returns the full endpoint path (e.g., /script/1/status) on success. The script is aborted if arguments are invalid.
Complete Examples
Basic GET Endpoint
// Simple GET endpoint that returns device information
HTTPServer.registerEndpoint("info", function(request, response) {
if (request.method !== "GET") {
response.code = 405; // Method Not Allowed
response.body = "Only GET method is allowed";
response.send();
return;
}
let deviceInfo = Shelly.getDeviceInfo();
let status = Shelly.getComponentStatus("sys");
response.body = JSON.stringify({
device: {
id: deviceInfo.id,
model: deviceInfo.model,
fw_version: deviceInfo.fw_id
},
system: {
uptime: status.uptime,
ram_free: status.ram_free,
ram_size: status.ram_size
}
});
response.headers = [["Content-Type", "application/json"]];
response.code = 200;
response.send();
});
POST Endpoint with JSON
// POST endpoint that accepts JSON data
HTTPServer.registerEndpoint("control", function(request, response) {
if (request.method !== "POST") {
response.code = 405;
response.body = JSON.stringify({error: "Only POST allowed"});
response.headers = [["Content-Type", "application/json"]];
response.send();
return;
}
// Parse JSON body
let data;
try {
data = JSON.parse(request.body);
} catch(e) {
response.code = 400;
response.body = JSON.stringify({error: "Invalid JSON"});
response.headers = [["Content-Type", "application/json"]];
response.send();
return;
}
// Process the data
if (data.action === "toggle") {
Shelly.call("Switch.Toggle", {id: 0}, function(result) {
response.body = JSON.stringify({
success: true,
switch_state: result.was_on ? "off" : "on"
});
response.headers = [["Content-Type", "application/json"]];
response.code = 200;
response.send();
});
} else {
response.code = 400;
response.body = JSON.stringify({error: "Unknown action"});
response.headers = [["Content-Type", "application/json"]];
response.send();
}
});
Query Parameters Handling
// Endpoint that handles query parameters
HTTPServer.registerEndpoint("query", function(request, response) {
let params = {};
// Parse query string
if (request.query) {
let pairs = request.query.split("&");
for (let i = 0; i < pairs.length; i++) {
let pair = pairs[i].split("=");
if (pair.length === 2) {
params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
}
}
// Use parameters
let action = params.action || "status";
let format = params.format || "json";
let result = {
action: action,
timestamp: Date.now()
};
if (format === "json") {
response.body = JSON.stringify(result);
response.headers = [["Content-Type", "application/json"]];
} else if (format === "text") {
response.body = "Action: " + action + "\nTime: " + result.timestamp;
response.headers = [["Content-Type", "text/plain"]];
} else {
response.code = 400;
response.body = "Unsupported format";
}
response.send();
});
// Access: /script/1/query?action=test&format=json
Authentication Check
// Endpoint with custom authentication
HTTPServer.registerEndpoint("secure", function(request, response) {
// Check for API key in headers
let authorized = false;
let apiKey = null;
for (let i = 0; i < request.headers.length; i++) {
if (request.headers[i][0].toLowerCase() === "x-api-key") {
apiKey = request.headers[i][1];
break;
}
}
// Validate API key (in real use, store this securely)
if (apiKey === "secret-key-123") {
authorized = true;
}
if (!authorized) {
response.code = 401;
response.body = JSON.stringify({error: "Unauthorized"});
response.headers = [
["Content-Type", "application/json"],
["WWW-Authenticate", "API-Key"]
];
response.send();
return;
}
// Authorized request
response.body = JSON.stringify({message: "Access granted"});
response.headers = [["Content-Type", "application/json"]];
response.code = 200;
response.send();
});
Webhook Receiver
// Webhook endpoint for external services
let webhookData = [];
HTTPServer.registerEndpoint("webhook", function(request, response) {
if (request.method === "POST") {
// Store webhook data
let data = {
timestamp: Date.now(),
headers: request.headers,
body: request.body
};
webhookData.push(data);
// Keep only last 10 entries
if (webhookData.length > 10) {
webhookData.shift();
}
console.log("Webhook received:", request.body);
response.code = 200;
response.body = JSON.stringify({status: "received"});
response.headers = [["Content-Type", "application/json"]];
response.send();
} else if (request.method === "GET") {
// Return stored webhooks
response.body = JSON.stringify(webhookData);
response.headers = [["Content-Type", "application/json"]];
response.code = 200;
response.send();
}
});
Important Limitations
Request Size Limit
The HTTP server cannot process requests larger than 3072 bytes (including request line, headers, and body). Larger requests will cause the connection to reset with no response.
Multipart Not Supported
POST and PUT requests with multipart/* content type are not supported. These will return HTTP status code 415 (Unsupported Media Type).
Response Headers
Content-LengthandConnection: closeheaders are automatically added- These headers cannot be overridden by the script
Content-Typedefaults totext/plainif not specified
Timeouts and Concurrency
- HTTP transactions timeout after 10 seconds
- Timeout results in automatic
504(Gateway Timeout) response - Maximum 5 concurrent transactions allowed
- Exceeding limit results in
503(Service Unavailable) response
Authentication
HTTP endpoints will require authentication if it's enabled on the device. The device's authentication settings apply to script endpoints.
Script Limits
- Maximum 5 HTTP endpoints per script
- Endpoints are removed when script stops
- Endpoint names must be unique within a script
Testing Endpoints
Using curl
# GET request
curl http://<device_ip>/script/1/status
# POST with JSON
curl -X POST http://<device_ip>/script/1/control \
-H "Content-Type: application/json" \
-d '{"action":"toggle"}'
# With authentication
curl http://admin:password@<device_ip>/script/1/secure
# With custom headers
curl http://<device_ip>/script/1/api \
-H "X-API-Key: secret-key-123"
Using JavaScript
// From another script or browser
fetch("http://<device_ip>/script/1/status")
.then(response => response.json())
.then(data => console.log(data));
Common Use Cases
- Status API - Expose device status as JSON
- Control API - Accept commands via HTTP
- Webhook Receiver - Accept notifications from external services
- Data Logger - Collect and serve logged data
- Configuration API - Remote configuration interface
- Bridge/Proxy - Forward requests to other services
- Custom Dashboard - Serve simple HTML interfaces