Author blueprints over the REST API
Audience: a console integration or a server-side script that wants to read, validate, and persist agent blueprints over plain HTTP — the same surface the Amdahl Console's blueprint pages use. Prerequisites: an authenticated session (Bearer JWT) or a platform API key whose scopes include
versioning:write(theadminrole default and the MCP customer-agent bundle both carry it);artifacts:readfor the read + introspection endpoints. End state: a tenant-owned blueprint authored over REST, gated through the same validation moat the MCPblueprintscoarse tool and the Anthropic tool surface apply.
This guide is the REST sibling of authoring-via-mcp.md. The authoring actions are one unified blueprints.* operation family, so they auto-project to REST, the Anthropic tool surface, and the MCP coarse tool from a single registry definition — the validation moat and write semantics are identical on every protocol. Use REST when you are wiring the Console, a CI job, or a backend service rather than driving a chat session.
Endpoints
The authoring ops project to these routes (all under the platform API prefix, e.g. /api/platform/v1):
| Method + path | Op | Purpose |
|---|---|---|
GET /agent-blueprints | agent_blueprint.list | Catalog every blueprint visible to the tenant (code-defined starters merged with tenant rows). |
GET /agent-blueprints/:id | agent_blueprint.get | Fetch one blueprint's full v1 DSL body (accepts a tenant UUID or a starter slug / deterministic UUID). |
POST /agent-blueprints/validate | blueprints.validate | Dry-run the moat against a draft body without writing it. |
POST /agent-blueprints | blueprints.create | Create a new tenant blueprint. |
PATCH /agent-blueprints/:id | blueprints.update | Patch a tenant blueprint (content is replace-semantic; top-level fields patch the row). |
DELETE /agent-blueprints/:id | blueprints.delete | Soft-delete (archive). |
POST /agent-blueprints/:id/unarchive | blueprints.unarchive | Restore an archived blueprint. |
The read + introspection schemes (step_kind://, trigger_kind://, prompt://) are also REST-mounted (GET /step-kinds, GET /trigger-kinds, GET /prompt-fragments) so you can pull the step-kind grammar and the reusable prompt-fragment catalog before composing a body.
Kill-switch. The blueprint REST routes ride the
BLUEPRINTS_ENABLEDflag (enabled by default; disable withBLUEPRINTS_ENABLED=false). When the feature is off these routes 404 — the ops stay registered, so flipping the flag back restores them with no code change.
Why go through blueprints.* rather than artifacts.*
Blueprints land as platform_artifacts rows with artifact_type = 'agent_blueprint', so a generic POST /artifacts would technically write one. The blueprints.* path adds two things that generic artifact writes do not:
- Aggregated moat validation. Beyond the Zod schema parse, the moat checks six invariants — duplicate step ids, unresolved
$step.fieldreferences, unknownprompt://fragments, sub-blueprint slugs that don't resolve, sub-blueprint cycles, and malformed tool ids in the policy allowlist — and returns the full error list in onevalidation_failedenvelope. The generic artifact path surfaces a single registry rejection. - A dry-run.
POST /agent-blueprints/validateruns the moat without writing, so a console form can show field-level errors before the user commits.
Authoring loop
The REST loop mirrors the MCP one: orient → draft → validate → create → iterate.
1. Orient on the grammar
GET /api/platform/v1/step-kinds
GET /api/platform/v1/prompt-fragments?scheme=content_writerstep-kinds returns the v1 step grammar (the kinds tool, llm, loop, branch, parallel, blueprint, transform, assert) with example bodies. prompt-fragments returns the reusable fragment catalog; pass ?scheme= to scope it. Every $-prefixed reference in a body must satisfy a resolver ($inputs.X, $step_id.field, $now, $today, $random_uuid, $secret.X, $builtin.X) or the moat rejects it.
2. Dry-run a draft
POST /api/platform/v1/agent-blueprints/validate
Content-Type: application/json
{ "content": { /* the v1 DSL body */ } }Response shape: { valid: boolean, errors: ValidationError[] }. Each error carries a code discriminator (schema_error, duplicate_step_id, unknown_reference, unknown_fragment, unresolved_sub_blueprint, circular_sub_blueprint, invalid_tool_id) plus a structured details blob you can map to a form field. See authoring-via-mcp.md for an annotated example body — the body is byte-identical across protocols.
3. Create
POST /api/platform/v1/agent-blueprints
Content-Type: application/json
{ "content": { /* the body */ }, "status": "draft" }The default status is draft. The response is the standard write envelope (below); save the returned blueprint id to update it later, to hand to an interactive agent that will read and walk the recipe itself (agent_blueprint://<id> — there is no run-blueprint tool on that surface, the agent IS the runner), or to drive the headless SDK runner for unattended / scheduled / backtest runs (see blueprint-runner-sdk.md).
4. Iterate
PATCH /api/platform/v1/agent-blueprints/:id
Content-Type: application/json
{ "content": { /* the WHOLE new body */ }, "change_summary": "Tightened the hook step" }Two notes carried over from the artifact registry:
contentis replace-semantic — pass the full newcontent_json; omitted top-level keys are dropped (the legacy merge semantic was retired because it bricked schema-tightening migrations).- Top-level fields patch the row —
title,description,status,tags,metadatapatch the artifact's columns independently ofcontent_json, so you can flipstatuswithout resending the recipe. Theagent_blueprintstate machine (draft → published → archived) gatesstatustransitions; an illegal jump returns avalidation_failedenvelope.
Lifecycle — archive + restore
DELETE is a soft archive (the row stays with archived_at set); POST .../unarchive restores it. Archived rows are excluded from GET /agent-blueprints unless you pass ?include_archived=true. Hard delete is an admin / GDPR path and is not part of this surface — use the generic DELETE /artifacts/:id if you truly need the row gone.
Write envelope reference
Every write op (create / update / delete / unarchive) returns one of:
{
"success": true,
"blueprint": {
"header": {
/* lean catalog projection */
},
"content": {
/* full DSL */
}
}
}{
"success": false,
"error": {
"code": "invalid_argument" | "not_found" | "slug_conflict" | "validation_failed" | "unauthorized" | "internal_error",
"message": "...",
"details": { /* structured, optional */ }
}
}slug_conflict carries details = { field: 'identity.slug', slug, conflict_artifact_id }. validation_failed carries details.errors[] — the same ValidationError[] shape validate returns.
Related docs
- Author blueprints from MCP — the same loop from a Claude session connected over MCP.
- Blueprint DSL reference (auto-generated) — the canonical Zod schema dump for every field on every step kind.
- Tutorial — fork a starter — the fork-first path from the in-app copilot.