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:
- 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_runyou can poll for status. - 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:
toolcall a platform operation with arguments.llma prompt at an effort tier (fast/medium/deep/research), mapped to a model per tenant.loopiterate over a collection; optionally in parallel.branchif / else on a structured condition (eq/neq/gt/gte/lt/lte/in/is_null, combined withand/or/not).parallelrun child steps concurrently and merge their results (objectorarray).blueprintrun another workflow as a sub-step.transformreshape data with a JSONata expression. No LLM, no tool call, pure data.asserthalt 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://listresource. - 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://listresource. - 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_memberis a unified operation, so it's callable over the REST / HTTP API like any other operation (find it in the/api/platform/v1/operationscatalog). The two reads sit alongside it atGET /notifications/recipientsandGET /notifications/sends. - From an MCP agent. It's exposed as a
notificationscoarse tool with anemail_memberaction, so MCP clients (Claude Desktop, Cursor) can call it too. The recipient picker and send history are reachable as thenotification_recipient://listandnotification_sends://listresources. - From a workflow. Add a
toolstep that callsnotifications.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_workspaceget a fresh workspace ready.draft_pieceresearch and draft a single content piece.plan_and_draft_windowplan a content window and draft across it.multi_persona_social_launcha coordinated social launch across personas.research_reportproduce a researched, cited report.gtm_diagnostica living go-to-market health report for your workspace.substack_thought_leader_newslettera thought-leadership newsletter.viz_page_from_querybuild 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/:idbrowse and read recipes.POST /agent-blueprintscreate a workflow.PATCH /agent-blueprints/:idupdate one.DELETE /agent-blueprints/:idarchive one.POST /agent-blueprints/:id/unarchiverestore one.POST /agent-blueprints/forkfork a starter into your workspace.POST /agent-blueprints/validatevalidate a body without saving (catches bad step references, cycles, and tool-allowlist typos).
# 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/runwith an optional{ inputs, as_of }body.as_of(an ISO date) makes the run read the data as of that date.GET /blueprint-runs/:idpoll one run,GET /blueprint-runslist runs (filter byblueprint_id+status). Status movesqueued -> running -> complete | error | canceled; the row carries step states, the current step, tokens used, and an error message on failure.
# 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/schedulescreate a schedule.GET /agent-blueprints/:id/schedules/GET /agent-blueprints/:id/schedules/:scheduleIdlist and read.PATCH /agent-blueprints/:id/schedules/:scheduleIdupdate (including enable / disable).DELETE /agent-blueprints/:id/schedules/:scheduleIdremove.
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/previewwith{ start, end, cadence }returns the dates it would run and the point count, without firing anything.POST /agent-blueprints/:id/backtestwith{ start, end, cadence, cumulative?, inputs? }fires the sweep (one run per date) and returns a backtest id.GET /blueprint-backtests/:id/GET /blueprint-backtestsread 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/getbrowse and read recipes (starters and your own).validatedry-run a body.describe_step_kinds/list_prompt_fragmentsintrospect what the DSL offers.create/update/delete/unarchiveauthor recipes.forkfork 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.