Docs

Build your first workflow — tutorial

Audience: a tenant employee or external Claude session that wants to ship a custom recipe in 30 minutes. Prerequisites: an Amdahl API key with artifact:write scope; the Amdahl MCP server connected to your client. End state: a working workflow forked from content-calendar, customized to your team's pillars and pacing, ready to read and walk from MCP.

Workflows run two ways. (a) Interactive — an LLM reads the recipe (agent_blueprint://<id>) and walks the steps itself; that's the MCP path this tutorial's audience uses. (b) Headless — the platform runs the workflow for you (how a workflow runs). Over MCP the blueprints coarse tool covers authoring + discovery; it also carries schedule actions, but they need the workflows:delegate scope that standard customer keys don't carry, so Run now / scheduling / backtests run over REST + the Anthropic tool surface (Workflows over the API). To run a workflow on demand from an MCP client, walk the recipe yourself. Workflows are written in a typed DSL (reference) and stored as agent_blueprint artifacts. The platform ships several forkable starters; this tutorial walks through forking the content-calendar starter, customizing it for your team, and walking the customized recipe yourself.

Step 1 — Look at the starter

The Amdahl-shipped starters are public. Read the calendar starter to see what you're starting from:

text
read_resource artifact://<content-calendar-blueprint-id>

If you don't know the id off-hand, list public agent_blueprint artifacts:

text
read_resource agent_blueprint://list?slug=content-calendar

You'll see the validated DSL. Pay attention to:

  • identity.slug + version — you'll bump these when you fork.
  • inputs — the surface customers fill in to run the workflow.
  • policy.tool_allowlist — the operations the workflow's tool steps will call.
  • steps — the recipe body. The steps run in order; the planning workflow emits placeholder content_piece rows, then plan_and_draft_window (the composition starter) fans out draft_piece per planned piece in parallel.

Step 2 — Decide what to change

Common forks for plan_and_draft_window:

  1. Lock the channel — your team only publishes to LinkedIn; remove the channel input + hardcode 'linkedin' everywhere.
  2. Pin the audience lens — your ICP is fixed; remove the input + hardcode the audience filter.
  3. Tighten the cadence — replace prompt://content_writer/cadence_rhythm with your own fragment if your audience has unusual posting times.
  4. Pillar-locked — bake the team's pillar set into the workflow and remove the optional input for fixed-pillar teams.

For this tutorial we'll do #1 — lock to LinkedIn.

Step 3 — Fork the starter

Forking is a single op: agents.fork_blueprint reads the starter, creates a tenant-owned copy, and returns the new artifact id. The fork starts at version 1.0.0, status draft, visibility private — you customize from there.

text
agents.fork_blueprint
  source: 'content-calendar'           # starter slug, OR a starter UUID, OR a tenant blueprint UUID
  new_slug: 'team-x-linkedin-calendar' # optional; auto-generates `<source>-fork` when omitted
  new_name: 'Team X LinkedIn Calendar' # optional; defaults to `Copy of <source name>`

Returns:

jsonc
{
  "artifact_id": "<new-uuid>",
  "slug": "team-x-linkedin-calendar",
  "name": "Team X LinkedIn Calendar",
  "source_kind": "starter",
}

Once the fork lands, customize it via artifacts.update with the patches you want — drop the channel input, hardcode 'linkedin' in the steps that referenced $inputs.channel, etc. The fork's content_json is a full copy of the starter; you patch only the fields you care about.

text
artifacts.update
  id: '<new-uuid>'
  content_json:
    inputs:
      # Drop the `channel` input entirely; keep the rest.
      - { name: 'voice_profile_id', type: 'artifact_ref', required: true, validate: { artifact_type: 'voice_profile' } }
      - { name: 'period_start', type: 'date', required: true }
      - { name: 'period_days', type: 'integer', default: 7, validate: { min: 3, max: 14 } }
      - { name: 'piece_count', type: 'integer', default: 5 }
      - { name: 'audience', type: 'string' }
      - { name: 'pillar_ids', type: 'artifact_ref_list', validate: { artifact_type: 'content_pillar' } }
    steps:
      # Replace `$inputs.channel` with the literal `'linkedin'`
      # everywhere the starter referenced it.
      # ... (omitted for brevity; copy from the starter and edit)

The fork now reflects your customization. Promote when ready via another artifacts.update with status: 'published'.

Step 4 — Walk your fork

You walk the fork yourself (the interactive path). Read the recipe back:

text
read_resource agent_blueprint://<your-fork-id>

You get the inputs, the policy, and the step tree. Gather the inputs you'd supply:

text
voice_profile_id: '<your voice profile uuid>'
period_start: '2026-04-29'
period_days: 7
piece_count: 5
audience: 'B2B operators at Series B+ companies'
pillar_ids: ['<pillar-1>', '<pillar-2>', '<pillar-3>']

Then perform each step in order with your own primitive tools — substituting $inputs.* with the values above and each $step_id with the output of the step you already ran. A tool step names the operation to call (data.cluster_search, artifacts.create, etc.); an llm step is reasoning you do yourself; loop / branch / parallel steps tell you how to fan out. (Headless runs — Run now, scheduling, backtest — execute on the platform, set up over REST + the Anthropic tool surface, not from your MCP session; see Workflows over the API.)

Step 5 — You're done when the steps are done

When you walk a workflow yourself there's no run row to close — when you've walked every step and written the artifacts the recipe produces (the calendar, the pieces, the drafts), the work is complete. (A headless platform run does record a row; see Run history.) Capture the artifact ids you created as you go; the recipe's outputs_mapping tells you which step output maps to which named output.

Step 6 — Iterate

Once you've walked it once and the output matches your taste, you're done. Common iteration patterns:

  • Bump version to 1.1.0 when you tweak the prompt language or add a step. The version is authoring metadata on identity.version; the next agent that reads the recipe picks up the new body.
  • Re-read the recipe (read_resource agent_blueprint://<your-fork-id>) any time to confirm a change landed before walking it again.
  • Note on unattended use — the trigger.schedule field declares cron intent in the DSL, and the platform runs scheduled workflows for you headlessly. You set that up over REST + the Anthropic tool surface (blueprint_schedule.*; see Workflows over the API) — not from a standard MCP customer key, whose schedule actions need the workflows:delegate scope it doesn't carry. From an MCP session, for ad-hoc recurring use just walk the recipe yourself (or wrap it in your own scheduled job that does).

What this recipe does and doesn't constrain

The recipe's policy.tool_allowlist is GUIDANCE you should respect, not a server-enforced contract. The API key's scope grammar is the real safety boundary. You may adapt mid-walk when an unanticipated case calls for a different tool.

This means: if a workflow declares a tight allowlist, follow it; if the API key has broader scope, you have the headroom to adapt. The workflow declares intent; the API key enforces it. The headless runner does not enforce the allowlist for you either — it runs against a least-privilege scoped token, so the API key's scope grammar, not the allowlist, is the real boundary on every surface.

What's next