Create Agent
curl --request POST \
--url https://api.velt.dev/v2/agents/create \
--header 'Content-Type: application/json' \
--header 'x-velt-api-key: <x-velt-api-key>' \
--header 'x-velt-auth-token: <x-velt-auth-token>' \
--data '
{
"data": {
"name": "<string>",
"description": "<string>",
"rawInstructions": "<string>",
"instructions": "<string>",
"enabled": true,
"phaseTimeoutMs": 123,
"metadata": {},
"contextGathering": {},
"execution": {},
"response": {},
"postProcess": {},
"input": {},
"scope": {},
"setup": {}
}
}
'import requests
url = "https://api.velt.dev/v2/agents/create"
payload = { "data": {
"name": "<string>",
"description": "<string>",
"rawInstructions": "<string>",
"instructions": "<string>",
"enabled": True,
"phaseTimeoutMs": 123,
"metadata": {},
"contextGathering": {},
"execution": {},
"response": {},
"postProcess": {},
"input": {},
"scope": {},
"setup": {}
} }
headers = {
"x-velt-api-key": "<x-velt-api-key>",
"x-velt-auth-token": "<x-velt-auth-token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-velt-api-key': '<x-velt-api-key>',
'x-velt-auth-token': '<x-velt-auth-token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: {
name: '<string>',
description: '<string>',
rawInstructions: '<string>',
instructions: '<string>',
enabled: true,
phaseTimeoutMs: 123,
metadata: {},
contextGathering: {},
execution: {},
response: {},
postProcess: {},
input: {},
scope: {},
setup: {}
}
})
};
fetch('https://api.velt.dev/v2/agents/create', 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.velt.dev/v2/agents/create",
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([
'data' => [
'name' => '<string>',
'description' => '<string>',
'rawInstructions' => '<string>',
'instructions' => '<string>',
'enabled' => true,
'phaseTimeoutMs' => 123,
'metadata' => [
],
'contextGathering' => [
],
'execution' => [
],
'response' => [
],
'postProcess' => [
],
'input' => [
],
'scope' => [
],
'setup' => [
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-velt-api-key: <x-velt-api-key>",
"x-velt-auth-token: <x-velt-auth-token>"
],
]);
$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.velt.dev/v2/agents/create"
payload := strings.NewReader("{\n \"data\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"rawInstructions\": \"<string>\",\n \"instructions\": \"<string>\",\n \"enabled\": true,\n \"phaseTimeoutMs\": 123,\n \"metadata\": {},\n \"contextGathering\": {},\n \"execution\": {},\n \"response\": {},\n \"postProcess\": {},\n \"input\": {},\n \"scope\": {},\n \"setup\": {}\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-velt-api-key", "<x-velt-api-key>")
req.Header.Add("x-velt-auth-token", "<x-velt-auth-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.velt.dev/v2/agents/create")
.header("x-velt-api-key", "<x-velt-api-key>")
.header("x-velt-auth-token", "<x-velt-auth-token>")
.header("Content-Type", "application/json")
.body("{\n \"data\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"rawInstructions\": \"<string>\",\n \"instructions\": \"<string>\",\n \"enabled\": true,\n \"phaseTimeoutMs\": 123,\n \"metadata\": {},\n \"contextGathering\": {},\n \"execution\": {},\n \"response\": {},\n \"postProcess\": {},\n \"input\": {},\n \"scope\": {},\n \"setup\": {}\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.velt.dev/v2/agents/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-velt-api-key"] = '<x-velt-api-key>'
request["x-velt-auth-token"] = '<x-velt-auth-token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"rawInstructions\": \"<string>\",\n \"instructions\": \"<string>\",\n \"enabled\": true,\n \"phaseTimeoutMs\": 123,\n \"metadata\": {},\n \"contextGathering\": {},\n \"execution\": {},\n \"response\": {},\n \"postProcess\": {},\n \"input\": {},\n \"scope\": {},\n \"setup\": {}\n }\n}"
response = http.request(request)
puts response.read_body{
"result": {
"status": "success",
"message": "Agent created successfully",
"data": {
"agentId": "abc123def456"
}
}
}
Agents
Create Agent
POST
/
v2
/
agents
/
create
Create Agent
curl --request POST \
--url https://api.velt.dev/v2/agents/create \
--header 'Content-Type: application/json' \
--header 'x-velt-api-key: <x-velt-api-key>' \
--header 'x-velt-auth-token: <x-velt-auth-token>' \
--data '
{
"data": {
"name": "<string>",
"description": "<string>",
"rawInstructions": "<string>",
"instructions": "<string>",
"enabled": true,
"phaseTimeoutMs": 123,
"metadata": {},
"contextGathering": {},
"execution": {},
"response": {},
"postProcess": {},
"input": {},
"scope": {},
"setup": {}
}
}
'import requests
url = "https://api.velt.dev/v2/agents/create"
payload = { "data": {
"name": "<string>",
"description": "<string>",
"rawInstructions": "<string>",
"instructions": "<string>",
"enabled": True,
"phaseTimeoutMs": 123,
"metadata": {},
"contextGathering": {},
"execution": {},
"response": {},
"postProcess": {},
"input": {},
"scope": {},
"setup": {}
} }
headers = {
"x-velt-api-key": "<x-velt-api-key>",
"x-velt-auth-token": "<x-velt-auth-token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-velt-api-key': '<x-velt-api-key>',
'x-velt-auth-token': '<x-velt-auth-token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
data: {
name: '<string>',
description: '<string>',
rawInstructions: '<string>',
instructions: '<string>',
enabled: true,
phaseTimeoutMs: 123,
metadata: {},
contextGathering: {},
execution: {},
response: {},
postProcess: {},
input: {},
scope: {},
setup: {}
}
})
};
fetch('https://api.velt.dev/v2/agents/create', 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.velt.dev/v2/agents/create",
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([
'data' => [
'name' => '<string>',
'description' => '<string>',
'rawInstructions' => '<string>',
'instructions' => '<string>',
'enabled' => true,
'phaseTimeoutMs' => 123,
'metadata' => [
],
'contextGathering' => [
],
'execution' => [
],
'response' => [
],
'postProcess' => [
],
'input' => [
],
'scope' => [
],
'setup' => [
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-velt-api-key: <x-velt-api-key>",
"x-velt-auth-token: <x-velt-auth-token>"
],
]);
$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.velt.dev/v2/agents/create"
payload := strings.NewReader("{\n \"data\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"rawInstructions\": \"<string>\",\n \"instructions\": \"<string>\",\n \"enabled\": true,\n \"phaseTimeoutMs\": 123,\n \"metadata\": {},\n \"contextGathering\": {},\n \"execution\": {},\n \"response\": {},\n \"postProcess\": {},\n \"input\": {},\n \"scope\": {},\n \"setup\": {}\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-velt-api-key", "<x-velt-api-key>")
req.Header.Add("x-velt-auth-token", "<x-velt-auth-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.velt.dev/v2/agents/create")
.header("x-velt-api-key", "<x-velt-api-key>")
.header("x-velt-auth-token", "<x-velt-auth-token>")
.header("Content-Type", "application/json")
.body("{\n \"data\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"rawInstructions\": \"<string>\",\n \"instructions\": \"<string>\",\n \"enabled\": true,\n \"phaseTimeoutMs\": 123,\n \"metadata\": {},\n \"contextGathering\": {},\n \"execution\": {},\n \"response\": {},\n \"postProcess\": {},\n \"input\": {},\n \"scope\": {},\n \"setup\": {}\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.velt.dev/v2/agents/create")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-velt-api-key"] = '<x-velt-api-key>'
request["x-velt-auth-token"] = '<x-velt-auth-token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data\": {\n \"name\": \"<string>\",\n \"description\": \"<string>\",\n \"rawInstructions\": \"<string>\",\n \"instructions\": \"<string>\",\n \"enabled\": true,\n \"phaseTimeoutMs\": 123,\n \"metadata\": {},\n \"contextGathering\": {},\n \"execution\": {},\n \"response\": {},\n \"postProcess\": {},\n \"input\": {},\n \"scope\": {},\n \"setup\": {}\n }\n}"
response = http.request(request)
puts response.read_body{
"result": {
"status": "success",
"message": "Agent created successfully",
"data": {
"agentId": "abc123def456"
}
}
}
Use this API to create a new custom agent configuration. The engine stores the agent in Firestore and writes version 1 to the agent’s versions subcollection. The schema uses
Required when
MCP servers (
When
4. Agent that verifies content against an MCP server (
Errors:
.passthrough(), so any additional behavioral fields are forwarded to the service layer.
Endpoint
POST https://api.velt.dev/v2/agents/create
Headers
string
required
Your API key.
string
required
Your Auth Token.
Body
Params
object
required
Show properties
Show properties
string
required
Min 1 char. Agent display name.
string
required
Min 1 char. Human-readable description of what the agent does.
string
Original user-provided instructions (before enhancement).
string
Processed/enhanced instructions used by the AI execution. Required whenever the execution strategy consumes a prompt —
"ai" (the default), "service+ai", "stagehand-agent", and "mcp-tools". Only pure "service" agents may omit it.boolean
required
Whether the agent is active.
number
Per-phase timeout in milliseconds. Positive integer, no upper bound. Omit to use the platform default.
object
Arbitrary client metadata. Use your own keys (e.g.
{ "team": "growth", "owner": "jane" }).object
required
Context gathering configuration.
| Field | Type | Required | Description |
|---|---|---|---|
strategies | string[] | yes | Min 1 strategy. See context gathering strategies. |
strategyOptions | object | no | Per-strategy option overrides, keyed by strategy name (see below). When strategies includes "rest-api", strategyOptions["rest-api"] is required (see below). |
aiConfig | object | no | AI provider/model override for the gather phase (see below). |
object
required
Execution configuration. Send
{} to accept the defaults (the "ai" strategy).| Field | Type | Required | Description |
|---|---|---|---|
executionStrategy | string | no | "ai" (default), "service", "service+ai", "stagehand-agent", or "mcp-tools". |
serviceId | string | no | Required when executionStrategy is "service" or "service+ai". Values: "broken-links", "crawler", "screenshot", "accessibility-checker", "og-image-checker". |
aiConfig | object | no | AI provider/model override for the execute phase (see below). |
mcpServers | object[] | no | Required (≥1) when executionStrategy is "mcp-tools". Remote MCP servers the model may call during the tool loop (see below). |
responseDescriptions | object | no | Per-field descriptions guiding AI response shaping (see below). |
strategyOptions | object | no | Per-strategy option overrides. See execution strategy options. |
knowledge | object | no | Memory-RAG knowledge integration (see below). |
object
Response formatting configuration.
| Field | Type | Required | Description |
|---|---|---|---|
useAiFormatting | boolean | no | Whether to use AI for response formatting. |
formattingPrompt | string | no | Custom prompt for AI formatting. |
responseAdapter | string | no | Adapter name: "broken-links-response", "crawler-response", "screenshot-response", "accessibility-checker-response", "og-image-checker-response". |
aiConfig | object | no | Accepted and stored, but not consumed. See the note below. |
AI response formatting is not implemented yet.
useAiFormatting, formattingPrompt, and response.aiConfig are accepted and stored, but nothing reads them at runtime, so setting them has no effect on the agent’s output.object
Post-processing pipeline configuration. All stages default to enabled when omitted or when
Unknown keys inside
enabled is not explicitly false.| Field | Type | Required | Description |
|---|---|---|---|
guardrails | object | no | { enabled?: boolean }: finding quality pipeline — deduplicates findings (identical source URL + target text + occurrence + issue type) and sanitizes HTML/XSS from all text fields. |
deletePreviousSuggestions | object | no | { enabled?: boolean }: per-URL delete-and-recreate that clears the prior run’s suggestions for the URL before writing the new ones. This is the active dedup mechanism. |
annotations | object | no | { enabled?: boolean, strategy?: string }: strategy is "findings", "word-level", "page-level", or "none". |
analytics | object | no | { enabled?: boolean }: analytics event tracking. |
matchAndMerge | object | no | Deprecated / ignored. { enabled?: boolean }: replaced by deletePreviousSuggestions. Still accepted for backward compatibility but has no effect. |
customProcessors | string[] | no | Custom post-processor names. |
postProcess are rejected. Pin resolution (anchoring visual findings to element coordinates) always runs as part of the pipeline and is not configurable via this field.object
Input declaration configuration.
Each
| Field | Type | Required | Description |
|---|---|---|---|
variableKeys | string[] | no | Variable names the agent expects in payload.variables. |
userContextFields | object[] | no | User-context field definitions ({ id, title, type, required?, example?, defaultValue? }). |
inputRequirements | object | no | { requires?: string[], requiresOneOf?: string[] }. |
userContextFields[] entry: id (min 1), title (min 1), type ("string" / "number" / "boolean"), optional required (boolean), optional example (string; sample value surfaced in setup UIs), optional defaultValue (string / number / boolean).input.supportedVariables is response-only. It lists the template variables the agent’s prompt can reference (e.g. {{restApiData}}, {{memoryContext}}) and is server-computed from contextGathering.strategies and input.variableKeys. On create, anything you send in this field is discarded. On a version update it is recomputed only when those inputs change.object
Scope and targeting configuration.
| Field | Type | Required | Description |
|---|---|---|---|
pageScope | string[] | no | URL glob patterns the agent runs on. |
pageScopeExclude | string[] | no | URL glob patterns to exclude. |
contentTypes | string[] | no | Content types the agent handles. |
crossPage | object | no | Cross-page execution config. See below. |
crossPage is optional, but when present these three fields are required; omitting any of them fails validation.| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | yes | Whether cross-page evaluation is on. |
targetProperty | string | yes | The property being compared across pages. |
pageDiscovery | string | yes | "auto" (crawl to discover pages) or "manual" (use pages[]). |
pages | string[] | no | Explicit page list. Pair with pageDiscovery: "manual". |
sourceOfTruthKnowledgeSourceId | string | no | Knowledge source to treat as the reference for the comparison. |
object
Tooling metadata for the setup assistant and checklist-to-agent converter. Not consumed by the agent pipeline at runtime.
Each
| Field | Type | Required | Description |
|---|---|---|---|
setupSamples | object[] | no | Sample content plus the findings the agent should produce on it. |
checklistOrigin | object | no | Provenance for an agent generated from a checklist. |
setupSamples[] entry:| Field | Type | Required | Description |
|---|---|---|---|
content | string | yes | The sample content the agent is evaluated against. |
source | string | yes | "synthetic" or "user-provided". |
findings | object[] | yes | Expected findings, each { message (required), elementSelector?, isCorrect? }. |
pmVerdict | string | no | Reviewer verdict: "correct" or "incorrect". |
pmNotes | string | no | Free-text reviewer notes. |
checklistOrigin:| Field | Type | Required | Description |
|---|---|---|---|
checklistId | string | yes | Source checklist ID. |
checklistVersion | number | yes | Source checklist version. Integer ≥ 0. |
sectionRefs | string[] | yes | Checklist sections this agent covers. |
memoryRuleIds | string[] | yes | Knowledge rules this agent was derived from. |
deduplicatedFrom | number | no | Count of source rules collapsed into this agent. Integer ≥ 0. |
Context gathering strategies
| Strategy | What it returns |
|---|---|
web-page-text | Visible text extracted via Puppeteer |
web-page-screenshot | Full-page screenshot as image (multimodal input) |
web-page-html | Cleaned HTML content |
web-page-css | Cleaned CSS content from stylesheets |
web-page-links | All hyperlinks, classified internal/external |
web-page-accessibility | Accessibility issues (Pa11y / HTML_CodeSniffer) |
computed-styles | Browser-resolved computed CSS for specific selectors |
robots-txt | Fetched robots.txt |
sitemap-data | Discovered and parsed XML sitemaps |
lighthouse | Google Lighthouse audit (performance / a11y / best-practices / SEO) |
rest-api | Customer-configured REST endpoints fetched at runtime (see below) |
none | No context gathering (for service-only or text-only agents) |
Context gathering strategy options
Per-strategy overrides are supplied undercontextGathering.strategyOptions["<strategy>"]. All are optional unless noted.
| Strategy | Option | Type | Description |
|---|---|---|---|
web-page-text | deviceType | string | "mobile" / "desktop" (default "desktop"). |
excludeIframes | boolean | Exclude iframe content. Default false. | |
web-page-screenshot | scrollBeforeCapture | boolean | Scroll the full page before capture. |
maxPageHeightPx | number | Cap the captured page height. | |
waitUntil | string | Puppeteer wait condition. | |
deviceType | string | "mobile" / "desktop". | |
web-page-html | includeHead | boolean | Include <head>. Default true. |
includeBody | boolean | Include <body>. Default true. | |
excludeIframes | boolean | Default false. | |
waitUntil | string | Puppeteer wait condition. | |
web-page-css | includeInlineStyles | boolean | Include inline <style> content. |
computed-styles | selectors | string[] | Required: CSS selectors to inspect. |
properties | string[] | Override the curated default property list. | |
sitemap-data | maxEntries | number | Max sitemap entries. Default 500. |
lighthouse | categories | string[] | Lighthouse categories to run. |
formFactor | string | "mobile" / "desktop". | |
rest-api | (see below) | object | Required when rest-api is selected: rest-api strategy options. |
rest-api strategy options
Required when strategies includes "rest-api". Each configured endpoint is fetched at execution time and the resolved URL + description + JSON-stringified response is injected into the prompt via the {{restApiData}} template variable.
| Field | Type | Required | Description |
|---|---|---|---|
endpoints | RestApiEndpoint[] | yes | 1–10 endpoints to fetch. |
maxResponseBytes | number | no | Per-endpoint response-body cap. Default 1000000, hard-capped 5000000. |
RestApiEndpoint fields:
| Field | Type | Required | Description |
|---|---|---|---|
description | string | yes | What this endpoint is about; surfaced in the prompt alongside the URL + response. |
url | string | yes | Endpoint URL. Supports {{variable}} templating. SSRF-validated at fetch time (no internal/loopback/link-local hosts). |
method | "GET"|"POST"|"PUT"|"PATCH"|"DELETE" | no | Default "GET". |
id | string | no | Stable endpoint identifier. |
name | string | no | Human label. |
headers | object | no | Non-secret headers (templated). |
query | object | no | Query params (templated). |
body | object | string | no | Request body for write methods (templated). |
auth | object | no | Discriminated union (none|bearer|basic|header). Secret fields are encrypted at rest and redacted on read. |
cacheTtlSeconds | number | no | In-process per-instance cache TTL. Default 0 (always fresh). Max 86400. |
timeoutMs | number | no | Per-request timeout. Default 10000, hard-capped 30000. |
responsePath | string | no | Dot-path to extract a sub-field of the JSON response. |
auth shapes:
{ "type": "none" }
{ "type": "bearer", "token": "<secret>" }
{ "type": "basic", "username": "user", "password": "<secret>" }
{ "type": "header", "headers": { "X-Api-Key": "<secret>" } }
Auth secret handling (redaction-on-read):
token, password, and header values are encrypted before the Firestore write and are never returned to clients. On read (Get / List Agents), encrypted secret fields are replaced with "__redacted__". To rotate a secret, send the new plaintext value on update; to keep an existing secret, omit the field.MCP servers (mcp-tools strategy)
When execution.executionStrategy is "mcp-tools", the agent runs a model-driven tool loop: at execution time the engine connects to each remote MCP (Model Context Protocol) server declared in execution.mcpServers, lists its tools, exposes them to the model, and runs a multi-turn call→tool→call loop until the model emits a final structured response. For example, a docs code-verification agent can check on-page snippets live against your documentation MCP server instead of a pre-ingested corpus.
The
"mcp-tools" strategy is provider-agnostic (works on both Claude and Gemini) and requires a non-empty top-level instructions field and at least one entry in execution.mcpServers. Omitting either fails validation.execution.mcpServers is an array of 1–5 server objects. Each server object is strictly validated (unknown keys rejected):
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Min 1 char. Stable identifier for the server (used in logs + findings). |
url | string | yes | Remote MCP server URL. Must start with http(s):// or contain a {{template}} that resolves to one. SSRF-validated at connect time (no internal/loopback/link-local hosts; redirects re-validated, max 3 hops). |
name | string | no | Human-readable label. |
description | string | no | What this server provides (e.g. "Velt docs MCP"). |
transport | string | no | Transport type. Only "http" is supported (Streamable HTTP with SSE fallback). Defaults to "http". |
auth | object | no | Auth config. Discriminated union (none | bearer | basic | header); same shape as the rest-api strategy. Secret fields are encrypted at rest and redacted on read. |
allowedTools | string[] | no | Allowlist of tool names. When set, only these tools are exposed/callable. Up to 64 tools per server are exposed. |
timeoutMs | number | no | Per-request timeout (ms) for connect/listTools/callTool. Default 30000, hard-capped 120000. |
auth shapes (identical to the rest-api strategy):
{ "type": "none" }
{ "type": "bearer", "token": "<secret>" }
{ "type": "basic", "username": "user", "password": "<secret>" }
{ "type": "header", "headers": { "X-Api-Key": "<secret>" } }
Auth secret handling (redaction-on-read): MCP
token, password, and header values are encrypted before the Firestore write (using an MCP-specific encryption salt) and are never returned to clients. On read (Get / List Agents, List Versions), encrypted secret fields are replaced with "__redacted__". To rotate a secret, send the new plaintext value on a version update; to keep an existing secret, omit the field.AI config (provider / model)
execution.aiConfig, contextGathering.aiConfig, and response.aiConfig all accept the same strictly-validated object. Unknown keys are rejected.
| Field | Type | Description |
|---|---|---|
provider | string | LLM provider: "gemini", "claude", or "openai". Defaults to the platform default when omitted. Persisted. |
model | string | Accepted for backward compatibility and then discarded. Not persisted, and not validated against any allowlist. |
responseMimeType | string | Accepted for backward compatibility and then discarded. Not persisted. |
maxToolTurns | number | Tool-loop turn budget for the mcp-tools strategy. Integer 1..16; out-of-range values are rejected at config time. Default: 8. Persisted on execution.aiConfig only. |
Only
provider survives a write here (plus maxToolTurns on execution.aiConfig). The effective model is resolved on every execution rather than frozen onto the agent, so pinning model at create time does nothing and returns 200 without complaint.To pin a model, send aiConfig on the request instead: see Run Execution. That one is validated against a model allowlist and returns INVALID_ARGUMENT for anything off it.Execution strategy options
execution.strategyOptions["<strategy>"] carries per-strategy runtime overrides. The stagehand-agent strategy accepts:
| Option | Type | Default | Description |
|---|---|---|---|
mode | string | "hybrid" | "dom" / "hybrid" / "cua". |
maxSteps | number | 25 | Max autonomous steps. |
model | string | inherited | Model override for the agent loop. |
systemPrompt | string | System prompt for the agent. | |
useStructuredOutput | boolean | true | Return structured output. |
waitUntil | string | "networkidle2" | "domcontentloaded" / "load" / "networkidle2". |
navigationTimeout | number | 30000 | Navigation timeout in ms. |
When using
stagehand-agent, set contextGathering.strategies: ["none"]; the agent handles navigation, interaction, and extraction itself.Knowledge (Memory-RAG)
execution.knowledge pulls workspace knowledge from Velt Memory into the prompt. Strictly validated. Only these four fields are accepted:
| Field | Type | Range | Description |
|---|---|---|---|
useMemory | boolean | When true, retrieves knowledge/patterns/activities and injects them as {{memoryContext}}. | |
maxChunks | number | 1–20 | Max knowledge chunks to retrieve. |
maxPatterns | number | 1–20 | Max learned patterns to retrieve. |
maxActivities | number | 1–20 | Max recent activities to retrieve. |
The legacy
sourceIds field has been removed and maxChunks is now capped at 20 (previously 50). Sending sourceIds or any other extra key inside knowledge returns a 400 INVALID_ARGUMENT.Response Descriptions
All fields are optional strings that instruct the AI on how to populate each finding field:| Field | Description |
|---|---|
summary | Summary text of the overall analysis. |
title | Short title of the finding. |
description | Detailed description of the finding. |
severity | Severity level. Values: critical, high, medium, low, info. |
targetText | The exact text on the page this finding refers to. |
suggestion | Suggested fix or recommendation. |
isPageLevel | "true" for visual/layout findings; "false" when targetText is real DOM text. |
issueType | Issue classification tag (short, lowercase, hyphenated). |
confidence | Confidence score from 0 to 100. |
htmlSelector | CSS selector of the DOM element. |
findingType | Finding type classification. |
Example Requests
1. Minimal agent
{
"data": {
"name": "Simple Content Checker",
"description": "Checks for broken images and missing alt text",
"enabled": true,
"instructions": "Check for broken images and missing alt text on the page.",
"contextGathering": {
"strategies": ["web-page-html"]
},
"execution": {}
}
}
2. Full custom agent with cross-page execution and Memory-RAG
{
"data": {
"name": "Brand Consistency Checker",
"description": "Validates brand colors, logos, and typography across pages",
"rawInstructions": "Check that all headings use the brand font 'Inter'. Verify the primary color #1A73E8 is used for CTAs.",
"instructions": "Check that all headings use the brand font 'Inter'. Verify the primary color #1A73E8 is used for CTAs.",
"enabled": true,
"metadata": { "team": "growth", "owner": "jane" },
"contextGathering": {
"strategies": ["web-page-screenshot", "web-page-text", "web-page-html"],
"strategyOptions": {
"web-page-screenshot": { "scrollBeforeCapture": true }
}
},
"execution": {
"executionStrategy": "ai",
"responseDescriptions": {
"title": "Short name for the brand inconsistency",
"severity": "One of: critical, high, medium, low",
"suggestion": "Specific CSS or design fix",
"issueType": "One of: brand-color, brand-font, brand-logo"
},
"knowledge": {
"useMemory": true,
"maxChunks": 10,
"maxPatterns": 5,
"maxActivities": 5
}
},
"postProcess": {
"guardrails": { "enabled": true },
"deletePreviousSuggestions": { "enabled": true },
"annotations": { "enabled": true, "strategy": "findings" },
"analytics": { "enabled": true }
},
"input": {
"inputRequirements": { "requires": ["url"] },
"userContextFields": [
{ "id": "brand_color", "title": "Primary brand color?", "type": "string", "required": true },
{ "id": "brand_font", "title": "Heading font family?", "type": "string" }
]
},
"scope": {
"pageScope": ["https://example.com/*"],
"pageScopeExclude": ["https://example.com/admin/*"],
"crossPage": {
"enabled": true,
"targetProperty": "brandConsistency",
"pageDiscovery": "auto"
}
}
}
}
3. Agent with live REST API context
{
"data": {
"name": "Pricing Parity Checker",
"description": "Compares on-page prices with the billing API",
"enabled": true,
"instructions": "Compare the price shown on the page with the price returned by the billing API and flag any mismatch.",
"execution": {},
"contextGathering": {
"strategies": ["web-page-text", "rest-api"],
"strategyOptions": {
"rest-api": {
"endpoints": [
{
"name": "Plan pricing",
"description": "Canonical plan prices from the billing service",
"url": "https://api.example.com/plans/{{variables.planId}}",
"method": "GET",
"headers": { "Accept": "application/json" },
"auth": { "type": "bearer", "token": "sk_live_xxx" },
"cacheTtlSeconds": 60,
"responsePath": "data.plan"
}
]
}
}
},
"input": { "variableKeys": ["planId"] }
}
}
4. Agent that verifies content against an MCP server (mcp-tools)
{
"data": {
"name": "Docs Code Verifier",
"description": "Verifies on-page code snippets against the docs MCP",
"enabled": true,
"instructions": "For every code snippet on the page, use the documentation MCP tools to find the matching feature and verify the snippet is correct and up to date. Flag any snippet that does not match the docs.",
"contextGathering": {
"strategies": ["web-page-html"]
},
"execution": {
"executionStrategy": "mcp-tools",
"mcpServers": [
{
"id": "velt-docs",
"name": "Velt Docs MCP",
"description": "Live Velt documentation MCP server",
"url": "https://velt.dev/docs/mcp",
"transport": "http",
"auth": { "type": "none" }
}
]
}
}
}
Response
Success Response
{
"result": {
"status": "success",
"message": "Agent created successfully",
"data": {
"agentId": "abc123def456"
}
}
}
| Field | Type | Description |
|---|---|---|
data.agentId | string | Firestore document ID of the new agent |
Failure Response
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
INVALID_ARGUMENT (Zod validation failure, e.g. missing name, description, enabled, contextGathering.strategies, or execution; an unknown key inside knowledge or postProcess; an invalid userContextFields.type; missing instructions for an AI/stagehand-agent/mcp-tools strategy; or missing/invalid execution.mcpServers when executionStrategy is "mcp-tools") / RESOURCE_EXHAUSTED (workspace already has the maximum number of custom agents).
{
"result": {
"status": "success",
"message": "Agent created successfully",
"data": {
"agentId": "abc123def456"
}
}
}
Was this page helpful?
⌘I

