curl --request POST \
--url https://api.erstan.com/v1/public/authoring/agents/{id}/previews \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'If-Match: <if-match>' \
--data '
{
"skillVersions": [
{
"skillId": "<string>",
"version": 2
}
],
"laneId": "<string>",
"input": {},
"attachments": [
{
"name": "<string>",
"type": "<string>",
"base64": "<string>",
"storageKey": "<string>"
}
]
}
'import requests
url = "https://api.erstan.com/v1/public/authoring/agents/{id}/previews"
payload = {
"skillVersions": [
{
"skillId": "<string>",
"version": 2
}
],
"laneId": "<string>",
"input": {},
"attachments": [
{
"name": "<string>",
"type": "<string>",
"base64": "<string>",
"storageKey": "<string>"
}
]
}
headers = {
"If-Match": "<if-match>",
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'If-Match': '<if-match>',
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
skillVersions: [{skillId: '<string>', version: 2}],
laneId: '<string>',
input: {},
attachments: [
{name: '<string>', type: '<string>', base64: '<string>', storageKey: '<string>'}
]
})
};
fetch('https://api.erstan.com/v1/public/authoring/agents/{id}/previews', 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.erstan.com/v1/public/authoring/agents/{id}/previews",
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([
'skillVersions' => [
[
'skillId' => '<string>',
'version' => 2
]
],
'laneId' => '<string>',
'input' => [
],
'attachments' => [
[
'name' => '<string>',
'type' => '<string>',
'base64' => '<string>',
'storageKey' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"If-Match: <if-match>"
],
]);
$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.erstan.com/v1/public/authoring/agents/{id}/previews"
payload := strings.NewReader("{\n \"skillVersions\": [\n {\n \"skillId\": \"<string>\",\n \"version\": 2\n }\n ],\n \"laneId\": \"<string>\",\n \"input\": {},\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"type\": \"<string>\",\n \"base64\": \"<string>\",\n \"storageKey\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("If-Match", "<if-match>")
req.Header.Add("Idempotency-Key", "<idempotency-key>")
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.erstan.com/v1/public/authoring/agents/{id}/previews")
.header("If-Match", "<if-match>")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"skillVersions\": [\n {\n \"skillId\": \"<string>\",\n \"version\": 2\n }\n ],\n \"laneId\": \"<string>\",\n \"input\": {},\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"type\": \"<string>\",\n \"base64\": \"<string>\",\n \"storageKey\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erstan.com/v1/public/authoring/agents/{id}/previews")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["If-Match"] = '<if-match>'
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"skillVersions\": [\n {\n \"skillId\": \"<string>\",\n \"version\": 2\n }\n ],\n \"laneId\": \"<string>\",\n \"input\": {},\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"type\": \"<string>\",\n \"base64\": \"<string>\",\n \"storageKey\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"runId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "<string>",
"laneId": "<string>",
"status": "queued"
}{
"runId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "<string>",
"laneId": "<string>",
"status": "queued"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": [
{
"code": "<string>",
"message": "<string>",
"field": "<string>"
}
],
"requestId": "<string>"
}
}Preview the exact guarded draft
Requires stagedAuthoring, guardedPreviews, agents:read, agents:write and agents:run. This is a real hosted run with possible costs and external effects, not an offline simulation. Admission atomically checks the revision and seals the graph. Later edits/publication fork that snapshot. Optional skillVersions select immutable snapshots outside business input. Each requires current edit access and the Agent’s existing Skill scope. Unselected Skills and child Agents remain published. Poll the ordinary run handle using the same API key. Validation and publication are independent.
curl --request POST \
--url https://api.erstan.com/v1/public/authoring/agents/{id}/previews \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'If-Match: <if-match>' \
--data '
{
"skillVersions": [
{
"skillId": "<string>",
"version": 2
}
],
"laneId": "<string>",
"input": {},
"attachments": [
{
"name": "<string>",
"type": "<string>",
"base64": "<string>",
"storageKey": "<string>"
}
]
}
'import requests
url = "https://api.erstan.com/v1/public/authoring/agents/{id}/previews"
payload = {
"skillVersions": [
{
"skillId": "<string>",
"version": 2
}
],
"laneId": "<string>",
"input": {},
"attachments": [
{
"name": "<string>",
"type": "<string>",
"base64": "<string>",
"storageKey": "<string>"
}
]
}
headers = {
"If-Match": "<if-match>",
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'If-Match': '<if-match>',
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
skillVersions: [{skillId: '<string>', version: 2}],
laneId: '<string>',
input: {},
attachments: [
{name: '<string>', type: '<string>', base64: '<string>', storageKey: '<string>'}
]
})
};
fetch('https://api.erstan.com/v1/public/authoring/agents/{id}/previews', 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.erstan.com/v1/public/authoring/agents/{id}/previews",
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([
'skillVersions' => [
[
'skillId' => '<string>',
'version' => 2
]
],
'laneId' => '<string>',
'input' => [
],
'attachments' => [
[
'name' => '<string>',
'type' => '<string>',
'base64' => '<string>',
'storageKey' => '<string>'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"If-Match: <if-match>"
],
]);
$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.erstan.com/v1/public/authoring/agents/{id}/previews"
payload := strings.NewReader("{\n \"skillVersions\": [\n {\n \"skillId\": \"<string>\",\n \"version\": 2\n }\n ],\n \"laneId\": \"<string>\",\n \"input\": {},\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"type\": \"<string>\",\n \"base64\": \"<string>\",\n \"storageKey\": \"<string>\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("If-Match", "<if-match>")
req.Header.Add("Idempotency-Key", "<idempotency-key>")
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.erstan.com/v1/public/authoring/agents/{id}/previews")
.header("If-Match", "<if-match>")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"skillVersions\": [\n {\n \"skillId\": \"<string>\",\n \"version\": 2\n }\n ],\n \"laneId\": \"<string>\",\n \"input\": {},\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"type\": \"<string>\",\n \"base64\": \"<string>\",\n \"storageKey\": \"<string>\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erstan.com/v1/public/authoring/agents/{id}/previews")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["If-Match"] = '<if-match>'
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"skillVersions\": [\n {\n \"skillId\": \"<string>\",\n \"version\": 2\n }\n ],\n \"laneId\": \"<string>\",\n \"input\": {},\n \"attachments\": [\n {\n \"name\": \"<string>\",\n \"type\": \"<string>\",\n \"base64\": \"<string>\",\n \"storageKey\": \"<string>\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"runId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "<string>",
"laneId": "<string>",
"status": "queued"
}{
"runId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agentId": "<string>",
"laneId": "<string>",
"status": "queued"
}{
"error": {
"code": "<string>",
"message": "<string>",
"details": [
{
"code": "<string>",
"message": "<string>",
"field": "<string>"
}
],
"requestId": "<string>"
}
}Authorizations
Workspace API key created in Erstan Settings. Keys carry scopes (agents:list, agents:read, agents:write, agents:run, runs:read, tasks:execute, documents:read, documents:write, files:read, files:write). Agent access follows the key creator's current workspace, team, and agent permissions. Agent draft creation requires agents:write; update, validation, and publishing require both agents:read and agents:write; draft testing also requires agents:run. Keys with tasks:execute must include at least one allowed project or team (allowedProjectIds / allowedTeamIds); keys with document or file scopes must include at least one allowed team. Empty allowlists mean no access — the API fails closed.