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