Building your AI agents
A practical walkthrough for designing AI agents from scratch — how to structure agents, write effective prompts, declare access policies, configure human gates, and get ready to publish.
Five ways to create an AI agent design
Envelope gives you five entry points depending on how you think about the work. All five produce the same output — a valid agent design — so you can switch between them at any time.
Chat — the primary creation flow. Describe what you want to build, Envelope asks clarifying questions, proposes an agent structure, and generates the full definition once you confirm. No forms, no dropdowns — just describe the work.
From a template — pick from pre-built starting points — Customer Support, Sales Development, Research, Engineering On-Call, Content Production, and more. Loads with realistic agents, prompts, and credential requirements. Customise from there.
Fork a community team — browse the community gallery on the Templates page and click Fork & edit on any published team. Envelope copies it into your workspace as a private draft — all agents, prompts, and settings intact.
Editor (blank) — start with an empty definition and build agent by agent using the guided form. No code required. Best when you have a precise structure in mind and prefer not to start from a generated draft.
JSON — edit the raw definition directly. Full control, no guardrails. Best when you know exactly what you want or are importing an existing config from another platform.
Chat creation flow
When you click New team, Envelope opens a chat interface and asks: "What would you like to build today?" From there the conversation moves through three stages.
Discovery — Envelope asks about the work — what the agents do, who handles what, which tools are involved. You can be brief or detailed. The more you say about how work flows, the more accurate the proposal.
Proposal — Envelope proposes a set of agents with names, titles, and descriptions. The preview panel on the right fills in as agents are proposed. You can push back, add constraints, or ask for changes — the conversation continues until the structure looks right.
Ready — once you confirm ("looks good", "yes", "build it"), Envelope synthesises the full agent design — prompts, reporting hierarchy, required variables, and secrets — saves it as a draft and drops you into the editor to review and publish.
Tool integrations — name specific tools in your description — Zendesk, Slack, HubSpot, Stripe — and Envelope adds the relevant credentials to
requiredSecretsautomatically. You don't need to list them separately.
Too vague: "A sales team"
More useful: "A sales development team with an SDR manager, two outbound SDRs who work leads in HubSpot, and a RevOps analyst who tracks pipeline metrics. The manager escalates stalled deals to a human for approval before any outreach goes out."
Using the editor
Everyone lands in the editor — whether you came from the chat flow or started blank. It has five tabs — Team, Agents, Inputs, Schedule, and JSON. The Schedule tab is where you configure how the team fires: on a recurring cron schedule, when an inbound webhook arrives (event trigger), or when a plain-English condition check passes (condition trigger). See the scheduling guide → Each agent is an expandable card with fields for name, title, role, adapter, model, system prompt, and reporting relationship. You can add agents, reorder them, and delete them.
Team tab — owner, name, visibility: The Team tab shows basic info displayed to deployers in the Library: the team's Name, a one-to-two sentence Description, Owner (which workspace it belongs to), and Visibility. There are three visibility options:
- Private — only you can see and install it. The default while you're building.
- Team — visible to everyone in your organisation. Use this to share a team internally without making it public.
- Public — listed in the Library for anyone to find and install.
Keep it Private until you're ready to share. The Team tab also has an Import from a Paperclip instance option — if you're running Paperclip self-hosted and want to pull an existing agent configuration into Envelope, use this to import the definition directly.
Right panel — AI Assist, Test, History: The right panel stays visible across all tabs and has three modes:
- AI Assist — chat with an AI that has full context of your current team. The panel surfaces contextual suggestions based on what you've built. Say "go ahead" or "apply it" to have the AI rewrite the definition and show you a diff before you commit. Choose the assist model — Claude Sonnet 4.6, Claude Haiku 4.5, or GPT-5.2 — from the Model picker at the top of the panel.
- Test — run a live chat against your team using Envelope's managed runtime without publishing. Use this to verify agent behaviour, check escalation paths, and confirm prompts work as expected before going public.
- History — browse previous saved states of the definition and restore any past version.
Go Deeper — the Go Deeper button in the AI Assist panel is a one-click action that enriches prompts, strengthens role descriptions, and infers any missing secrets across the whole team. Run it once after generating or importing a team to tighten everything up before publishing.
Edit with AI — the Edit with AI button in the editor top bar opens the chat interface with your existing team pre-loaded. Use this when you want to make larger structural changes conversationally — adding a whole new agent tier, reshaping the hierarchy, or rethinking the escalation model — rather than editing fields one at a time.
Platform Fit strip — the platform fit strip at the top of the editor shows which platforms — Envelope, Paperclip, Relevance AI, Bedrock — support your current team configuration in real time. If an agent uses an adapter or model that a platform doesn't support, that platform's badge goes grey. Check this before publishing to make sure deployers on their platform of choice can actually run your team.
Key things to set per agent:
- Name and title — shown in the platform's agent list. Keep names short and titles descriptive.
- Role — a short machine-readable identifier used for routing (e.g.
support,analyst,manager). - Reports to — which agent this one escalates to. Leave blank for top-level agents.
- Adapter and model — the runtime this agent runs on. See the adapters guide for a full breakdown.
- System prompt — your instructions to the agent. This is your IP and is never shown to deployers or end users.
- Access policy — the list of external hosts this agent is permitted to call. Declared per-agent and enforced by the security proxy at runtime. See the access policy guide.
Required variables and secrets are declared at the team level in the Inputs tab — list anything deployers must provide at install time.
Schedule tab — configures how the team fires automatically. Choose a trigger type: Scheduled (cron — pick a cadence and timezone), Event (fires when an inbound webhook arrives), or Condition (polls at an interval and runs only if a plain-English condition check passes). Schedules are stored per-deployment, not in the team definition, so each deployer can configure their own trigger independently. Leave it on No Schedule if the team is triggered on demand. See the scheduling guide →
Distribution settings — only apply when visibility is set to Public. They live at the bottom of the Team tab:
- Billing model — Free by default. Set this before publishing if you plan to charge deployers for access.
- Allow forking — controls whether deployers can copy the team into their own workspace. Off by default for teams you want to keep proprietary.
Once a team is published, the editor becomes read-only. To make changes, click Create new version — this creates a draft that you can edit and publish as the next version without affecting existing installs until deployers choose to upgrade.
Editing JSON directly
Switch to the JSON tab to edit the raw definition. The schema is straightforward:
{
"version": 1,
"description": "...",
"requiredVariables": ["companyName"],
"requiredSecrets": ["ZENDESK_API_KEY"],
"agents": [
{
"key": "support-manager",
"name": "Support Manager",
"title": "Head of Support",
"role": "manager",
"adapterType": "claude_local",
"capabilities": "Triages tickets and escalates complex issues.",
"model": "anthropic:claude-sonnet-4-5",
"prompt": "You are the Support Manager for {{companyName}}...",
"accessPolicy": {
"defaultAction": "deny",
"rules": [
{ "host": "api.zendesk.com", "action": "allow" },
{ "host": "hooks.slack.com", "action": "allow" }
]
}
},
{
"key": "l1-agent",
"name": "L1 Agent",
"title": "First Line Support",
"role": "support",
"adapterType": "claude_local",
"capabilities": "Handles inbound tickets via Zendesk.",
"model": "anthropic:claude-haiku-3-5",
"reportsToKey": "support-manager",
"prompt": "You are the L1 Support Agent for {{companyName}}...",
"accessPolicy": {
"defaultAction": "deny",
"rules": [
{ "host": "api.zendesk.com", "action": "allow" }
]
}
}
]
}The key field is your internal identifier — used in reportsToKey references and must be unique within the team. It is not shown to deployers or end users.
version is an integer. Start at 1 — Envelope increments it automatically on each publish. Do not set it to a string like "v1".
Writing a good agent prompt
The system prompt is the only input you control at runtime — the platform passes it to the model on every invocation. Prompts that work well in practice share four elements:
1. Role — start with who the agent is and what company they're at. Use the {{companyName}} variable so the prompt personalises at deploy time. This grounds the model and reduces generic responses.
You are the Customer Success Lead at {{companyName}}.2. Primary responsibility — state the agent's specific mandate — not a job description, but what they actually do. Be concrete. "Handle tickets" is worse than "receive inbound tickets, categorise by urgency, and resolve simple issues in the same session."
You ensure every customer is onboarded within 48 hours,
maintains a health score above 80, and renews at the end
of their contract term.3. Tool references — if the agent uses an external service, name it in the prompt and reference the secret it runs on. This tells the model when and how to use the integration, and makes the dependency explicit.
Track all customer touchpoints in HubSpot (HUBSPOT_API_KEY).
Send proactive health score summaries to #cs in Slack
(SLACK_BOT_TOKEN).Any secret name referenced in the prompt should also appear in requiredSecrets. Any host the agent calls should be permitted in its accessPolicy.
4. Escalation path — tell the agent when to stop and escalate, and to whom. Without this, agents tend to attempt resolution even when they shouldn't. This is especially important for agents that handle customer-facing or financial decisions.
Escalate to the Chief of Staff when a deal has been
stalled for more than 5 days with no response.
Escalate to the CFO Agent any invoice dispute over $5,000.Designing the org structure
Agents in a team form a hierarchy. Every agent either reports to another agent (via reportsToKey) or is a root-level agent with no parent. Supported platforms use this hierarchy to determine escalation routing at runtime.
A few principles that work well in practice:
- One clear root. Most teams work best with a single top-level agent (a manager or lead) that all others eventually escalate to. Multiple roots can cause ambiguous routing.
- Keep depth shallow. Two or three levels is usually enough. Deep hierarchies add latency and make escalation chains hard to reason about.
- Match the real org. Teams that mirror how the work actually flows — who hands off to whom — tend to behave more predictably than teams designed around model capability.
- Capability descriptions matter. The
capabilitiesfield is shown alongside the agent name in supported platform UIs. It should describe what the agent does in one sentence — not restate the role.
Access policy
Every agent can declare an accessPolicy — a list of hosts it is permitted to call and what to do with everything else. Policies are enforced at runtime by the Envelope security proxy. Requests that don't match any rule are either denied or sent to an AI judge, depending on your configuration.
"accessPolicy": {
"defaultAction": "deny",
"rules": [
{ "match": { "host": "api.hubspot.com" }, "action": "allow" },
{ "match": { "host": "hooks.slack.com" }, "action": "allow" },
{ "match": { "host": "api.stripe.com" }, "action": "require_approval",
"reason": "Financial operations require human sign-off" }
]
}defaultAction:denyorallow. Usedenyunless you have a reason not to.action:allow,deny, orrequire_approval(routes to the AI judge).reason: optional — shown in the security dashboard and used as context by the AI judge.
If an agent has no accessPolicy, all its outbound requests pass through without inspection — visible in the security dashboard as PASSTHROUGH. Best practice is to declare a policy for every agent that makes external calls.
Human gates
Human gates pause a pipeline run and require a human to approve, reject, or request a rerun before work continues. They are declared in the team definition and enforced by the runtime — no separate configuration per deployment.
When to use a gate:
- Before any content leaves the system (outbound email, published posts, customer-facing messages)
- Before financial operations (invoices, charges, refunds)
- At any handoff where a human sign-off is required by policy or compliance
"gates": [
{
"name": "review-outreach",
"type": "content_generation",
"afterStep": "draft-email",
"triggersStep": "send-email",
"fields": ["subject", "body"],
"trigger": "any_approved",
"onReject": "rerun"
}
]Gates appear in the workspace room thread as a review card — anyone in the workspace can see the content, approve or reject it, and the run continues or stops accordingly.
Variables and secrets
Variables and secrets are the two types of input deployers provide at install time. They serve different purposes.
Variables — non-sensitive configuration values interpolated into agent prompts at install time. Declared in requiredVariables and referenced in prompts as {{companyName}}. Examples: companyName, supportEmail, launchDate.
Secrets — credentials for external services. Declared in requiredSecrets and stored securely at install time. For local adapters they are injected as environment variables into agent processes. For managed agents, they are substituted via ${SECRET_NAME} in URLs and headers. Examples: ZENDESK_API_KEY, SLACK_BOT_TOKEN.
Only declare what you actually use. Every entry in requiredSecrets is a blocker — the install will fail if the deployer doesn't supply it. Unnecessary secrets add friction and may cause deployers to abandon the install.
Important — never hardcode a real credential into a prompt or capability description. Use a secret name as a placeholder (e.g.
ZENDESK_API_KEY) and declare it inrequiredSecrets. Envelope saves an encrypted copy of secrets at install time — on re-install, Envelope pre-fills any secrets it already knows so deployers only need to re-enter credentials that are new or have changed.
Workspace documents
A workspace document is a persistent, structured table scoped to a team install. It accumulates state across every run — the agent writes to it autonomously, and the human can add notes, mark rows as blocked, or correct values directly from the workspace panel. Documents outlive individual runs and carry state forward across sessions.
Declare a workspace document in your team definition alongside schedule and pipeline:
{
"workspace": [
{
"name": "outreach-list",
"columns": [
{ "name": "company", "owner": "human", "id": true },
{ "name": "email", "owner": "agent" },
{ "name": "email-status", "owner": "agent" },
{ "name": "sent-at", "owner": "agent" }
],
"statusValues": ["unverified", "verified", "needs-lookup", "sent", "replied", "bounced"],
"triggers": [
{ "column": "email-status", "status": "unverified", "action": "run" }
]
}
]
}| Field | Description |
|---|---|
| name | Unique name for this document within the install. Used in document_read and document_write tool calls. |
| columns | Declared columns with ownership. owner: "agent" columns are written by the agent; owner: "human" columns are written by the human. Set id: true on the unique identifier column. |
| statusValues | Valid values for status columns — enforced at runtime. The agent cannot write an undeclared status value. |
| triggers | Condition triggers that fire based on document row state. action: "run" fires the full team; action: "notify" sends a notification without running. |
Each install can declare multiple documents — for example, a separate contact list and send log. Documents are created automatically at install time, pre-populated with the declared schema. The human never sees a blank page.
Two fields are always available on every row, regardless of schema: notes (human-owned, free-text context the agent reads and factors in but never overwrites) and blocked-by (human-authored dependency hold — the agent skips the row entirely while this field is non-empty).
At runtime, agents access declared documents via document_read and document_write tool calls. Write-back — updating the document after completing an action on a row — is a hard requirement, not optional. The document is the product the deployer sees.
Choosing adapters
The adapter determines the runtime your agent uses — Envelope Managed, Paperclip (self-hosted), Relevance AI, Bedrock, and more. Different agents in the same team can use different adapters. The definition stays the same regardless of which adapter runs it.
The default adapter is claude_local — the agent runs using Claude with a local agentic loop. Other local adapters include codex_local and gemini_local for Google models. Use the http adapter when you want Envelope's managed runtime — Envelope runs the model with an agentic loop and the http_request tool on your behalf, so deployers need zero infrastructure. The http adapter also supports a custom webhook mode where the deployer brings their own endpoint.
Before you publish
A team starts as a private draft. Before making it public, run through this checklist:
- Every agent has a name, title, role, adapter, and non-empty prompt.
- At least one agent has no reportsToKey (the root of the hierarchy).
- Every secret referenced in a prompt is listed in requiredSecrets.
- Every variable used in a prompt is listed in requiredVariables.
- Every agent that makes external calls has an accessPolicy declared. Agents without one show as PASSTHROUGH in the security dashboard.
- Any step that requires human approval before proceeding has a humanGate declared.
- The Platform Fit strip shows all target platforms as supported — if any badge is grey, that platform cannot run your team as configured.
- The team description explains what the team does in 1–2 sentences — this is what deployers see in the Library.
- You have run a Test Chat — click the Test button in the editor right panel to open a live chat against your team's agents using Envelope's managed runtime.
- If you plan to make the team public, Distribution settings (billing model and forking) are configured in the Team tab before publishing.
When you're ready, set visibility to Public and publish. Deployers will be able to find and install the team immediately. See the versioning guide for how to update a team after it's live without breaking existing installs.
Publishing and ownership
Understanding who owns what matters once teams are in the Library.
When you publish a team, it appears in the community Library under your handle. You remain the owner regardless of how many people install it.
When someone installs a free team (forking enabled), Envelope creates a fork — a full copy of the team under their handle, set to private. They own their copy independently. You can't edit their fork and they can't edit yours. You receive an email when someone forks your team.
When someone installs a priced team (forking disabled), Envelope creates a direct install pointing at your original template. You stay the owner. Every run on their install generates revenue for you. They can run the team and configure credentials, but they cannot modify the agents, prompts, or structure.
Revenue flows through your connected Stripe account (set up under Account → Payouts). Envelope processes the per-run charge on each execution and transfers your share automatically. You'll receive an email on your first payout.
Forking is automatically disabled when you set a price. A priced team is proprietary by definition — if it could be forked, anyone could get a free copy and bypass the per-run charge. You can re-enable forking by setting the billing model back to Free.
Forks inherit the source's pricing model at fork time but start as private drafts. The creator of the fork controls whether to reprice or publish their copy independently.