Docs

Workflows

A workflow is a reusable recipe you can run headlessly, schedule on a cron, backtest over history, or hand to an agent to walk step by step.

A workflow is a reusable recipe for getting something done, research a report, draft a piece, plan a content window. You fork a starter and customize the steps. A workflow is a recipe in a small step DSL; you can run it two ways, and both coexist.

How a workflow runs

There are two ways to run a workflow:

  1. Headless (the platform runs it). Trigger a run and the platform executes the recipe for you on the Claude Agent SDK, with no LLM in your request loop. This is what powers Run now, scheduled (cron) runs, and backtests. Each run is tracked as a blueprint_run you can poll for status.
  2. Interactive (an agent walks it). An LLM, the in-app chat copilot or your own agent over the API or MCP, reads the rendered recipe (agent_blueprint://<id>) and walks it step by step, making the tool calls itself. The recipe is the plan; the agent is the runner. Good for ad-hoc, in-conversation runs.

Triggers that ship today are manual (a person clicks Run now, or an agent picks up the recipe) and schedule (a cron fires a headless run). Event and webhook triggers are on the roadmap.

Step kinds

A workflow is a graph of steps. There are 8 step kinds:

  • tool call a platform operation with arguments.
  • llm a prompt at an effort tier (fast / medium / deep / research), mapped to a model per tenant.
  • loop iterate over a collection; optionally in parallel.
  • branch if / else on a structured condition (eq / neq / gt / gte / lt / lte / in / is_null, combined with and / or / not).
  • parallel run child steps concurrently and merge their results (object or array).
  • blueprint run another workflow as a sub-step.
  • transform reshape data with a JSONata expression. No LLM, no tool call, pure data.
  • assert halt the run if a condition is false.

Sending email to workspace members

A workflow can email people on your team. There's no new step kind for it, it's a plain tool step that calls the notifications.email_member operation. The DSL already treats email_sent and notification as side-effect categories; this is the sender behind them.

It runs autonomously. There's no human confirmation step in the loop, so a workflow can notify a teammate as part of getting the work done.

Sending well is a small loop: look up who you're allowed to email, send, then confirm it landed. notifications.list_recipients -> notifications.email_member -> notifications.list_sends. The two reads bracket the send so the step picks a real recipient up front and checks the result (and its own remaining cap) at the end, both gated by the notifications:read scope.

Picking a recipient

Before sending, look up who it can email with notifications.list_recipients. It returns the workspace's addressable members, each a { user_id, email, display_name }, so the step chooses a member email that email_member will actually accept, instead of guessing an address. This is a narrow recipient picker, not a full team roster: it answers "who can I email?" and nothing more.

  • Over MCP. Read the notification_recipient://list resource.
  • Over the API. GET /notifications/recipients.

Confirming a send landed (and staying under cap)

After sending, notifications.list_sends returns recent sends so you can confirm the email landed. Narrow the history with limit, recipient_email, blueprint_run_id, and since filters, for example, fetch the sends for the current run to verify each teammate was notified exactly once.

Each response also carries a summary of { sent_last_hour, cap_per_hour, cap_per_run }. An autonomous run reads this before a send to self-check it's under the per-hour and per-run caps, so it backs off on its own rather than firing into a rejection.

  • Over MCP. Read the notification_sends://list resource.
  • Over the API. GET /notifications/sends.

Members only, no external addresses

The recipient must be a current member of the workspace, identified by their member email. Free-text or external email addresses are rejected, you cannot use this to mail someone outside the workspace. If a member is removed, they stop being a valid recipient.

Caps and idempotency

Sends are bounded by a per-business and per-run cap, so a runaway workflow can't blast your team, the same caps the summary on notifications.list_sends reports, so a run can see how much headroom it has left before it sends. Sends are also idempotent, a retried step won't deliver the same email twice.

Where it's available

  • Over the API. notifications.email_member is a unified operation, so it's callable over the REST / HTTP API like any other operation (find it in the /api/platform/v1/operations catalog). The two reads sit alongside it at GET /notifications/recipients and GET /notifications/sends.
  • From an MCP agent. It's exposed as a notifications coarse tool with an email_member action, so MCP clients (Claude Desktop, Cursor) can call it too. The recipient picker and send history are reachable as the notification_recipient://list and notification_sends://list resources.
  • From a workflow. Add a tool step that calls notifications.email_member.

Sending is gated by the notifications:write scope, which is on the MCP customer-agent bundle and the admin role tier; the recipient and send-history reads are gated by notifications:read.

Because the platform runs scheduled workflows headlessly, unattended scheduled email works end to end, a cron fires the run, the run's email step sends, no human or interactive agent in the loop. The headless runner is the runner.

Starters

Amdahl ships 8 starter workflows, battle-tested recipes you fork into your workspace and customize:

  • bootstrap_workspace get a fresh workspace ready.
  • draft_piece research and draft a single content piece.
  • plan_and_draft_window plan a content window and draft across it.
  • multi_persona_social_launch a coordinated social launch across personas.
  • research_report produce a researched, cited report.
  • gtm_diagnostic a living go-to-market health report for your workspace.
  • substack_thought_leader_newsletter a thought-leadership newsletter.
  • viz_page_from_query build and publish a Page from a single question.

You fork a starter to customize it, forks land in your workspace as a fresh draft you own. There's no in-app step editor; authoring is done by handing the recipe off to an LLM (in chat or over MCP) that writes and validates the workflow for you.

In the app

Open Workflows from the sidebar. The list has two tabs: Your workflows (the ones your workspace owns) and Library (the Amdahl starters), each with a Fork button. Open any workflow to see a read-only canvas of its step graph, the tool, llm, branch, and loop steps laid out so you can read the recipe end to end, plus its Runs and Schedule tabs.

Over the API

Authenticate with the X-API-Key header (see Using the API). Not sure of the exact route or input shape? Call /api/platform/v1/operations, it returns the catalog of every operation your key can call, each with its JSON schema.

Author a workflow

Create, update, and delete recipes under /agent-blueprints, fork a starter, and dry-run-validate a body before you save it.

  • GET /agent-blueprints / GET /agent-blueprints/:id browse and read recipes.
  • POST /agent-blueprints create a workflow.
  • PATCH /agent-blueprints/:id update one.
  • DELETE /agent-blueprints/:id archive one.
  • POST /agent-blueprints/:id/unarchive restore one.
  • POST /agent-blueprints/fork fork a starter into your workspace.
  • POST /agent-blueprints/validate validate a body without saving (catches bad step references, cycles, and tool-allowlist typos).
bash
# Validate a workflow body before saving it
curl https://app.amdahl.co/api/platform/v1/agent-blueprints/validate \
  -H "X-API-Key: amdahl_sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "identity": { "slug": "weekly-digest" }, "steps": [] }'

Run now

Fire a one-shot headless run. The call returns immediately with a blueprint_run_id and status: "queued"; the platform executes the recipe out of process and you poll the run for progress. Gated by the workflows:delegate scope.

  • POST /agent-blueprints/:id/run with an optional { inputs, as_of } body. as_of (an ISO date) makes the run read the data as of that date.
  • GET /blueprint-runs/:id poll one run, GET /blueprint-runs list runs (filter by blueprint_id + status). Status moves queued -> running -> complete | error | canceled; the row carries step states, the current step, tokens used, and an error message on failure.
bash
# Run a workflow now, then poll the run
RUN=$(curl -s https://app.amdahl.co/api/platform/v1/agent-blueprints/$WORKFLOW_ID/run \
  -H "X-API-Key: amdahl_sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "inputs": {} }' | jq -r .blueprint_run_id)

curl https://app.amdahl.co/api/platform/v1/blueprint-runs/$RUN \
  -H "X-API-Key: amdahl_sk_live_your_key_here"

Schedule (cron)

Register a cron schedule and the platform fires a headless run on that cadence (triggered_by_kind: "schedule"); each fire shows up in run history. Writes are gated by workflows:delegate, reads by workflows:read.

  • POST /agent-blueprints/:id/schedules create a schedule.
  • GET /agent-blueprints/:id/schedules / GET /agent-blueprints/:id/schedules/:scheduleId list and read.
  • PATCH /agent-blueprints/:id/schedules/:scheduleId update (including enable / disable).
  • DELETE /agent-blueprints/:id/schedules/:scheduleId remove.

Backtest

Run the recipe across a date series using as-of time-travel, to see how it would have performed historically. Preview first (free, no execution), then start the sweep. Gated by workflows:delegate.

  • POST /agent-blueprints/:id/backtest/preview with { start, end, cadence } returns the dates it would run and the point count, without firing anything.
  • POST /agent-blueprints/:id/backtest with { start, end, cadence, cumulative?, inputs? } fires the sweep (one run per date) and returns a backtest id.
  • GET /blueprint-backtests/:id / GET /blueprint-backtests read backtest status and history.

From an MCP agent

Over MCP, workflows are reachable through the blueprints coarse tool, for authoring and discovery. Its actions:

  • list / get browse and read recipes (starters and your own).
  • validate dry-run a body.
  • describe_step_kinds / list_prompt_fragments introspect what the DSL offers.
  • create / update / delete / unarchive author recipes.
  • fork fork a starter into the workspace.

You can also read a recipe directly via the agent_blueprint:// resource scheme, and read run history via blueprint_run://.

The tool also carries create_schedule / update_schedule / delete_schedule actions, but they require the workflows:delegate scope, which standard customer MCP keys don't carry. So one-shot Run now, scheduling, and backtests run over REST + the Anthropic tool surface, not a typical MCP key. To run a workflow on demand from an MCP client, read agent_blueprint://<id> and walk the steps yourself, that's the interactive path, and it's a perfectly good way to run a workflow on the spot.