Create a new workflow.
## KG-First — before you write any prompt content
If the workflow you're building contains workspace-specific content (investment thesis, ICP criteria, scoring rubrics, target sectors, brand voice, geo focus, seed lists), run this preflight before generating AI-step prompt strings:
1. **Inspect** — call `list_memories`, `list_knowledge_lists`, `get_knowledge_text` to see what already exists in the workspace KG.
2. **Seed** — if the content isn't in the KG yet, write it there first (`store_memory`, `upsert_knowledge_text`, `upsert_knowledge_rows`). Confirm with the user before seeding new content.
3. **Reference** — pull KG content at runtime in the step (`kg.read-text`, `kg.read-list`, `recall_memory`). Never paste workspace-specific paragraphs into prompt template strings.
**Boundary: strategy → KG; execution wiring → workflow context; workflow structure → workflow.**
## Recommended flow for agent-authored workflows
`create_workflow({ name, goal })` → `add_step` per step → `validate_workflow` → `publish_workflow`.
Call this with **only** `name` + `goal` — leave `steps` empty. Then `add_step` one step at a time. The incremental path catches errors immediately and surfaces `{{input.X}}` / `{{steps.Y.Z}}` bindings per-step. Internal testing: 0 errors incremental vs 13 errors bulk.
`pipeline.steps` is supported for imports/templates/round-trips only. Agents authoring from scratch should not use it.
## Pipeline object fields
- `name` (required), `goal`, `description`, `context`, `metadata`, `style`
- `steps`: Closed `type` values (others silently stripped): `trigger`, `appAction`, `aiAction`, `aiActionWithTools`, `toolAction`, `code`, `knowledgeSync`, `return`, `milestone`, `share`, `wait`, `branch`, `parallel`, `loop`, `end_if`, `agentOrchestrator`.
Prefer `schedule` triggers for email intake and non-real-time use — idempotent, no webhook infra. Use `app_event`/`webhook` only for "as soon as" / "real-time" requirements.
For child / sub-workflows that end in a `return` step and are only invoked via `agentled.call-workflow`, set `context.executionInputConfig.internal: true` to hide the Run button in the UI (see `update_workflow` for details).
## Step shape reference
Call `get_step_schema({ stepType, shape? })` for the authoritative field schema AND minimal JSON examples of every common step shape. The highest-friction shapes are:
- **Report step** (aiAction with Config renderer) — `get_step_schema({ stepType: "aiAction", shape: "report" })`.
- **Composed email with approval** — `get_step_schema({ stepType: "aiAction", shape: "email" })`. Use this with `schedule-email`; do not add Gmail/Outlook send appActions unless explicitly requested.
- **Agentic research** (web_search + workspace_memory) — `get_step_schema({ stepType: "aiActionWithTools", shape: "agentic-search" })`.
- **Agent Team** (agentOrchestrator) — `get_step_schema({ stepType: "agentOrchestrator", shape: "supervisor" })`.
- **Public share URL for a report** — `get_step_schema({ stepType: "share", shape: "public" })`.
- **KG field mapping** — `get_step_schema({ stepType: "knowledgeSync", shape: "standard" })`.
- **Code step** (JavaScript transformation) — `get_step_schema({ stepType: "code", shape: "standard" })`. JavaScript only — Python is not supported.
For app actions, call `get_app_actions({ appId })` for input/output schemas. For models, call `list_models`.
## Composable Step Blocks
**Search & Extract**: `aiAction (queries) → appAction (search) → aiAction (extract)`
⚠️ NEVER pass raw user input (job titles, topics) directly to a search API — always generate optimized boolean/keyword queries first.
**Enrich & Score**: `appAction (fetch) → aiAction (score)`
**Draft & Send**: `aiAction email → approval + schedule-email`. Use HTML bodies for open/click tracking.
**Write app action with approval**: put the approval directly on the write `appAction` (`preExecuteApproval: true`, `onApproval.action: "execute-approved-action"`, `onApproval.target.type: "current-step"`). Do not add a separate "Approve X" milestone/wait step before the write action, and do not add a separate "Mark X sent/published" step unless it records the real post-send result.
**Report & Notify**: `aiAction report with Config renderer → share step → aiAction notification email with concise HTML overview + shareUrl`
**Loop Enrich & Filter**: `loopConfig on first step → appAction (enrich) → aiAction (score)`
## Available Apps & Data Sources
**Before proposing data sources or sourcing channels to the user, you MUST know what is actually reachable.** The catalog has two billing models — don't conflate them, and don't invent connectors that don't exist. Always call `list_apps` and `get_app_actions({ appId })` to verify before finalizing a plan.
### Billed via Agentled credits (no user setup needed)
- **agentled native LinkedIn / email**: `get-linkedin-profile-from-url`, `get-linkedin-company-from-url`, `find-email-person-domain`, `get-emails-from-company-domain`.
- **agentled native LinkedIn search & content**: `linkedin-post-search` (keyword post search — primary LinkedIn discovery surface), `linkedin-jobs`, `linkedin-profile-posts`, `linkedin-company-posts`.
- **email finder**: `hunter`.
- **web fetch / scrape**: `web-scraping.scrape` (any URL → markdown), `http-request.request`, `page-index`.
- **browser automation**: `browser-use.run-task` / `extract-data`, `anthropic-computer-use`, `openai-computer-use`.
- **AI / image gen**: `openai`, `google-gemini`, `mistral`, `bytedance`, `kling`.
- **public data feeds**: `french-gouv`, `google-maps`, `realtor`, `seloger`, `amazon`, `ad-intelligence`, `upwork`, `instagram`, `facebook`.
- **knowledge graph**: `kg.*` (read-list, upsert-rows, update-rows, traverse-edges, etc.) — 1 credit per call.
- **comms**: `gmail`, `google-calendar`, `webhook` (Slack/Discord), `notion`.
### Bring-your-own-key (NOT billed via Agentled credits)
These require the user to connect their own account / paste their own API key. Treat them as available only if the user has the integration connected.
- `crunchbase` (user's Crunchbase API key)
- `specter` (user's Specter API key)
- `affinity-crm` (user's Affinity API key)
- `phantombuster` (user's PhantomBuster account — runs LinkedIn search agents, Sales Navigator scrapers, etc.)
When proposing one of these, ask "do you already have a <service> account connected?" before assuming you can use it.
### Built-in tools (for `aiActionWithTools` steps, not standalone apps)
`web_search`, `file_search`, `code_interpreter`, `fetch_website_content`, `kg_search`, `kg_traverse`, `kg_nodes`, `kg_write`, `workspace_memory`. Attach via the step's `tools` array; the AI decides at runtime whether to call them.
### Common gotchas
- Want LinkedIn keyword/post search? Use the native `linkedin-post-search` (Agentled credits) — see `deal-sourcing-linkedin-founder-signals.ts` for a reference workflow.
- Want LinkedIn Sales Navigator search / lead lists / company employees? `phantombuster` (BYOK) — see `deal-sourcing-linkedin.ts`.
- No native ProductHunt / EU-Startups / X-Twitter connector — use `web-scraping.scrape` on a known URL or `web_search` via `aiActionWithTools`.
- LinkedIn profile / company *enrichment* is URL-only (`get-linkedin-profile-from-url`). For discovery, pair it with `linkedin-post-search` or `phantombuster`.
## Multi-Workflow Architecture (Source → KG → Process)
Before building a multi-workflow goal, call `list_use_cases` or `get_use_case`; reuse matching `workflowGraphId`, KG refs, agents, and workflows. Tag new bundles with `metadata.workflowGraph.id`.
For a new multi-source/shared-tail goal, call `preview_use_case_kit` first and review its dry-run operations with the user. If the ask fits Source -> KG -> Process, **do not hand-roll** N disconnected `create_workflow` calls from scratch.
When the user wants to "find leads", "source startups", "build a list to act on later", or run anything on a recurring cadence that produces entities to act on, **do not build one monolithic workflow**. Build several:
1. **One sourcing workflow per channel/theme** (e.g. "LinkedIn cybersecurity startups", "YC W25 batch", "ProductHunt this week"). Each runs on its own schedule and writes to a **shared KG list** via `kg.upsert-rows` with: `userKey` = stable id (URL/domain/LinkedIn URL) for O(1) dedup across runs; `status: "new"` to mark rows for downstream processing; `mergeStrategy: "merge"` so fields added later (scores, outreach status) survive re-upserts.
2. **One orchestrator/qualifier workflow** that runs on its own cadence (e.g. weekly), reads `kg.read-list({ filters: { status: "new" } })`, qualifies/scores each row against the current theme, then either dispatches to outreach or marks `status: "qualified" / "rejected"`.
3. **One outreach workflow** (often a child workflow called via `agentled.call-workflow`) that the orchestrator invokes for qualified rows.
Why split: sourcing cadences, qualification criteria, and approval-gated outreach evolve independently. Multiple sourcing workflows converging on one `listKey` is the canonical pattern.
Suggest the split explicitly ("N sourcing workflows + 1 qualifier + 1 outreach") instead of a mega-workflow.
## Build incrementally — two first, then refactor, then the rest
When the plan calls for many sourcing workflows (or any N near-identical workflows), **do not build all N upfront**. Build two first, ship them end-to-end, then extract what is actually shared — most often the *tail*: normalize → kg.upsert-rows (with userKey + status: "new" + mergeStrategy: "merge") → milestone.
Once the shared shape is clear:
1. Extract the common tail into a **child workflow** (terminal `return` step, `context.executionInputConfig.internal: true`) and have the two existing sourcing workflows call it via `agentled.call-workflow`.
2. Validate + run the two pilots end-to-end on the shared tail.
3. *Then* build the remaining sourcing workflows on top of that shared tail — they become small (just the source-specific search/scrape/extract head, then call the shared tail).
Why: two pilots surface the real shared shape; refactoring before scaling keeps workflows 3..N short and consistent. Don't pre-extract a child workflow before the second pilot exists.
## KG Status Lifecycle — multi-phase pipeline pattern
When a workflow acts on entities across phases (source → score → report → outreach), use KG row `status` as a DB-indexed state machine. Filtering by one status (e.g. `status: "new"`) is indexed — never scan the full list and filter in code.
**Status values are user-defined** — choose names that map to your pipeline phases (e.g. `new → scored → reported → email_sent → closed_*`, or `draft → review → approved → published`). Document the state machine in the workflow goal or as a KG text entry.
Key rules:
- Sourcing writes `status: "new"` via `kg.upsert-rows` with `mergeStrategy: "merge"` (preserves downstream-added fields across re-runs).
- Each phase reads only its input status tier and advances rows to the next.
- Mark the next status **before** side-effects (email, share, Slack). If delivery fails, the row stays in the new status — it won't be double-sent on retry.
- Every upserted row needs a `userKey` (URL, domain, LinkedIn URL, email) for O(1) cross-run dedup.
- Use `entryConditions.criteria[{ type: "loop_completion" }]` with `onCriteriaFail: "wait"` before cross-phase reads that depend on a loop finishing.
The `Loop Enrich & Filter` block above hints at the same fan-in mechanism: for post-loop convergence, use `entryConditions.criteria[{ type: "loop_completion" }]` with `onCriteriaFail: "wait"` — do not use `scope` as the runtime wait mechanism.
Requires write access when connected with OAuth.