Skip to main content
Version: 1.0

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​

PropertyTypeDescription

endpoint_name

string

The name of the endpoint to register. This becomes part of the URL path.

callback

function

Function called when a request arrives at the endpoint

PropertyTypeDescription

request

object

Request object containing HTTP request data

PropertyTypeDescription

method

string

HTTP method (GET, POST, PUT, DELETE, etc.)

query

string

Query string after the ? in the URL (optional)

headers

array

Request headers as array of [name, value] pairs

body

string

Request body for POST/PUT requests (optional)

response

object

Response object for sending data back to client

PropertyTypeDescription

code

number

HTTP status code (default: 200)

body

string

Response body content

headers

array

Response headers as array of [name, value] pairs

send

function

Method to send the response. Returns true on success.

userdata

any

The userdata passed during registration

userdata

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-Length and Connection: close headers are automatically added
  • These headers cannot be overridden by the script
  • Content-Type defaults to text/plain if 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​

  1. Status API - Expose device status as JSON
  2. Control API - Accept commands via HTTP
  3. Webhook Receiver - Accept notifications from external services
  4. Data Logger - Collect and serve logged data
  5. Configuration API - Remote configuration interface
  6. Bridge/Proxy - Forward requests to other services
  7. Custom Dashboard - Serve simple HTML interfaces