Docs

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 (the admin role default and the MCP customer-agent bundle both carry it); artifacts:read for the read + introspection endpoints. End state: a tenant-owned blueprint authored over REST, gated through the same validation moat the MCP blueprints coarse 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 + pathOpPurpose
GET /agent-blueprintsagent_blueprint.listCatalog every blueprint visible to the tenant (code-defined starters merged with tenant rows).
GET /agent-blueprints/:idagent_blueprint.getFetch one blueprint's full v1 DSL body (accepts a tenant UUID or a starter slug / deterministic UUID).
POST /agent-blueprints/validateblueprints.validateDry-run the moat against a draft body without writing it.
POST /agent-blueprintsblueprints.createCreate a new tenant blueprint.
PATCH /agent-blueprints/:idblueprints.updatePatch a tenant blueprint (content is replace-semantic; top-level fields patch the row).
DELETE /agent-blueprints/:idblueprints.deleteSoft-delete (archive).
POST /agent-blueprints/:id/unarchiveblueprints.unarchiveRestore 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_ENABLED flag (enabled by default; disable with BLUEPRINTS_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:

  1. Aggregated moat validation. Beyond the Zod schema parse, the moat checks six invariants — duplicate step ids, unresolved $step.field references, unknown prompt:// 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 one validation_failed envelope. The generic artifact path surfaces a single registry rejection.
  2. A dry-run. POST /agent-blueprints/validate runs 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

http
GET /api/platform/v1/step-kinds
GET /api/platform/v1/prompt-fragments?scheme=content_writer

step-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

http
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

http
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

http
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:

  • content is replace-semantic — pass the full new content_json; omitted top-level keys are dropped (the legacy merge semantic was retired because it bricked schema-tightening migrations).
  • Top-level fields patch the rowtitle, description, status, tags, metadata patch the artifact's columns independently of content_json, so you can flip status without resending the recipe. The agent_blueprint state machine (draft → published → archived) gates status transitions; an illegal jump returns a validation_failed envelope.

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:

json
{
  "success": true,
  "blueprint": {
    "header": {
      /* lean catalog projection */
    },
    "content": {
      /* full DSL */
    }
  }
}
json
{
  "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