Building a team (API)
How to write a team definition file, publish it to the Library, and manage it entirely via the API — no editor required.
Authenticate every request with your API key:
Authorization: Bearer <your-api-key>All endpoints are prefixed with /api. The base URL is your Envelope instance origin (e.g. https://openenvelope.org/api).
1. Write the definition
A team definition is a JSON object describing the team's name, agents, required secrets, required variables, access policy, and pricing. The full schema is published at:
https://schema.openenvelope.org/team/v1.jsonAdd a $schema reference to your file and your editor validates against it automatically.
Minimal valid definition:
{
"$schema": "https://schema.openenvelope.org/team/v1.json",
"name": "Support Triage",
"description": "Routes inbound support tickets to the correct queue based on urgency and topic.",
"agents": [
{
"key": "coordinator",
"name": "Triage Coordinator",
"title": "Coordinator",
"role": "Receives incoming ticket text and routes it to the correct specialist.",
"model": "claude-3-5-sonnet-20241022",
"systemPrompt": "You are a support triage coordinator. Analyse the ticket and output a routing decision."
}
],
"requiredSecrets": ["ZENDESK_API_KEY"],
"requiredVariables": ["QUEUE_EMAIL"]
}Agent hierarchy
Connect agents with reportsToKey. The agent with no reportsToKey is the coordinator — all others report up through the hierarchy.
{
"agents": [
{ "key": "coordinator", "name": "Coordinator" },
{ "key": "specialist-a", "name": "Tier 1 Specialist", "reportsToKey": "coordinator" },
{ "key": "specialist-b", "name": "Escalation Specialist", "reportsToKey": "coordinator" }
]
}Keep hierarchies shallow. One coordinator with 2–4 specialists is the most common pattern. Deep hierarchies add latency and complicate debugging.
Access policy
Declare which outbound hosts each agent may call. Deployers and their security teams can review this in the Library before installing. An explicit policy — even a permissive one — builds more trust than no policy.
{
"agents": [
{
"key": "coordinator",
"accessPolicy": {
"default": "deny",
"rules": [
{ "host": "api.zendesk.com", "methods": ["GET", "POST"], "effect": "allow" }
]
}
}
]
}Adapter types
By default Envelope chooses the adapter. Override per-agent with adapterType:
{
"agents": [
{ "key": "coordinator", "adapterType": "claude_local" }
]
}Supported values: claude_local, codex_local, gemini_local, opencode_local, cursor_local, openclaw, process, http.
2. Create a draft
POST /templates{
"slug": "support-triage",
"name": "Support Triage",
"description": "Routes inbound support tickets to the correct queue based on urgency and topic.",
"visibility": "private",
"category": "support",
"definition": { ... }
}| Field | Required | Description |
|---|---|---|
| slug | Yes | URL-safe identifier. Must be globally unique. Min 2 chars. |
| name | Yes | Display name shown in the Library. Min 2 chars. |
| description | Yes | Short summary shown in search results. Min 10 chars. |
| visibility | No | "private" (default), "team", or "public" |
| category | No | Functional category, e.g. "support", "sales", "ops" |
| targetPlatform | No | Platform the team is optimised for, e.g. "paperclip", "bedrock" |
| definition | Yes | Full team definition object (see above) |
Response: 201 Created — returns the full template object including the assigned id. Store this id — you'll use it in every subsequent call for this team.
A slug collision returns 409 Conflict. Choose a different slug.
3. Update the draft
PATCH /templates/:id{
"description": "Routes tickets and escalates P1s automatically.",
"definition": { ... }
}Only draft status templates can be patched. Attempting to patch a published template returns 409. Create a version draft first — see Versioning your team.
Updatable fields: name, description, visibility, pricingModel, priceUsdCents, forkable, changeLog, definition.
4. Set pricing
Update via PATCH /templates/:id:
{
"pricingModel": "per_run",
"priceUsdCents": 50
}| pricingModel | Description |
|---|---|
| "free" | No charge to deployers |
| "per_run" | Fixed charge per run trigger |
| "per_k_tokens" | Charge per 1,000 tokens consumed across the run |
priceUsdCents is the amount in US cents charged to deployers. Envelope's platform fee is deducted automatically before payout. See Pricing your team for fee details and payout mechanics.
5. Publish
POST /templates/:id/publish{
"changeLog": "Initial release. Routes tickets by urgency using keyword detection."
}Always supply a changeLog string. It is shown to deployers in the Library and in their install's version history — a blank changelog makes upgrade decisions harder for everyone who installs your team.
Response: Returns the updated template with status: "published" and a version number. The team is now visible in the Library and installable by ID or slug.
Envelope validates your definition against the JSON Schema on publish. A 400 error includes specific field-level messages. Fix those and re-publish.
6. List your teams
GET /templates/allReturns all templates owned by your API key — drafts, published, and archived — ordered newest first.
7. Get a team
GET /templates/:idReturns the full template including definition and system prompts. Other callers receive a stripped version without prompts.
8. Workspace document endpoints (deployer-scoped)
Workspace documents are scoped to installs, not templates. They are accessible to the install owner via these routes. All routes require authentication (Authorization: Bearer <api-key>).
List documents
GET /installs/:installId/documentsReturns all documents for the install with their lifecycle state and timestamps.
Read a document
GET /installs/:installId/documents/:nameReturns the full document including all rows, schema, and per-row metadata (notes, blocked-by, last-changed-by).
Create / ensure a document
POST /installs/:installId/documents/:name{
"schema": {
"columns": [
{ "name": "company", "owner": "human", "id": true },
{ "name": "email", "owner": "agent" },
{ "name": "email-status", "owner": "agent" }
],
"statusValues": ["unverified", "verified", "sent"]
}
}Idempotent — returns the existing document ID if the document already exists. Documents declared in the team definition's workspace block are created automatically at install time.
Append rows
POST /installs/:installId/documents/:name/rows{ "rows": [{ "company": "Acme", "email-status": "unverified" }] }Edit a row (human)
PATCH /installs/:installId/documents/:name/rows/:rowId{ "update": { "email": "[email protected]" }, "notes": "Confirmed via LinkedIn", "blockedBy": null }Delete a row (hard delete for PII compliance)
DELETE /installs/:installId/documents/:name/rows/:rowIdPhysically removes the row — no soft-delete flag. Required for GDPR erasure and any PII compliance obligation.
Export
GET /installs/:installId/documents/:name/export?format=csv
GET /installs/:installId/documents/:name/export?format=jsonReturns the document as a file download. format defaults to json.
Lifecycle transition
POST /installs/:installId/documents/:name/lifecycle{ "state": "archived" }Valid states: new, active, complete, archived, deleted.
Version history
GET /installs/:installId/documents/:name/snapshotsReturns all named snapshots (one per agent write batch). Each snapshot has an id, label, agentRunId, and createdAt.
Revert to snapshot
POST /installs/:installId/documents/:name/snapshots/:snapshotId/revertRestores the document to the state at that snapshot. The agent's last write is the most recent snapshot.
Common mistakes
Not validating the definition before publishing — run the JSON Schema validator locally first. Envelope validates on publish and returns specific field errors, but catching them locally is faster.
No access policy — teams with no policy look opaque to deployers who need to justify network access to their security teams. Even a permissive "default": "allow" policy is better than nothing.
Blank changelog — every release your deployers track needs context to decide when to upgrade. Write something meaningful even for small changes.
Wrong version bump for breaking changes — removing a field, renaming an agent key, or making an optional input required are all breaking changes. When in doubt, bump MAJOR. See Versioning your team.