Automation
List automation projects
http://127.0.0.1:40325/automation/projectsEvery project saved in the launcher, with its blocks and run settings. These are the same projects the desktop Automation section edits.
Permanent JWT from Settings → Automation API.
200Project list.application/json
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
401Missing or invalid Bearer token.
/automation/projectsGET /automation/projects HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X GET 'http://127.0.0.1:40325/automation/projects' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects";
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.get("http://127.0.0.1:40325/automation/projects")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1:40325/automation/projects");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}[
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}
]Create project
http://127.0.0.1:40325/automation/projectsPermanent JWT from Settings → Automation API.
Uses Untitled when empty.
200Created project.application/json
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
401Missing or invalid Bearer token.
/automation/projectsPOST /automation/projects HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
{
"name": "text"
}curl -X POST 'http://127.0.0.1:40325/automation/projects' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"name":"text"}'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects"
headers = {
"Authorization": f"Bearer {token}",
}
payload = {
"name": "text",
}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects";
const payload = {
"name": "text"
};
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
console.log(response.status);
console.log(await response.text());package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects"
body := bytes.NewBufferString(`{
"name": "text"
}`)
req, err := http.NewRequest("POST", url, body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }, serde_json = "1"
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.post("http://127.0.0.1:40325/automation/projects")
.header("Authorization", format!("Bearer {token}"))
.json(&serde_json::json!({
"name": "text"
}))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:40325/automation/projects");
request.Content = new StringContent("""
{
"name": "text"
}
""", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
const std::string body = R"json({
"name": "text"
})json";
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size()));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}Get project
http://127.0.0.1:40325/automation/projects/{id}Permanent JWT from Settings → Automation API.
200Project details.application/json
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
401Missing or invalid Bearer token.
404Project not found.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000GET /automation/projects/123e4567-e89b-12d3-a456-426614174000 HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X GET 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000";
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.get("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}{
"error": "text"
}Replace project
http://127.0.0.1:40325/automation/projects/{id}Replaces the entire project. Send the object returned by GET. The path ID takes precedence over the body ID. A missing project returns 404; use POST /automation/projects to create one.
Permanent JWT from Settings → Automation API.
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
200Saved project.application/json
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
400Invalid project object.application/json
401Missing or invalid Bearer token.
404Project not found.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000PUT /automation/projects/123e4567-e89b-12d3-a456-426614174000 HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}curl -X PUT 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"id":"123e4567-e89b-12d3-a456-426614174000","name":"text","notes":"text","blocks":[{"id":"text","kind":"click","label":"text","params":{},"enabled":true,"x":1,"y":1,"on_done":"next","on_fail":null,"secrets":["text"]}],"run":{"threads":1,"loops":1,"hours":1,"start":"text","profiles":["text"]},"created_at":1,"updated_at":1}'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000"
headers = {
"Authorization": f"Bearer {token}",
}
payload = {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": True,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": None,
"secrets": [
"text",
],
},
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text",
],
},
"created_at": 1,
"updated_at": 1,
}
response = requests.put(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000";
const payload = {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
};
const response = await fetch(url, {
method: "PUT",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
console.log(response.status);
console.log(await response.text());package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000"
body := bytes.NewBufferString(`{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}`)
req, err := http.NewRequest("PUT", url, body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }, serde_json = "1"
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.put("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000")
.header("Authorization", format!("Bearer {token}"))
.json(&serde_json::json!({
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Put, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000");
request.Content = new StringContent("""
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}
""", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
const std::string body = R"json({
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
})json";
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size()));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}{
"error": "text"
}{
"error": "text"
}Delete a project
http://127.0.0.1:40325/automation/projects/{id}Permanent JWT from Settings → Automation API.
200Deleted.application/json
401Missing or invalid Bearer token.
404Project not found.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000DELETE /automation/projects/123e4567-e89b-12d3-a456-426614174000 HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X DELETE 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.delete(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000";
const response = await fetch(url, {
method: "DELETE",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000"
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.delete("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Delete, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"deleted": true,
"id": "123e4567-e89b-12d3-a456-426614174000"
}{
"error": "text"
}Duplicate project
http://127.0.0.1:40325/automation/projects/{id}/duplicatePermanent JWT from Settings → Automation API.
200Duplicated project.application/json
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
401Missing or invalid Bearer token.
404Project not found.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicatePOST /automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X POST 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.post(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate";
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate"
req, err := http.NewRequest("POST", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.post("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/duplicate");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}{
"error": "text"
}Export project bundle
http://127.0.0.1:40325/automation/projects/{id}/exportParameters the project marked secret are emptied on the way out, so a bundle can be handed on without taking a password with it. The import side asks for them again.
Permanent JWT from Settings → Automation API.
200Export bundle.application/json
A project on its way to another machine.
An import refuses a number it does not know.
Unix seconds.
projectobject · AutomationProjectoptionalShow propertiesHide properties
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
Module ids the steps need.
module_filesobject[]optionalThose modules' files, carried inside the bundle so an imported project can run where it lands. A module missing from disk at export time is left out here but still named in modules. On import, a module already installed under the same id is kept as it is rather than overwritten.
Show propertiesHide properties
Those modules' files, carried inside the bundle so an imported project can run where it lands. A module missing from disk at export time is left out here but still named in modules. On import, a module already installed under the same id is kept as it is rather than overwritten.
The .wasm, gzipped then base64.
needsobject[]optionalWhat export blanked, so an import can ask for exactly that.
Show propertiesHide properties
What export blanked, so an import can ask for exactly that.
401Missing or invalid Bearer token.
404Project not found.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000/exportGET /automation/projects/123e4567-e89b-12d3-a456-426614174000/export HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X GET 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export";
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.get("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/export");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
}{
"error": "text"
}Import project bundle
http://127.0.0.1:40325/automation/importThe bundle arrives as a new project with a new id.
Permanent JWT from Settings → Automation API.
An import refuses a number it does not know.
Unix seconds.
projectobject · AutomationProjectoptionalShow propertiesHide properties
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
Module ids the steps need.
module_filesobject[]optionalThose modules' files, carried inside the bundle so an imported project can run where it lands. A module missing from disk at export time is left out here but still named in modules. On import, a module already installed under the same id is kept as it is rather than overwritten.
Show propertiesHide properties
Those modules' files, carried inside the bundle so an imported project can run where it lands. A module missing from disk at export time is left out here but still named in modules. On import, a module already installed under the same id is kept as it is rather than overwritten.
The .wasm, gzipped then base64.
needsobject[]optionalWhat export blanked, so an import can ask for exactly that.
Show propertiesHide properties
What export blanked, so an import can ask for exactly that.
200Imported project.application/json
blocksobject · AutomationBlock[]optionalShow propertiesHide properties
A built-in palette kind (goto, click, type, profile.create, var.set, ...) or module:<module id>:<block>.
On a mobile profile, common actions use touch input: click taps, rightClick performs a long press, and scroll and swipe use touch gestures. hover is unavailable because mobile devices have no pointer. The following mobile-only steps are rejected on desktop profiles:
| kind | params | action |
| --- | --- | --- |
| touch.tap | selector or x+y, tapCount (1-3), timeout | Tap the target. |
| touch.longPress | selector or x+y, timeout | Press and hold. |
| touch.swipe | selector or x+y, dx, dy, timeout | Swipe. |
| touch.drag | selector, to, holdMs, timeout | Drag an element. |
| touch.pinch | scale, selector or x+y | Pinch to zoom. Values above 1 zoom out. |
| screen.rotate | angle (0/90/180/270), turnMs, into | Rotate the device. into stores the new screen.orientation.type. |
The step's own fields, as the palette defines them.
Canvas position. Layout only; the run follows the branches.
Where the run goes after a step. A plain string for the simple cases, an object for the ones carrying a value.
Defaults to stop; carrying on after a click that found nothing is how a run types into the wrong page.
Parameter names emptied on export.
runobject · AutomationRunSettingsoptionalShow propertiesHide properties
Browsers running at once; clamped to 1-64.
Passes over the blocks. 0 means run until hours is up.
Only meaningful when loops is 0.
Block id to start at; empty means the first.
Kept for projects saved before profiles became blocks; one thread each when set.
Unix seconds.
Unix seconds.
400Invalid bundle or unsupported format version.application/json
401Missing or invalid Bearer token.
/automation/importPOST /automation/import HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
{
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
}curl -X POST 'http://127.0.0.1:40325/automation/import' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"format":1,"exported_at":1,"project":{"id":"123e4567-e89b-12d3-a456-426614174000","name":"text","notes":"text","blocks":[{"id":"text","kind":"click","label":"text","params":{},"enabled":true,"x":1,"y":1,"on_done":"next","on_fail":null,"secrets":["text"]}],"run":{"threads":1,"loops":1,"hours":1,"start":"text","profiles":["text"]},"created_at":1,"updated_at":1},"modules":["text"],"module_files":[{"id":"text","wasm":"text"}],"needs":[{"block_id":"text","label":"text","params":["text"]}]}'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/import"
headers = {
"Authorization": f"Bearer {token}",
}
payload = {
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": True,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": None,
"secrets": [
"text",
],
},
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text",
],
},
"created_at": 1,
"updated_at": 1,
},
"modules": [
"text",
],
"module_files": [
{
"id": "text",
"wasm": "text",
},
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text",
],
},
],
}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/import";
const payload = {
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
};
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
console.log(response.status);
console.log(await response.text());package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/import"
body := bytes.NewBufferString(`{
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
}`)
req, err := http.NewRequest("POST", url, body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }, serde_json = "1"
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.post("http://127.0.0.1:40325/automation/import")
.header("Authorization", format!("Bearer {token}"))
.json(&serde_json::json!({
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
}))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:40325/automation/import");
request.Content = new StringContent("""
{
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
}
""", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
const std::string body = R"json({
"format": 1,
"exported_at": 1,
"project": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
},
"modules": [
"text"
],
"module_files": [
{
"id": "text",
"wasm": "text"
}
],
"needs": [
{
"block_id": "text",
"label": "text",
"params": [
"text"
]
}
]
})json";
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/import");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size()));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"notes": "text",
"blocks": [
{
"id": "text",
"kind": "click",
"label": "text",
"params": {},
"enabled": true,
"x": 1,
"y": 1,
"on_done": "next",
"on_fail": null,
"secrets": [
"text"
]
}
],
"run": {
"threads": 1,
"loops": 1,
"hours": 1,
"start": "text",
"profiles": [
"text"
]
},
"created_at": 1,
"updated_at": 1
}{
"error": "text"
}Start project
http://127.0.0.1:40325/automation/projects/{id}/runStarts the project and returns immediately. Project blocks start the required browsers. Use /status to monitor progress.
Returns 400 if the project is already running or has no enabled steps.
Permanent JWT from Settings → Automation API.
200Project started.application/json
400Project is already running or has no enabled steps.application/json
401Missing or invalid Bearer token.
404Project not found.application/json
501This build does not support automation.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000/runPOST /automation/projects/123e4567-e89b-12d3-a456-426614174000/run HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X POST 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.post(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run";
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run"
req, err := http.NewRequest("POST", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.post("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/run");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"project_id": "123e4567-e89b-12d3-a456-426614174000",
"running": true
}{
"error": "text"
}{
"error": "text"
}{
"error": "text"
}Stop project
http://127.0.0.1:40325/automation/projects/{id}/stopRequests that the run stop. Each browser finishes its current step before closing. Use /status to monitor the running field.
Permanent JWT from Settings → Automation API.
200Stop requested.application/json
401Missing or invalid Bearer token.
501This build does not support automation.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000/stopPOST /automation/projects/123e4567-e89b-12d3-a456-426614174000/stop HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X POST 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.post(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop";
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop"
req, err := http.NewRequest("POST", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.post("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/stop");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"project_id": "123e4567-e89b-12d3-a456-426614174000",
"stopping": true
}{
"error": "text"
}Get project run status
http://127.0.0.1:40325/automation/projects/{id}/statusReturns null if the project is not running and has not run since launcher startup. log contains the recent entries shown in the fleet window. The full log is stored under automation-runs/.
Permanent JWT from Settings → Automation API.
200Run status or `null`.application/json
401Missing or invalid Bearer token.
501This build does not support automation.application/json
/automation/projects/123e4567-e89b-12d3-a456-426614174000/statusGET /automation/projects/123e4567-e89b-12d3-a456-426614174000/status HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X GET 'http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status";
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.get("http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/projects/123e4567-e89b-12d3-a456-426614174000/status");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"project_id": "123e4567-e89b-12d3-a456-426614174000",
"project_name": "text",
"started_at": 1,
"running": true,
"workers": [
{
"profile_id": "text",
"profile_name": "text",
"pass": 1,
"step": 1,
"steps_total": 1,
"status": "queued",
"note": "text"
}
],
"log": [
"text"
]
}{
"error": "text"
}List active runs
http://127.0.0.1:40325/automation/runsPermanent JWT from Settings → Automation API.
200Active run list.application/json
Unix seconds.
workersobject · AutomationWorker[]optionalShow propertiesHide properties
1-based; 0 before the first pass starts.
queuedstartingrunningdonefailedstoppedThe tail the fleet window shows; the whole log is on disk under automation-runs/.
401Missing or invalid Bearer token.
501This build does not support automation.application/json
/automation/runsGET /automation/runs HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X GET 'http://127.0.0.1:40325/automation/runs' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/runs"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/runs";
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/runs"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.get("http://127.0.0.1:40325/automation/runs")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1:40325/automation/runs");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/runs");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}[
{
"project_id": "123e4567-e89b-12d3-a456-426614174000",
"project_name": "text",
"started_at": 1,
"running": true,
"workers": [
{
"profile_id": "text",
"profile_name": "text",
"pass": 1,
"step": 1,
"steps_total": 1,
"status": "queued",
"note": "text"
}
],
"log": [
"text"
]
}
]{
"error": "text"
}List installed modules
http://127.0.0.1:40325/automation/modulesReturns WebAssembly modules installed by the operator. Each module adds blocks with the kind module:<module id>:<block>. Modules return actions for the runner and do not access the page directly.
Permanent JWT from Settings → Automation API.
200Module list.application/json
File stem; also the prefix every one of its block kinds carries.
Block descriptors, in the same shape as the built-in palette.
Empty when the module loaded; otherwise why it did not.
401Missing or invalid Bearer token.
501This build does not support automation.application/json
/automation/modulesGET /automation/modules HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X GET 'http://127.0.0.1:40325/automation/modules' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/modules"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/modules";
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/modules"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.get("http://127.0.0.1:40325/automation/modules")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Get, "http://127.0.0.1:40325/automation/modules");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/modules");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}[
{
"id": "text",
"name": "text",
"path": "text",
"blocks": [
{}
],
"error": "text"
}
]{
"error": "text"
}Install module
http://127.0.0.1:40325/automation/modulespath must point to a local .wasm file on the launcher host. Modules contain native code created by the operator.
Permanent JWT from Settings → Automation API.
200The installed module.application/json
File stem; also the prefix every one of its block kinds carries.
Block descriptors, in the same shape as the built-in palette.
Empty when the module loaded; otherwise why it did not.
400Missing path or unsupported module file.application/json
401Missing or invalid Bearer token.
501This build does not support automation.application/json
/automation/modulesPOST /automation/modules HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
{
"path": "/home/me/module.wasm"
}curl -X POST 'http://127.0.0.1:40325/automation/modules' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"path":"/home/me/module.wasm"}'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/modules"
headers = {
"Authorization": f"Bearer {token}",
}
payload = {
"path": "/home/me/module.wasm",
}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/modules";
const payload = {
"path": "/home/me/module.wasm"
};
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
console.log(response.status);
console.log(await response.text());package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/modules"
body := bytes.NewBufferString(`{
"path": "/home/me/module.wasm"
}`)
req, err := http.NewRequest("POST", url, body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }, serde_json = "1"
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.post("http://127.0.0.1:40325/automation/modules")
.header("Authorization", format!("Bearer {token}"))
.json(&serde_json::json!({
"path": "/home/me/module.wasm"
}))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Post, "http://127.0.0.1:40325/automation/modules");
request.Content = new StringContent("""
{
"path": "/home/me/module.wasm"
}
""", Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
headers = curl_slist_append(headers, "Content-Type: application/json");
const std::string body = R"json({
"path": "/home/me/module.wasm"
})json";
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/modules");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size()));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"id": "text",
"name": "text",
"path": "text",
"blocks": [
{}
],
"error": "text"
}{
"error": "text"
}{
"error": "text"
}Remove module
http://127.0.0.1:40325/automation/modules/{id}Projects that use the module stop working. Existing project definitions are not modified.
Permanent JWT from Settings → Automation API.
200Removed.application/json
401Missing or invalid Bearer token.
404Module not found.application/json
501This build does not support automation.application/json
/automation/modules/textDELETE /automation/modules/text HTTP/1.1
Host: 127.0.0.1:40325
Authorization: Bearer YOUR_SECRET_TOKEN
Accept: */*curl -X DELETE 'http://127.0.0.1:40325/automation/modules/text' \
-H 'Authorization: Bearer YOUR_SECRET_TOKEN'import requests
# Permanent JWT from Settings → Automation API.
token = "YOUR_SECRET_TOKEN"
url = "http://127.0.0.1:40325/automation/modules/text"
headers = {
"Authorization": f"Bearer {token}",
}
response = requests.delete(url, headers=headers, timeout=30)
response.raise_for_status()
print(response.status_code)
print(response.text)// Permanent JWT from Settings → Automation API.
const token = "YOUR_SECRET_TOKEN";
const url = "http://127.0.0.1:40325/automation/modules/text";
const response = await fetch(url, {
method: "DELETE",
headers: {
"Authorization": `Bearer ${token}`,
},
});
console.log(response.status);
console.log(await response.text());package main
import (
"fmt"
"io"
"log"
"net/http"
)
func main() {
// Permanent JWT from Settings → Automation API.
token := "YOUR_SECRET_TOKEN"
url := "http://127.0.0.1:40325/automation/modules/text"
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(out))
}// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }, tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Permanent JWT from Settings → Automation API.
let token = "YOUR_SECRET_TOKEN";
let client = reqwest::Client::new();
let response = client
.delete("http://127.0.0.1:40325/automation/modules/text")
.header("Authorization", format!("Bearer {token}"))
.send()
.await?;
println!("{}", response.status());
println!("{}", response.text().await?);
Ok(())
}using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Permanent JWT from Settings → Automation API.
var token = "YOUR_SECRET_TOKEN";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var request = new HttpRequestMessage(HttpMethod.Delete, "http://127.0.0.1:40325/automation/modules/text");
var response = await client.SendAsync(request);
Console.WriteLine((int)response.StatusCode);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
}// build: c++ -std=c++17 main.cpp -lcurl
#include <curl/curl.h>
#include <iostream>
#include <string>
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
static_cast<std::string *>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
int main() {
// Permanent JWT from Settings → Automation API.
const std::string token = "YOUR_SECRET_TOKEN";
curl_global_init(CURL_GLOBAL_DEFAULT);
CURL *curl = curl_easy_init();
if (!curl) {
std::cerr << "curl_easy_init failed\n";
curl_global_cleanup();
return 1;
}
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, ("Authorization: Bearer " + token).c_str());
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:40325/automation/modules/text");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
const CURLcode rc = curl_easy_perform(curl);
if (rc != CURLE_OK) {
std::cerr << curl_easy_strerror(rc) << "\n";
} else {
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
std::cout << status << "\n" << response << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl_global_cleanup();
return rc == CURLE_OK ? 0 : 1;
}{
"deleted": true,
"id": "text"
}{
"error": "text"
}{
"error": "text"
}Last updated on