curl --request POST \
--url https://api.erdo.ai/v1/pages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"clone_from_page_id": "<string>",
"css": "<string>",
"dataset_slugs": [
"<string>"
],
"html": "<string>",
"js": "<string>",
"kv_slugs": [
"<string>"
],
"meta_description": "<string>",
"meta_icon_asset_id": "<string>",
"meta_image_asset_id": "<string>",
"meta_title": "<string>",
"public": true,
"runtime": "<string>",
"title": "<string>",
"writable_dataset_slugs": [
"<string>"
],
"writable_kv_slugs": [
"<string>"
]
}
'import requests
url = "https://api.erdo.ai/v1/pages"
payload = {
"clone_from_page_id": "<string>",
"css": "<string>",
"dataset_slugs": ["<string>"],
"html": "<string>",
"js": "<string>",
"kv_slugs": ["<string>"],
"meta_description": "<string>",
"meta_icon_asset_id": "<string>",
"meta_image_asset_id": "<string>",
"meta_title": "<string>",
"public": True,
"runtime": "<string>",
"title": "<string>",
"writable_dataset_slugs": ["<string>"],
"writable_kv_slugs": ["<string>"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
clone_from_page_id: '<string>',
css: '<string>',
dataset_slugs: ['<string>'],
html: '<string>',
js: '<string>',
kv_slugs: ['<string>'],
meta_description: '<string>',
meta_icon_asset_id: '<string>',
meta_image_asset_id: '<string>',
meta_title: '<string>',
public: true,
runtime: '<string>',
title: '<string>',
writable_dataset_slugs: ['<string>'],
writable_kv_slugs: ['<string>']
})
};
fetch('https://api.erdo.ai/v1/pages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.erdo.ai/v1/pages",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'clone_from_page_id' => '<string>',
'css' => '<string>',
'dataset_slugs' => [
'<string>'
],
'html' => '<string>',
'js' => '<string>',
'kv_slugs' => [
'<string>'
],
'meta_description' => '<string>',
'meta_icon_asset_id' => '<string>',
'meta_image_asset_id' => '<string>',
'meta_title' => '<string>',
'public' => true,
'runtime' => '<string>',
'title' => '<string>',
'writable_dataset_slugs' => [
'<string>'
],
'writable_kv_slugs' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.erdo.ai/v1/pages"
payload := strings.NewReader("{\n \"clone_from_page_id\": \"<string>\",\n \"css\": \"<string>\",\n \"dataset_slugs\": [\n \"<string>\"\n ],\n \"html\": \"<string>\",\n \"js\": \"<string>\",\n \"kv_slugs\": [\n \"<string>\"\n ],\n \"meta_description\": \"<string>\",\n \"meta_icon_asset_id\": \"<string>\",\n \"meta_image_asset_id\": \"<string>\",\n \"meta_title\": \"<string>\",\n \"public\": true,\n \"runtime\": \"<string>\",\n \"title\": \"<string>\",\n \"writable_dataset_slugs\": [\n \"<string>\"\n ],\n \"writable_kv_slugs\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.erdo.ai/v1/pages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"clone_from_page_id\": \"<string>\",\n \"css\": \"<string>\",\n \"dataset_slugs\": [\n \"<string>\"\n ],\n \"html\": \"<string>\",\n \"js\": \"<string>\",\n \"kv_slugs\": [\n \"<string>\"\n ],\n \"meta_description\": \"<string>\",\n \"meta_icon_asset_id\": \"<string>\",\n \"meta_image_asset_id\": \"<string>\",\n \"meta_title\": \"<string>\",\n \"public\": true,\n \"runtime\": \"<string>\",\n \"title\": \"<string>\",\n \"writable_dataset_slugs\": [\n \"<string>\"\n ],\n \"writable_kv_slugs\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/pages")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"clone_from_page_id\": \"<string>\",\n \"css\": \"<string>\",\n \"dataset_slugs\": [\n \"<string>\"\n ],\n \"html\": \"<string>\",\n \"js\": \"<string>\",\n \"kv_slugs\": [\n \"<string>\"\n ],\n \"meta_description\": \"<string>\",\n \"meta_icon_asset_id\": \"<string>\",\n \"meta_image_asset_id\": \"<string>\",\n \"meta_title\": \"<string>\",\n \"public\": true,\n \"runtime\": \"<string>\",\n \"title\": \"<string>\",\n \"writable_dataset_slugs\": [\n \"<string>\"\n ],\n \"writable_kv_slugs\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"cloned_pipelines": [
{
"id": "<string>",
"purpose": "<string>",
"slug": "<string>",
"source_id": "<string>"
}
],
"id": "<string>",
"meta_description": "<string>",
"meta_icon_asset_id": "<string>",
"meta_image_asset_id": "<string>",
"meta_title": "<string>",
"notes": [
"<string>"
],
"public": true,
"public_url": "<string>",
"review": {
"issues": [
{
"category": "<string>",
"description": "<string>",
"id": "<string>",
"judge_slug": "<string>",
"severity": "<string>",
"suggestion": "<string>"
}
],
"judges": [
"<string>"
],
"lighthouse": {
"accessibility": 123,
"best_practices": 123,
"fetched_at": "2023-11-07T05:31:56Z",
"performance": 123,
"seo": 123
},
"reason": "<string>",
"reviewed": true,
"summary": "<string>"
},
"rewrites": {},
"source_page_id": "<string>",
"thread_id": "<string>",
"title": "<string>",
"url": "<string>",
"validation": {
"errors": [
{
"column": 123,
"context": "<string>",
"field": "<string>",
"line": 123,
"message": "<string>",
"severity": "<string>"
}
],
"valid": true
},
"warning": "<string>"
}{
"code": "not_found",
"details": {},
"message": "<string>"
}DeployPageAPI mirrors the erdo_deploy_page MCP tool. clone_from_page_id in
the body switches it into clone mode — a new page copied server-side from an existing one, instead of content supplied here.
curl --request POST \
--url https://api.erdo.ai/v1/pages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"clone_from_page_id": "<string>",
"css": "<string>",
"dataset_slugs": [
"<string>"
],
"html": "<string>",
"js": "<string>",
"kv_slugs": [
"<string>"
],
"meta_description": "<string>",
"meta_icon_asset_id": "<string>",
"meta_image_asset_id": "<string>",
"meta_title": "<string>",
"public": true,
"runtime": "<string>",
"title": "<string>",
"writable_dataset_slugs": [
"<string>"
],
"writable_kv_slugs": [
"<string>"
]
}
'import requests
url = "https://api.erdo.ai/v1/pages"
payload = {
"clone_from_page_id": "<string>",
"css": "<string>",
"dataset_slugs": ["<string>"],
"html": "<string>",
"js": "<string>",
"kv_slugs": ["<string>"],
"meta_description": "<string>",
"meta_icon_asset_id": "<string>",
"meta_image_asset_id": "<string>",
"meta_title": "<string>",
"public": True,
"runtime": "<string>",
"title": "<string>",
"writable_dataset_slugs": ["<string>"],
"writable_kv_slugs": ["<string>"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
clone_from_page_id: '<string>',
css: '<string>',
dataset_slugs: ['<string>'],
html: '<string>',
js: '<string>',
kv_slugs: ['<string>'],
meta_description: '<string>',
meta_icon_asset_id: '<string>',
meta_image_asset_id: '<string>',
meta_title: '<string>',
public: true,
runtime: '<string>',
title: '<string>',
writable_dataset_slugs: ['<string>'],
writable_kv_slugs: ['<string>']
})
};
fetch('https://api.erdo.ai/v1/pages', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.erdo.ai/v1/pages",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'clone_from_page_id' => '<string>',
'css' => '<string>',
'dataset_slugs' => [
'<string>'
],
'html' => '<string>',
'js' => '<string>',
'kv_slugs' => [
'<string>'
],
'meta_description' => '<string>',
'meta_icon_asset_id' => '<string>',
'meta_image_asset_id' => '<string>',
'meta_title' => '<string>',
'public' => true,
'runtime' => '<string>',
'title' => '<string>',
'writable_dataset_slugs' => [
'<string>'
],
'writable_kv_slugs' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.erdo.ai/v1/pages"
payload := strings.NewReader("{\n \"clone_from_page_id\": \"<string>\",\n \"css\": \"<string>\",\n \"dataset_slugs\": [\n \"<string>\"\n ],\n \"html\": \"<string>\",\n \"js\": \"<string>\",\n \"kv_slugs\": [\n \"<string>\"\n ],\n \"meta_description\": \"<string>\",\n \"meta_icon_asset_id\": \"<string>\",\n \"meta_image_asset_id\": \"<string>\",\n \"meta_title\": \"<string>\",\n \"public\": true,\n \"runtime\": \"<string>\",\n \"title\": \"<string>\",\n \"writable_dataset_slugs\": [\n \"<string>\"\n ],\n \"writable_kv_slugs\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.erdo.ai/v1/pages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"clone_from_page_id\": \"<string>\",\n \"css\": \"<string>\",\n \"dataset_slugs\": [\n \"<string>\"\n ],\n \"html\": \"<string>\",\n \"js\": \"<string>\",\n \"kv_slugs\": [\n \"<string>\"\n ],\n \"meta_description\": \"<string>\",\n \"meta_icon_asset_id\": \"<string>\",\n \"meta_image_asset_id\": \"<string>\",\n \"meta_title\": \"<string>\",\n \"public\": true,\n \"runtime\": \"<string>\",\n \"title\": \"<string>\",\n \"writable_dataset_slugs\": [\n \"<string>\"\n ],\n \"writable_kv_slugs\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/pages")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"clone_from_page_id\": \"<string>\",\n \"css\": \"<string>\",\n \"dataset_slugs\": [\n \"<string>\"\n ],\n \"html\": \"<string>\",\n \"js\": \"<string>\",\n \"kv_slugs\": [\n \"<string>\"\n ],\n \"meta_description\": \"<string>\",\n \"meta_icon_asset_id\": \"<string>\",\n \"meta_image_asset_id\": \"<string>\",\n \"meta_title\": \"<string>\",\n \"public\": true,\n \"runtime\": \"<string>\",\n \"title\": \"<string>\",\n \"writable_dataset_slugs\": [\n \"<string>\"\n ],\n \"writable_kv_slugs\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"cloned_pipelines": [
{
"id": "<string>",
"purpose": "<string>",
"slug": "<string>",
"source_id": "<string>"
}
],
"id": "<string>",
"meta_description": "<string>",
"meta_icon_asset_id": "<string>",
"meta_image_asset_id": "<string>",
"meta_title": "<string>",
"notes": [
"<string>"
],
"public": true,
"public_url": "<string>",
"review": {
"issues": [
{
"category": "<string>",
"description": "<string>",
"id": "<string>",
"judge_slug": "<string>",
"severity": "<string>",
"suggestion": "<string>"
}
],
"judges": [
"<string>"
],
"lighthouse": {
"accessibility": 123,
"best_practices": 123,
"fetched_at": "2023-11-07T05:31:56Z",
"performance": 123,
"seo": 123
},
"reason": "<string>",
"reviewed": true,
"summary": "<string>"
},
"rewrites": {},
"source_page_id": "<string>",
"thread_id": "<string>",
"title": "<string>",
"url": "<string>",
"validation": {
"errors": [
{
"column": 123,
"context": "<string>",
"field": "<string>",
"line": 123,
"message": "<string>",
"severity": "<string>"
}
],
"valid": true
},
"warning": "<string>"
}{
"code": "not_found",
"details": {},
"message": "<string>"
}Authorizations
An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).
Body
byte-identical server-side copy of the named page rather than content the caller supplies. It is a parameter here, not a separate endpoint/tool, for the same reason a WHERE-clause variant is a parameter: cloning is deploying a page whose content comes from another page.
knowledge attachment ids (from the org's Brand Style record) the public shell renders as the browser-tab icon and the share-card image. The page itself is an iframe, so this is the only way its tab/preview can carry the customer's brand. Optional; unset falls back to Erdo's icon and card.
title + description shown when the page is shared or crawled) — distinct from Title, the internal library name. Mirrors the create_html_artifact convention in backend/agent/agents/helpers.go.
required — in clone mode both must be absent. Content mode still requires them; deployPageLogic enforces it with a clear error.
named-KV-store access that erdo.insertRows and erdo.kv.set need. Without them those writes 403 ("this page has no write access"). dataset_slugs is read-only; these are the write side.
Response
Success response
Show child attributes
Show child attributes
ids (favicon / share image), nil when unset.
(nil when unset), so a caller can confirm what it just deployed/updated without a separate read.
request_review. Structured rather than prose so the tool result can carry it without a second formatting layer inventing its own vocabulary.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
clone_from_page_id deploy: the page the copy was made from, the lead-capture pipelines duplicated onto it, every id substitution applied to the copied content, and anything the caller still has to act on.
share link and is only present while the page is public. Use these values verbatim — hosts differ across prod/staging/local.
action callers can reference it without importing the validator implementation.
Show child attributes
Show child attributes
successful content deploy/update). The page exists and is usable; the calling agent should surface this to the user and retry the failing op.

