The HTTP Requests tool turns your dashboard into a built-in API client — like a mini Postman that lives inside Virtuino Cloud. You can call any public REST API, test it live, and then have the server run it automatically (on a schedule or when a field changes) and feed the response straight into your fields and scripts.
https://httpbin.org — a request inspector that echoes back exactly what it received (great for verifying params, auth, headers and body).https://api.open-meteo.com — a free weather API (no key required) used for the two complete real-world examples.Every request you build has the same top section, regardless of the tabs you fill in:
| Field | What it does |
|---|---|
| title | A friendly name shown in the list (e.g. Athens weather). Has no effect on the call. |
| method | The HTTP verb: GET POST PUT PATCH DELETE. GET retrieves data; POST/PUT/PATCH send a body; DELETE removes. |
| url | The full endpoint, starting with http:// or https://. |
| Test | Runs the request immediately through the server and shows the live response below — without saving. |
Below the URL there are four tabs that shape the request: Params, Authorization, Headers and Body. The second top-level tab, automation, is covered in section 7.
Query parameters are the ?key=value&key2=value2 part of a URL. Instead of typing them by hand, add them as rows here — each enabled row is appended to the URL, correctly URL-encoded.
| Column | Meaning |
|---|---|
| (checkbox) | Include this row in the request. Uncheck to keep a param without sending it. |
| Key | Parameter name (e.g. latitude). |
| Value | Parameter value (e.g. 37.98). |
GET https://httpbin.org/get
Params:
| Key | Value |
|---|---|
| city | Athens |
| units | metric |
Press Test. httpbin echoes the parameters it received under args:
{
"args": {
"city": "Athens",
"units": "metric"
},
"url": "https://httpbin.org/get?city=Athens&units=metric"
}
args match what you typed, the Params tab is working. The final url shows exactly what was called.Many APIs require credentials. Choose the type and the tool builds the correct header for you. The credentials are stored with the request and sent on every (manual or automatic) run.
The default. Nothing is added. Use it for open APIs like Open-Meteo.
Adds the header Authorization: Bearer <token>. This is the most common scheme for modern APIs (OAuth2, personal access tokens, etc.).
GET https://httpbin.org/bearer
Authorization: Bearer Token → token = my-secret-123
{
"authenticated": true,
"token": "my-secret-123"
}
Adds Authorization: Basic <base64(username:password)>. The tool does the Base64 encoding for you.
GET https://httpbin.org/basic-auth/admin/s3cret
Authorization: Basic Auth → username = admin, password = s3cret
{
"authenticated": true,
"user": "admin"
}
/basic-auth/admin/s3cret tells httpbin which credentials to accept. If you type the wrong username/password in the Auth tab, you get 401 Unauthorized — try it to see the failure case.Sends a custom header of your choice, e.g. X-API-Key: <value>. Set the header name and the value. Many weather/finance APIs use this style.
GET https://httpbin.org/headers
Authorization: API Key → header name = X-API-Key, value = abc123
{
"headers": {
"Host": "httpbin.org",
"X-Api-Key": "abc123"
}
}
?apikey=abc123). In that case leave Authorization on No Auth and add the key in the Params tab instead.Custom request headers, added as key/value rows (same layout as Params). Use them for content negotiation, custom tokens, or anything the API documents. Common examples:
| Header | Typical use |
|---|---|
Accept: application/json | Ask the server to return JSON. |
User-Agent: VirtuinoCloud | Identify your client. |
X-Custom-Token: ... | Any vendor-specific header. |
GET https://httpbin.org/headers
Headers:
| Key | Value |
|---|---|
| Accept | application/json |
| X-Demo | virtuino |
{
"headers": {
"Accept": "application/json",
"X-Demo": "virtuino",
"Host": "httpbin.org"
}
}
Authorization header. Anything you can do there you could also type manually in Headers — but the Auth tab is safer and clearer.The body is the payload you send to the server. It only applies to POST, PUT and PATCH — for GET/DELETE it is ignored. Choose one of four modes:
| Mode | What is sent | Content-Type set |
|---|---|---|
| none | No body. | — |
| JSON | A raw JSON document you type. | application/json |
| form-urlencoded | Key/value pairs encoded as a=1&b=2. | application/x-www-form-urlencoded |
| raw | Any plain text (XML, CSV, etc.). | none (set your own in Headers) |
POST https://httpbin.org/post
Body → JSON:
{
"device": "pump-1",
"command": "on",
"value": 42
}
httpbin returns what it parsed under json:
{
"json": { "device": "pump-1", "command": "on", "value": 42 },
"headers": { "Content-Type": "application/json" }
}
POST https://httpbin.org/post
Body → form-urlencoded:
| Key | Value |
|---|---|
| relay | 1 |
| duration | 30 |
{
"form": { "relay": "1", "duration": "30" }
}
The Test button sends the request immediately through the Virtuino server (never from your browser — that avoids CORS problems and applies our security checks). The results panel shows:
Switch to the automation tab to make the request run by itself on the server. There are three independent cards — enable any combination with the checkbox at the top of each.
Runs the request on a fixed schedule. Set the interval in minutes (1–1440, i.e. up to 24 hours). The server executes the request every interval and — if Store output is on — saves the response to your field.
Runs the request when one of your fields meets a condition. Pick a device + field, a condition, and a value:
| Condition | Fires when the field value is… |
|---|---|
| greater than > | strictly above the value |
| less than < | strictly below the value |
| greater or equal ≥ | at or above the value |
| less or equal ≤ | at or below the value |
| equal == | exactly equal |
| not equal != | anything except the value |
| changed | any time the value actually changes (the value box is ignored) |
changed fires on each genuine change of value.Pick a device + field where the raw response is written after each run (polling or trigger). The value flows through the same engine as everything else, which means:
Goal: every minute, fetch the current temperature for Athens and store it in a numeric field.
GET https://api.open-meteo.com/v1/forecast?latitude=37.98&longitude=23.73¤t=temperature_2m
(Equivalently, put the URL up to /forecast and add latitude, longitude, current in the Params tab.) Press Test — you should get:
{
"latitude": 37.98,
"longitude": 23.72,
"current": {
"time": "2026-06-20T10:00",
"interval": 900,
"temperature_2m": 27.8
}
}
1 minute.Create an Automation Script with two variables: in1_variable (IN, mapped to weather_raw, marked as trigger) and out1_variable (OUT, mapped to your temperature field).
// Title: Open-Meteo Temperature Extractor
// in1_variable -> IN : the field holding the raw JSON (trigger)
// out1_variable -> OUT : the destination temperature field
const raw = (inputs.in1_variable && inputs.in1_variable.value) ? inputs.in1_variable.value : "";
if (typeof raw !== "string" || raw.trim() === "") {
console.log("in1_variable empty - run the request first.");
} else {
try {
const data = JSON.parse(raw);
const t = (data && data.current) ? data.current.temperature_2m : undefined;
if (t !== undefined && t !== null) {
outputs.out1_variable = t;
console.log("Temperature -> " + t);
} else {
console.log("temperature_2m not found.");
}
} catch (e) {
console.log("Parse error: " + e.message);
}
}
That's the whole pipeline: request → weather_raw → script → temperature field, refreshing every minute.
Goal: pull temperature, humidity and wind in one call and split them into three fields.
GET https://api.open-meteo.com/v1/forecast?latitude=37.98&longitude=23.73¤t=temperature_2m,relative_humidity_2m,wind_speed_10m
{
"current": {
"temperature_2m": 27.8,
"relative_humidity_2m": 41,
"wind_speed_10m": 12.6
}
}
10 minutes.Variables: in1_variable (IN, weather_raw, trigger), out1_variable/out2_variable/out3_variable (OUT, mapped to temperature / humidity / wind fields).
// Title: Open-Meteo Weather Parser (multi-value)
// in1_variable -> IN : raw JSON field (trigger)
// out1_variable -> OUT : temperature
// out2_variable -> OUT : humidity
// out3_variable -> OUT : wind speed
const raw = (inputs.in1_variable && inputs.in1_variable.value) ? inputs.in1_variable.value : "";
if (typeof raw !== "string" || raw.trim() === "") {
console.log("in1_variable is empty - run the request first.");
} else {
try {
const data = JSON.parse(raw);
const cur = (data && data.current) ? data.current : null;
if (!cur) {
console.log("No 'current' object in response.");
} else {
if (cur.temperature_2m !== undefined && cur.temperature_2m !== null) outputs.out1_variable = cur.temperature_2m;
if (cur.relative_humidity_2m !== undefined && cur.relative_humidity_2m !== null) outputs.out2_variable = cur.relative_humidity_2m;
if (cur.wind_speed_10m !== undefined && cur.wind_speed_10m !== null) outputs.out3_variable = cur.wind_speed_10m;
console.log("temp: " + cur.temperature_2m + " | hum: " + cur.relative_humidity_2m + " | wind: " + cur.wind_speed_10m);
}
} catch (e) {
console.log("JSON parse error: " + e.message);
}
}
return. Scripts run in a sandboxed isolate that evaluates your code directly, so a bare return; at the outermost level is a syntax error. Wrap your logic in if/else or try/catch as shown above. Inside a function, return is fine.
| Limit | Value | Why |
|---|---|---|
| Request timeout | 10 seconds | A slow/hanging endpoint is aborted so it can't block the server. |
| Polling interval | 1 minute min · 1440 (24 h) max | You choose the cadence per request, within these bounds. |
| Trigger cooldown | once per 10 s per request | If the trigger field oscillates or changes many times per second, the request still fires at most once every 10 s — it can never flood the external API. |
| Stage | Limit | What happens if exceeded |
|---|---|---|
| Response download | 1 MB | The request is rejected — request only the data you need from the API. |
| Stored to a standard field | 1 MB | Matches the download cap, so any response that downloaded also fits the field. |
| Stored to a historical field | 512 characters | Anything longer is rejected — see the warning below. |
| Saved in the execution logs | 4000 characters | The stored response is truncated (the live response itself is not). |
http/https URLs are allowed, and the hostname is DNS-resolved and checked — requests to private/loopback ranges (127.x, 10.x, 192.168.x, 169.254.x, 172.16–31.x, ::1) are rejected. You cannot use this tool to reach internal infrastructure.polling enabled · trigger enabled · stores output to a field.
Check that (1) the relevant card's checkbox in the automation tab is on and the request is saved; (2) for Store output, a device and field are selected; (3) for triggers, the field really updates and the condition is met.
Make sure the script's input variable is mapped to the same field chosen in Store output, and that the mapping is marked as the trigger. Confirm the script is Active and Ready.
The output field doesn't yet hold valid JSON. Run the request once (Test or wait for a poll) so the field is populated, then the script will parse it on the next update.
Leave Authorization on No Auth and add the key as a row in the Params tab (e.g. apikey = ...).
Virtuino Cloud — HTTP Requests Tool