For Claude and Codex, install the official Erstan plugin with browser-based OAuth and authorize Build access. Use the Advanced API-key setup only for automation or MCP clients that do not support the plugin flow. See Connect a Coding Agent.
Backend-enforced guarantees
These restrictions are server controls, not prompt guardrails:create_agentalways creates a draft, andupdate_agentwrites a draft while preserving any retained published version.publish_agentvalidates the complete saved graph and publishes only the exact current revision/version. The authoring tool family exposes no archive or delete operation.test_agentruns the guarded current draft;run_agentis a separate capability that resolves only a retained published version.- Every call re-checks the connected user’s live workspace, team, and agent permissions. Advanced API keys use their creator’s live permissions.
update_agentandpublish_agentrequire an opaque revision, and the backend atomically rejects stale revisions before mutation. The client handles that result by re-reading and reconciling; it cannot bypass the check by retrying.validate_agentnever mutates, publishes, or executes the draft.- Skill package validation never saves or executes code. Skill creation is draft-only, full-package updates require
expectedVersion, and publication re-validates the current package. - When the live guide advertises
lifecycle.skillSavesAreDrafts: true, Skill saves stage working content and retain the previous published package. Agent presentation stages with the graph whenlifecycle.agentPresentationVersioned: true. A missing capability is not evidence of staging support. - User-authored Skill actions run only through the isolated E2B action runtime. Declaring
sideEffects: "none"disables action-runtime network access, but does not make workspace-authored code trusted or bypass write confirmation.
Authorize agent building
Install the plugin and authorize Build access for the normal interactive setup. This grants the connected-app permissions View agents, Build agents, Run agents, Publish agents, and View runs, plus read-only access to work, documents, and files. It does not grant task, document, or file writes.Advanced API-key setup
For a non-OAuth client, create a workspace API key in Settings → API Keys with both authoring scopes:
Scopes are exact and additive.
create_agent needs agents:write; update_agent, validate_agent, and publish_agent require both agents:read and agents:write. A practical authoring key therefore needs both scopes. Published discovery uses agents:list; execution uses agents:run, which is also required for a draft preview. SDK/REST authoring inventories additionally use agents:list; do not assume their scopes or argument shapes are identical to MCP.
Access model
An OAuth connection acts as the user who approved it; an API key acts as the user who created it. Erstan evaluates that person’s live workspace, team, and agent permissions on every tool call:get_agentcan read only an agent the connection owner can normally see.create_agentmust target an accessible team in the connection owner’s workspace.update_agent,validate_agent, andpublish_agentrequire the connection owner’s normal edit permission for the target agent. Publishing also re-checks visibility of its current home team when one is assigned.
Tools
tools/list advertises a tool only when the OAuth grant holds the corresponding connected-app permission or an advanced API key holds every scope shown below. The plugin’s Build profile supplies the standard authoring permissions.
MCP
get_agent reads authoring state, including drafts. list_agents under agents:list is intentionally different: it lists only retained published versions that can be run externally.Argument and response summary
get_agent_node_catalogaccepts optionalnodeType,builderSection, andincludeFieldDetailsfilters. SetincludeActionTypestotrueto include valid action types and optionally setmaxActionTypesfrom 10 to 500 (default 120). Its versioned, builder-visible response is the source of truth; hidden/internal nodes, event triggers, raw HTTP categories, and unsafe action names are filtered out.get_agent_builder_guidetakes no arguments and returns versioned builder guidance, including the external tool-policy and baseline-tool rules below.get_agenttakesagentIdand an optional positive integerversion. It returns{ "agent": { ... }, "builderUrl": "..." }. A current read provides the opaque write revision paired with that content. An explicit historical version adds top-levelselectedVersionand a read-only history revision; never use it to authorize a current update or publication.create_agentrequiresteamId,name,nodes, andedges. Optional metadata includesdescription,category,tags,difficulty,estimatedTime, andbehaviorType(interactiveorpipeline). The new agent is shared with the selected team, and its response includes the first opaquerevision.update_agentrequiresagentId, the opaquerevisionpaired with the edited baseline, and at least one metadata or graph change.versionis an optional additional current-version guard. It does not move an agent to another team. Never decode or synthesize a revision, or substitute a newer token onto stale edits.validate_agentcan validate a saved draft byagentId, or validate proposedname,behaviorType,nodes, andedgesbefore creation. Optional proposed fields overlay the saved graph whenagentIdis present. Its response includesagentId,version,valid, and structureddiagnosticswith severity, code, message, and relevant graph paths or IDs.publish_agentrequiresagentIdand the opaquerevisionof the exact approved content; pass itscurrentVersionas optionalversionfor a second guard. It returns authoring state pluspublished: trueandalreadyPublished. An exact already-published version is a validated no-op. After a lost response, reuse the same supported idempotency key only with identical arguments, or re-read and reconcile content and lifecycle state. Do not infer success merely from a published status or retry blindly under a fresh key.test_agentrequires the selected draft’sversion. Send its pairedrevisionwhen advertised; explicit top-levelskillVersions: [{ skillId, version }]require that revision and matching schema support. Never place these controls inside businessinput.- A canonical Skill package is
{ "version": 1, "entryPath": "SKILL.md", "files": [...], "actions": [...] }. Every action points to an included file and declaresruntime: "e2b",language, andsideEffects(noneorexternal). validate_agent_skillandcreate_agent_skillaccept the complete object as eitherpackageorpackageJson. Create also requires a displaynameand always returns a draft.get_agent_skillreturnsskill.packageJson,currentVersion, and, on supporting servers,publishedVersionandhasDraft. The current package can be an unpublished draft. Send the complete candidate toupdate_agent_skillwith the version paired with its baseline asexpectedVersion; do not send only changed files.publish_agent_skillacceptsskillIdand the approved package’sexpectedVersion. A stale version returnsskill_version_conflict; re-read and reconcile, and obtain approval for changed publication content.
idempotencyKey arguments for Agent mutations where the schema
advertises them; reuse only for identical intent and arguments. MCP Skill
create/update/publish tools do not expose that argument. After an ambiguous
Skill mutation, reconcile the complete package and lifecycle state before any
retry. The binding catalog can omit drafts, so absence from list_agent_skills
does not prove a timed-out create failed. SDK/REST receipt guarantees do not
automatically apply to arbitrary MCP calls.
Recommended flow
- Call
get_agent_builder_guideonce at the start of the build. - Call
get_agent_node_catalogand choose only node types and fields present in the returned live catalog. - Construct the complete directed graph and call
create_agentwith the targetteamId. - Keep the returned agent ID, opaque
revision,currentVersion, andbuilderUrl. - Call
validate_agent. The backend requires the complete proposed definition to pass validation. If an existing agent has unrelated pre-existing errors, stop before writing and ask whether the user wants to expand the change to repair them. - Keep the returned content and revision together. Before a later update, compare any remote changes with the intended edit; do not replace a stored guard without reconciling. Pass the revision paired with the edited baseline and optionally its current version.
- Use
test_agentonly when a real hosted preview is authorized. It is optional, not a publication gate. Publish only the exact validated content the user approved, with its paired guard; never publish dependencies implicitly.
Exact draft Skill previews
Inspect the livetest_agent schema before offering an exact preview. Each
explicit Skill version selection requires edit permission and must fit the
Agent’s existing Skill policy. Unselected Skills and child Agents use published
content. If the schema does not support the required revision and selections,
report the capability gap; do not drop them, put them in input, widen policy,
publish dependencies, or silently run a published Agent instead.
Preview executes real tools with normal costs, approvals, and possible external
effects. It seals the selected graph, so later edits/publication may fork and
advance its version. Poll the returned run ID and detect drift before the next
write. A successful preview neither publishes content nor authorizes publication.
Manage a Skill package
Use the same authorized connection for reusable Skill packages:- Build the complete package, including
SKILL.md, every referenced text file or script, and action metadata. - Call
validate_agent_skilland resolve every error. Validation does not save or run the package. - Call
create_agent_skill; it always creates a draft. Review the returnedskill.packageJson. - Before editing a published Skill, read
get_agent_builder_guide. Withlifecycle.skillSavesAreDrafts: true, a save stages a working package while the prior published version remains live. Read the complete package, apply the intended edit, and send its pairedcurrentVersionasexpectedVersion. Omitstatusunless a separate availability change is authorized; setting it to draft is not how staging works. - Publish only the exact validated package explicitly approved by the user, using its
expectedVersion. A published status withhasDraft: trueis valid: availability and the presence of working changes are different things.
.erstan/skill-package.json; older SKILL.md-only archives remain valid. The manifest is transport metadata and is not included in packageJson.files.
Agent Builder skill
The official plugin installs and versions this Skill automatically. The reference below is for advanced MCP clients that cannot install the plugin; Claude and Codex plugin users should not paste a separate copy into their projects. It teaches other clients how to use the backend-enforced authoring workflow without treating prompt instructions as the security boundary.Create a draft
The exact argument schema is available through MCPtools/list. A create call includes the target team, agent metadata, and the complete node-and-edge graph:
revision for the next update. currentVersion may be passed as the optional version guard on update or validation, but it does not replace the required revision token for updates.
A decoded create response has this stable shape:
Update and validate
For an agent that has never been published,update_agent accepts editable metadata patches and optional complete replacement nodes and edges. Always pass the most recently returned revision; version is optional:
"valid": false; inspect diagnostics for graph or configuration errors. Validation never publishes and does not make the draft externally runnable.
Publish a validated draft
Publishing re-runs the external-authoring validation and the normal workflow publish checks before changing lifecycle state:agent_revision_conflict and publishes nothing. Re-read, reconcile the user’s intent, validate again, and ask before publishing a changed graph. Repeating publish_agent with the current revision after the version is already live returns alreadyPublished: true without creating another version.
External-authoring boundaries
The catalog and validation contract deliberately expose only builder-visible nodes and tools.Triggers
Externally authored start nodes may useinput_form, chat, schedule, or webhook. triggerType: "event" is not externally authorable at all. In particular, email.received remains app-only. To receive a webhook, use triggerType: "webhook" rather than an event trigger.
AI nodes and tool selection
Every externally authored AI/tool-capable node must explicitly set canonical top-leveldata.toolPolicy to a value advertised for that node by get_agent_node_catalog. AI Respond, AI Think, and AI Plan & Execute support auto_discover, pinned_only, and none. Other internal model and fixed-action AI nodes advertise only pinned_only and none. The saved top-level field is authoritative. Legacy or nested selection aliases such as input.toolPolicy, toolMode, toolsMode, autoSelectTools, and autoToolDomainSelection are rejected for external authoring; runtime and nested inputs can only narrow a saved policy and never promote it.
Use none when the model needs no ordinary tools or executable actions; it removes the platform baseline, pinned, provider, discovered, workflow-edge, Task/artifact, and skill_execute surfaces. An independently active skillPolicy may still expose scoped read-only Skill readers so the model can load reusable guidance on demand. AI Respond, AI Think, and AI Plan & Execute include the complete built-in Erstan catalog without connector setup or manual pins. This applies in pinned_only and auto_discover, including scheduled runs and runs without a Task. A compact baseline is bound directly; other built-ins are available through tool_search, tool_describe, and tool_invoke. Pins scope connector access and can prioritize built-ins or customize approval policies. Explicit none disables ordinary tools and executable actions. Tool denies, resource permissions, write approvals, and Skill policies still apply. Workbench uses the workspace’s configured sandbox. Other internal model and fixed-action steps keep their declared tools; an assigned Task may supply Task/artifact tools without general Tool Hub discovery.
Use auto_discover only for exploratory or low-risk general assistance. Auto expands external connector discovery; the built-in Erstan catalog is also available in Pinned mode. Explicit tool pins remain available, and every discovered write retains the workspace execution policy. Pin any ordinary tool the workflow must have and retain its individual governance settings. toolPolicy: "auto_discover" controls ordinary-tool discovery only; it never changes an explicit per-tool writePolicy. Only a server-owned platformMinimumWritePolicy may make the effective execution policy stricter. Use pinned_only for deterministic, production, ERP/accounting, NetSuite, or write-capable work.
skillPolicy independently governs read-only Skill access on AI Respond, AI Think, and AI Plan & Execute. Attached skillIds are metadata-only candidates under both active Skill policies. With skillPolicy: "auto_discover", skill_search, skill_describe, skill_read_file, and er_skill_read can access the full readable Skill catalog even when Tools are Pinned or None. On an authored node, skillPolicy: "pinned_only" restricts those readers exactly to canonical persisted data.skillIds; runtime or nested skillIds may narrow but never widen the set. Authorized thread bindings merge only for legacy/ad-hoc conversational nodes with no authored skillPolicy. Full instructions load on demand through the readers; attaching a Skill never eagerly injects its full instructions. skillPolicy: "none" disables all Skill readers and discovery. Executable Skill actions remain separately governed by toolPolicy: Tools None exposes no skill_execute, Tools Pinned limits it to attached executable Skills, and catalog-wide Skill action execution requires both policies to be Auto.
selectedActions remain explicit Plan & Execute pins. They can coexist with additional agentTools and with toolPolicy: "auto_discover"; selecting or removing an action does not replace an explicit node policy. Each selected action may carry its own writePolicy (allow, require_approval, or deny) in-app. External authors should omit that field to inherit the catalog default or use an accepted stricter override.
Conversational Agent nodes declare pins in agentTools. Do not combine toolPolicy: "auto_discover" with a non-empty selectedActions array on AI Respond: selectedActions is legacy pinned-only state there, so the external API rejects that contradictory shape instead of letting persistence silently change the policy. Use selectedActions only on node types whose live catalog advertises it, such as Action or AI Plan & Execute.
With pinned_only or auto_discover, every explicit pinned tool must be visible in the live catalog returned for the connection owner:
- Every Erstan
er_*binding must explicitly setsource: "erstan"; Erstan source is never inferred. The name must be catalog-visible. Hidden/internal names, includinger_agent_builder_*, are denied even when disguised with function prefixes, aliases, nestedselectedActions, ortoolKeys. - Connector source names, tool names, and explicit
connectionIdvalues must all be visible to the connection owner in the current workspace. - Within one node, normally pin each canonical runtime tool name once. The only accepted duplicate is one
agentToolsoccurrence mirrored by one legacyselectedActionsoccurrence with the same trimmed/lowercased exact raw identity, the same explicit normalized source, and no conflictingconnectionId(identical when both are set). Function prefixes are not ignored for compatibility:functions.fooandfooare different raw aliases. Duplicates within either array, different raw aliases that canonicalize to one runtime name, conflicting connections, and different sources are rejected. Source conflicts usepinned_tool_runtime_name_source_conflict; all other incompatible duplicates usepinned_tool_runtime_name_duplicate_conflict. - The runtime-owned Tool Hub and Skill names
tool_search,catalog_search,tool_describe,tool_invoke,tool_batch_invoke,skill_search,skill_describe,skill_read_file,er_skill_read, andskill_executecannot be externally pinned under any connector source. Validation returnspinned_tool_runtime_name_reservedbefore consulting the connector catalog. providerToolsis unavailable for external authoring at both node and nested-input level. Use validatedagentToolsbindings on conversational Agents, orselectedActionsonly on node types whose live catalog advertises that field.- Both
actionCategory: "http"and the integration aliaswebhook_outboundare raw HTTP actions and are denied. - If supplied, a pinned tool’s
writePolicymay berequire_approvalordeny. External authoring cannot introducewritePolicy: "allow", automatic-write flags, orautoApproveWrites. An update or publish may preserve an identical existingallowbinding configured in Erstan, but cannot add, move, or duplicate one.
get_agent_node_catalog with includeActionTypes: true when selecting an action node. Do not invent an actionType that is absent from its bounded response.
Canonical graph fields
node.type and data.nodeType must resolve to the same canonical node type. For direct action nodes, actionType is authoritative: a legacy selectedTool value cannot override it or name a hidden tool. Hidden node types, deprecated group containers, unknown action types, and invalid graph shapes are also rejected.
A direct action (executionMode: "direct") runs one tool with the authored actionParameters and calls no model. It runs an Erstan tool (actionCategory: "erstan_tool") or one NetSuite Add-on tool: set actionCategory: "integration", set actionType to the Add-on tool, pin that tool as the only netsuite-addon entry in selectedActions, and give its arguments in actionParameters. The pin’s writePolicy is the step’s permission, so actionWritePolicy, agentTools on the step, and input bindings on the pin are refused. A pin of require_approval parks the run on an approval card that shows the resolved arguments: approving sends exactly those arguments once, and declining ends the run as cancelled. On AI For Each, onItemError: "continue" records a failed item in the loop output (failedItems, with failedItemCount and failedItemsComplete) and moves to the next item instead of failing the run; it needs a loop body of exactly one direct action or one code step that calls no model, and publish refuses any other body. A retryable error still ends the run. On a direct action, so do a declined approval and a permission refusal. A direct action on an Erstan tool that only reads (such as er_file_extract) also records any failure once the tool has started: an answer that says it failed, or an error the tool raised (for example a file that could not be read), with the tool’s own message as the item’s error. That includes a fault that repeats for every item, such as a model that is not connected. A refusal made before the tool starts still ends the run, and so does the same failed answer from a tool that writes. On a code step, every other failure is recorded for its item, including a fault that repeats for every item (a disabled feature or a configuration fault), so such a run completes with every item in failedItems. An item whose error has outcomeUnknown: true may have been written, so a later step must not send it again; a failed read never carries it.
Aliases returned by the node catalog may be accepted as input, but they are canonicalized before persistence. Subsequent get_agent responses return the canonical node.type and matching canonical data.nodeType when that field is present.
Child agent references
Anai-agent node uses targetWorkflowId to invoke another saved agent. The referenced child must be currently readable by the connection owner and belong to the same workspace as the parent. Self-reference is denied.
For a public or automated parent run, Erstan executes the retained published child snapshot. A newer child draft is not exposed to that run, and a child with no published version cannot be invoked from that surface. Test child changes as appropriate, then publish the validated revision in the app or with publish_agent before relying on it from an external or automated parent.
Resource limits
Limits are checked before persistence or connector-catalog loading:validate_agent returns at most 200 normal diagnostics plus a final diagnostics_truncated marker when more problems exist.
Published agents and drafts
Updating a live agent does not replace its public version. Erstan preserves the last published snapshot for normalagents:list and agents:run calls while the newer draft is edited. Draft nodes and prompts are not exposed to public runs until the exact validated revision is published in the app or through publish_agent.
When lifecycle.agentPresentationVersioned: true is advertised, name,
description, category, tags, difficulty, and estimatedTime stage with
the graph and promote together on explicit publication. behaviorType remains
immutable after the first publication. If the capability is absent or false,
follow the live metadata restrictions rather than assuming staged edits are
supported. Archive/delete and other app-only controls remain outside MCP.
Stale or malformed revisions fail with validation_failed and an agent_revision_conflict or agent_revision_invalid message. Re-read with get_agent and reconcile the content instead of swapping in a new token. Unsupported post-publication behavior-type changes can return agent_published_metadata_immutable.
Inaccessible or cross-workspace teams and agents return not_found without revealing whether the target exists.
Next steps
Connect an MCP client
Install the plugin and authorize Build access.
Agent Builder
Review and edit the generated graph in Erstan.
Preview, publish & versions
Test and publish the draft when it is ready.
API keys
Scope and permission details.