improvement(docs): builder-first IA reorganization of the English docs (#4896)

* docs: reorganize into topic/ontology IA with a builder-first rewrite

Restructure the English docs from internal product categories into a
topic-based information architecture, and rewrite the conceptual pages
to install a mental model first rather than enumerate features.

Structure & navigation
- Reorder the sidebar to follow how someone builds: Get Started ->
  Workflows -> Tables -> Files -> Knowledge Bases -> Logs ->
  Building agents -> Mothership -> Workspaces -> Platform -> Reference.
- Demote the generated blocks/tools/triggers catalogs to a Reference
  section at the bottom.
- Break up the monolithic execution/ folder into deployment/ and
  logs-debugging/; collapse connections/* and variables/* into single
  pages under workflows/.
- Rename capabilities/ to building-agents/; relabel the integration
  catalog as "Integrations". Remove deprecated copilot and form
  deployment. Redirects added in next.config.ts for every moved URL.

Conceptual rewrites
- Workflows core (index, how-it-runs, data-flow, connections,
  variables): one mental model, one running example, terser prose.
- New building-agents overview distinguishes an agent (a workflow you
  build) from an Agent block (one reasoning step), plus a "choosing
  what to use" guide.
- Concept-trim passes on Knowledge Base, Tables, Blocks, Triggers
  overviews; new task pages for KB, Tables, and Files.
- New code-verified Alerts page.

Infrastructure
- pageType frontmatter (concept/guide/reference) + badge render.
- WorkflowPreview / OutputBundle components to embed real, app-styled
  workflow diagrams (adds framer-motion + reactflow to apps/docs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(docs): spec-driven BlockPreview for block reference heroes

Replace the static screenshot hero on each block reference page with a
<BlockPreview> that renders the block exactly as the builder canvas shows
it — header icon, sub-block rows, and branch/error handles — from a
hand-authored display spec. Static and non-interactive (no ReactFlow), so
it can't be panned or dragged, and self-updating to edit.

- block-display-specs.ts: one editable spec per block (rows, branches, handles)
- block-preview.tsx: static scaled card renderer with decorative handles
- block-icons.tsx: brand glyphs for the core block types; icons.tsx adds WaitIcon
- 14 block + 3 trigger pages swapped from <Image> to <BlockPreview>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): correct stale navigation and removed-feature references

Audited the docs against the product changelog (GitHub releases / staging
git history) for content that misleads readers — features that moved, were
renamed, or removed — rather than cosmetic drift. Fixes:

- Skills: no longer a Settings tab. It was promoted to its own workspace
  page (#4354), so "Settings → Skills under the Tools section" sent readers
  to a tab that no longer exists. (skills/index.mdx)
- Env vars: the workspace tab is "Secrets", not "Environment Variables"
  (credentials→secrets rename, #4364). (quick-reference/index.mdx)
- Mothership FAQ pointed to "Settings → Credentials" for integration
  connections; integrations moved to their own page and there is no
  Credentials tab. (mothership/tasks.mdx)
- Vision block was retired (#4684); a tip still named it. Reworded to
  "an Agent using a vision-capable model". (files/passing-files.mdx)
- Getting-started FAQ told new users to "use the Copilot feature" to build
  in natural language — that surface is Mothership. (getting-started)
- Removed the dead "Mod+Y → Go to templates" shortcut; the templates
  gallery was removed (#4354). (keyboard-shortcuts)

Note: MCP "tools" (Settings → Tools, for consuming) and MCP "servers"
(Settings → System, for exposing) are distinct surfaces — both doc
references are correct and were intentionally left as-is.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): repair broken /docs-prefixed enterprise links

The enterprise overview linked to /docs/enterprise/* (access-control, sso,
whitelabeling, audit-logs, data-retention, data-drains), but the docs site
is served at root — those 6 links 404'd. Now root-relative /enterprise/*.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): refresh stale workflow-preview example blocks

The /workflows diagram blocks are hand-authored (separate from the
spec-driven BlockPreview heroes) and had drifted from the real UI:
- Agent color purple #6f3dfa -> green #33C482 (the var(--brand) rebrand)
- Model gpt-4o -> claude-sonnet-4-6 (current default)
- "Prompt" row -> "Messages" (the actual agent sub-block)
- Start color #34B5FF -> #2FB3FF (real starter bgColor)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): align BlockPreview input/output handles to the card edge

The header (input/output) handles are positioned relative to the card and
used a -16px offset, so they floated 8px past the edge. Row/error handles
are -16px relative to a row that's already inset 8px by content padding, so
they sit correctly. Header handles are now -8px, so every handle sticks out
the same 8px and hugs the block edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Agent reference to match the current block

The page documented the old UI (System/User Prompt, no Files or Skills, Memory
taught as a separate block — contradicting its own FAQ). Rewritten to the real
sub-blocks (Messages, Model, Files, Tools, Skills, Memory, Response Format) in
the builder voice of the workflows exemplars: oriented opening, agent vs
Agent-block callout, outputs table, a live WorkflowPreview example, FAQ kept and
corrected (tool control "Force", not "Required"). pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite API reference to match the current block

Tightened to the builder voice and the real config (URL, Method, Query Params,
Headers, Body + Advanced timeout/retries/backoff). Dropped the off-topic
"Dynamic URL Construction" / "Response Validation" sections (those are
Function-block techniques, not API config). Outputs table, FAQ kept. The example
is now a live WorkflowPreview (new API_FETCH_WORKFLOW in examples.ts, exported
via the barrel). pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Condition reference to match the current block

Tightened to the builder voice: oriented opening (branches on boolean
expressions, no model call, vs Router), the real branch model (if / else if /
else, checked top to bottom), connection-tag expression examples, an error-path
callout, outputs table, and a live branching WorkflowPreview example
(CONDITION_ROUTE_WORKFLOW). FAQ kept. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): restore Best Practices + multi-example workflows on Condition

Recalibration: reference pages keep genuine substance (Best Practices, every
distinct example), cutting only redundancy and verbose register. Restores the
Best Practices section and turns the three use cases into three rendered
WorkflowPreview examples (route by priority, moderate content, branch
onboarding). Adds CONDITION_MODERATE_WORKFLOW and CONDITION_ONBOARD_WORKFLOW.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): restore Best Practices on Agent reference

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): restore Best Practices on API reference

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Function reference to match the current block

Fixed the verbose register and dropped the duplicated outputs section + the
stale Python screenshot/TODO, while keeping the real substance: JS vs Python
(local vs E2B sandbox), the large-inputs sim.files/sim.values helpers, the
worked loyalty-score example, and Best Practices. The use cases are now two
rendered WorkflowPreview examples (reshape an API response, validate input).
Adds FUNCTION_RESHAPE_WORKFLOW and FUNCTION_VALIDATE_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Router reference to match the current block

Cleaned the register, generalized the drifting model list, and folded the
Router-vs-Condition guidance into a callout. Kept the substance (routes as
output ports, NO_MATCH error path, all seven outputs, Best Practices, FAQ). The
three same-shape use cases collapse to one rendered triage WorkflowPreview
(ROUTER_TRIAGE_WORKFLOW), which the prose notes stands for the pattern.
pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): restore the classify and lead-qual examples on Router

I wrongly folded two distinct Router scenarios into a note. Restored all three
as their own rendered WorkflowPreview examples: triage a support ticket,
classify feedback (to child workflows), qualify a lead (sales vs self-serve).
Adds ROUTER_CLASSIFY_WORKFLOW and ROUTER_LEAD_WORKFLOW. (Also exports
RESPONSE_API_WORKFLOW for the next page.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Response reference to match the current block

Cleaned the register and broadened "Variable References" to connection tags
(any output, not just workflow variables). Kept the substance: exit-point
semantics, Builder/Editor mode, status codes, headers, the parallel-branch
warning, Best Practices, FAQ. All three use cases are now rendered
WorkflowPreview examples (API endpoint, webhook ack, status-per-branch). Adds
RESPONSE_API/WEBHOOK/ERROR_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Variables reference to match the current block

Cleaned the register, corrected the outputs (each assignment is also exposed as
<variables.name>, not "no outputs"), and kept the substance: assignments
reference earlier outputs and current values, global <variable.name> access,
Best Practices, FAQ. Two use cases now render as WorkflowPreview examples (count
retries, hold config). Adds VARIABLES_RETRY_WORKFLOW and VARIABLES_CONFIG_WORKFLOW.
pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Wait reference to match the current block

Corrected a real staleness: the block now has an Async mode that suspends the
run for minutes/hours/days (not a hard 10-minute cap), plus a resumeAt output.
Documents Wait Amount / Unit / Async, the sync-vs-async distinction, all three
outputs, Best Practices, and updated FAQ. Two rendered WorkflowPreview examples
(space out API calls, delayed follow-up). Adds WAIT_RATELIMIT_WORKFLOW and
WAIT_FOLLOWUP_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): polish Credential reference (frontmatter, fold redundant tabs)

The page was already accurate to the block (Select/List operations, the outputs
tabs, the wiring steps). Light touch only: added description + pageType, made the
header consistent, and folded the two identical Gmail/Slack "how to wire" tabs
into one line. Examples stay as labeled flows + the List/ForEach screenshot,
since they use integration blocks and a Loop the WorkflowPreview can't render.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): render the shared-credential example + icon fallback for integrations

Addressing the gap: WorkflowPreview block nodes now fall back to the integration
icon map, so diagrams can show Gmail/Drive/Slack/etc. with their real glyphs, not
just core blocks. Renders the Credential "share one account across blocks" example
as a WorkflowPreview (CREDENTIAL_SHARE_WORKFLOW). The multi-account and
List+ForEach examples stay as labeled flows + screenshot (the latter uses a Loop
container the preview can't render). Also exports EVALUATOR_GATE_WORKFLOW for the
next page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rewrite Evaluator reference to match the current block

Cleaned the register, generalized the drifting model list, and documented the
per-metric outputs (<evaluator.metricname>), which the page omitted. Kept the
substance (metrics with name/description/range, structured-output guarantee,
Best Practices, FAQ). The quality-gate example renders as a WorkflowPreview;
the same shape covers the parallel-variations and support-QC patterns, noted in
prose. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): render the Credential route-by-logic example too

The icon fallback unblocked it: the "route to a different account by logic"
example now renders as a WorkflowPreview (CREDENTIAL_ROUTE_WORKFLOW), a Condition
selecting a production vs staging credential. The List + ForEach example stays a
screenshot because it nests blocks in a Loop container the flat WorkflowPreview
can't represent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): render Guardrails examples + light accuracy pass

Kept the full substance (four validation types, PII entity/language detail,
the PII screenshot and video, outputs, Best Practices, FAQ). Light fixes:
frontmatter, and generalized the drifting model names (GPT-4o / Claude 3.7) to
"a strong reasoning model" with the current default. The three use cases now
render as WorkflowPreview examples (validate JSON, check grounding, block PII).
Adds GUARDRAILS_JSON/HALLUCINATION/PII_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): render Human-in-the-Loop examples + frontmatter

Kept all the substance (Display Data, Notification, Resume Form, the Approval
Methods and API Execute Behavior tabs, outputs, the paused/resume example).
Added frontmatter and rendered the use cases as WorkflowPreview examples
(approve before publish, two-stage approval, verify extracted data); Quality
Control folds into the approval note as the same approve-then-act shape. Adds
HITL_APPROVAL/MULTISTAGE/VALIDATE_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): render Webhook examples + frontmatter

The page was already accurate (Webhook URL/Payload/Signing Secret/Headers, the
automatic-headers table, HMAC details, outputs, POST-only callout, FAQ). Added
frontmatter and rendered the two use cases as WorkflowPreview examples (notify a
service, fire on a check). Adds WEBHOOK_NOTIFY_WORKFLOW and
WEBHOOK_TRIGGER_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): add example + pageType to Workflow block reference

The page was already accurate and well-structured (Configure It, outputs,
deployment-status badge, execution notes, FAQ). Added pageType: reference and a
rendered WorkflowPreview example showing a parent calling the child workflow
enrich-lead and reading its result. Adds WORKFLOW_CALL_WORKFLOW.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): container rendering for Loop/Parallel + render the Loop example

Adds subflow/container support to WorkflowPreview, modeled on the app's
subflow-node.tsx: a solid-bordered box with a header (icon + name), an internal
"Start" pill whose handle feeds the first nested block, and target/source
handles at the vertical center. PreviewBlock gains size/parentId; edges gain an
optional sourceHandle; nodes render nested children via React Flow parentNode.
Renders the Loop reference's ForEach example (LOOP_WORKFLOW) and keeps the four
loop-type sections + inside/outside referencing + caps. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): fix the Loop container's Start-pill connector

The Start pill -> first-block edge wasn't rendering: it was a React Flow
parent->child edge (unreliable), and the opaque container body hid it. Nested
blocks now render as absolute-positioned top-level nodes (container below at
zIndex 0, blocks above at zIndex 1), so the connector is an ordinary edge, and
the container body is see-through so it's visible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): render the Parallel example + frontmatter (last core block)

Reuses the container rendering for the Parallel reference. Kept all substance
(count/collection types, inside/outside referencing, batch size of 20, instance
isolation, the Parallel-vs-Loop table, Best Practices, FAQ). Added frontmatter
and a rendered container WorkflowPreview (PARALLEL_WORKFLOW: distribute tasks,
call concurrently, aggregate <parallel.results>); the two use cases stay as
labeled flows. Adds PARALLEL_WORKFLOW. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): prose glow-up for Guardrails to match the agent/condition voice

Rewrote the listy register (**Use Cases:** / **How It Works:** / **Configuration:**
scaffolding, "Use this when you need to..." filler) into the plain builder voice,
matching the depth of the Agent/Condition/Function rewrites. Kept every
validation type, option, range, the full PII entity/region list, the screenshot
and video, the outputs table, the rendered examples, Best Practices, and FAQ.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): prose glow-up for Loop to match the agent/condition voice

Rewrote into the plain builder voice and cut the filler: dropped the "Use this
when you need to..." lines and the ASCII "Example: Iteration 1, 2, 3" pseudo-code,
and folded the duplicated Inputs/Outputs tabs into Configuration + Referencing
sections. Kept all four loop types with their screenshots, the inside/outside
reference rules, the 1,000-iteration cap, sequential-vs-parallel guidance, the
rendered example, and FAQ.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): prose glow-up for Parallel to match the agent/condition voice

Same treatment as Loop: plain builder voice, dropped the ASCII pseudo-code and
the duplicated Inputs/Outputs tabs, folded the verbose Advanced Features into
tight Configuration + Referencing sections. Kept both types with screenshots,
the batch-size-of-20 cap, instance isolation, large-result indexing, the
Parallel-vs-Loop table, the rendered example, and FAQ.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): prose glow-up for Human-in-the-Loop

Tightened the register: folded the pause sentence into the intro, made the
section headers consistent (Configuration, Outputs), converted the bold-list
Block Outputs into a table, condensed the Notification channel bullets to a
line, and renamed the second "Example" so it no longer collides with the
rendered Examples. Kept all the substance — Display Data / Notification / Resume
Form, the Approval Methods and API Execute Behavior tabs, the portal video, and FAQ.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): re-enrich Loop prose (fuller, explanatory — not terse)

The first glow-up overcorrected into terse fragments. Restored proper
docs-quality prose at the Agent/Condition level: each loop type now explains
what it does, when to use it, and the relevant reference; Configuration,
Referencing, nesting, and Best Practices give context and the "why," not just
bullets. Same substance, readable depth.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): re-balance Parallel prose to the Agent/Condition register

Calibrated to the level signed off on elsewhere: each concept explained in a
couple of clear sentences with a concrete detail — informative, not terse, not
padded. Kept both types with screenshots, batch-size cap, isolation, large-result
indexing, the Parallel-vs-Loop table, the rendered example, and FAQ.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): restore the Notification channel detail on HITL

The glow-up over-compressed: it flattened the five notification channels (each
with what they do) into one sentence. Restored them as a list in plain voice —
tightening register shouldn't drop genuinely useful reference detail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): builder-voice polish on the Credential intro

Light touch only — the page was already well-structured and explanatory, so just
led the intro with what the block does (and bolded the name) to match the other
references. No content changed elsewhere.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(triggers): rewrite Start trigger in the builder voice

Tightened the register, swapped the <code>&lt;&gt;</code> noise for backticks,
added pageType + an outputs table, and kept all substance: Input Format types,
chat-only outputs (input/conversationId/files), the editor/API/chat tabs, and
best practices.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(triggers): rewrite Schedule trigger in the builder voice

Plain voice and clean markdown (dropped the raw <ul>/<div> lists). Kept all
substance: simple intervals, cron examples, timezone, deploy-tied activation,
the 100-failure auto-disable, and FAQ. Added pageType.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(triggers): refocus Webhook trigger on the generic (native) trigger

Rewrote in the builder voice and separated out the integration content: the
page now documents the generic Webhook trigger (URL, Input Format, auth, custom
response, outputs, dedup/rate-limit/deploy/no-auto-disable). The "trigger mode
for service blocks" section is reduced to a short pointer + the demo video, and
the long supported-services catalog and vague use-case bullets are dropped in
favor of the Triggers index. Fixed the title (Webhook) and added pageType.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(triggers): builder-voice glow-up for RSS

Light pass: added pageType + description, tightened the intro, and presented the
output fields as an <rss.*> outputs table. Kept the polling config, use cases,
the published-after-save callout, and the FAQ (poll cadence, dedup, 25-item cap,
auto-disable, Atom support).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(triggers): rewrite Table trigger off the auto-generated card

Replaced the BlockInfoCard/'provides 1 trigger' auto-gen format with a real
builder-voice page: a spec-driven BlockPreview hero (added a 'table' spec),
plain-language Configuration (table, event type, watch columns, include
headers), and a full <table.*> outputs table. pageType: reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(triggers): frame the index around native triggers + separate the catalog

Reframed "generic" as native (no connected account) and promoted RSS and Table
into the native set alongside Start/Schedule/Webhook — cards, comparison table,
and integration paragraph updated to match. In the sidebar, grouped the five
native triggers under a "Native triggers" header and divided the ~44 service
triggers under "Integration triggers" (nav-only — no files moved, URLs stable;
the move to integrations/ is a later, separate change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: promote Core Blocks + Core Triggers into the Workflows area

Restructured the Documentation sidebar (meta-only — no files moved, URLs stable):
after Deployment, the 16 core block pages now live under a "Core Blocks" section
and the 5 native trigger pages under "Core Triggers", instead of buried in the
bottom Reference catalog. Removed the now-redundant blocks tree from Reference,
and retitled the Reference triggers tree "Integration triggers" so it holds just
the service catalog (the native ones are promoted up top).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: merge block/trigger overviews into the Workflows overview; Core accordions

Restructured the sidebar and overview hub (meta + content only, no integration
files moved):

- Folded the /blocks and /triggers overview pages into /workflows: the overview
  now carries the core-block catalog (do work / direct flow / shape run), the
  Integrations-and-triggers families framing, the native + integration trigger
  framing, the trigger comparison, manual-run priority, and email-polling groups.
  Deleted blocks/index.mdx and triggers/index.mdx as redundant.
- Promoted the 16 core blocks into a "Core Blocks" folder accordion and the
  native triggers into a "Core Triggers" accordion, both under Workflows after
  Deployment. Integration triggers stay inside Core Triggers under a labeled
  divider, temporary until they move to integrations/<service> (tabs) later.
- Repointed every /blocks and /triggers index link to the /workflows#blocks and
  /workflows#triggers sections.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: split integration triggers into their own Reference accordion

Core Triggers is now the 5 native triggers only. Moved the 43 service triggers
out of triggers/ into a new integration-triggers/ folder, surfaced as an
"Integration triggers" accordion under Reference (an accordion must be its own
folder in Fumadocs). In Workflows, Core Triggers now sits before Core Blocks.
URLs: /triggers/<service> -> /integration-triggers/<service> (native /triggers/*
unchanged); the integrations/<service> tabbed-page migration remains the later step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): trim the overview back to an introduction

It had drifted from a concept intro into a catalog. Kept the spine (the four
parts with their previews, how-it-runs, workflows-in-context) and compressed the
merged-in material: the full 16-block enumeration becomes a three-kind taxonomy
with examples, the trigger section a short native/integration framing. Cut the
anxious in-between — manual-run trigger priority, the niche email-polling-groups
feature (belongs on the Gmail/Outlook trigger pages), the redundant block-def
line, the Start-outputs callout half, the connections video, and the catalog-y
FAQ items. Dropped the unused Video import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: relocate email-polling + trigger-priority out of the overview

Moved the two bits cut from the workflows overview to durable, generator-safe
homes: email-polling groups -> the Integrations (connecting accounts) page;
manual-run trigger priority -> the Start trigger page. Also added 'table' to the
generator's HANDWRITTEN_TRIGGER_DOCS / SKIP_TRIGGER_PROVIDERS so the hand-written
Table trigger page is no longer overwritten by generate-docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(docs-gen): emit per-service integration pages (actions + Trigger section)

Rewrites the generator to output one page per service under integrations/
instead of split tools/ + triggers/. Block pass writes the service's actions;
trigger pass appends a '## Triggers' section (badged) to the same page, or writes
a standalone page for trigger-only services. Meta is written after both passes;
hand-written integration pages are preserved; docsUrl repointed to /integrations.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(docs): unify tools + triggers into per-service /integrations pages

Encodes the ontology "everything is a block; some blocks are triggers." The
generator now emits one page per service under integrations/ — the service's
Actions plus, when it has one, a Triggers section on the same page — replacing
the split tools/<service> + triggers/<service>. No "Tools" terminology.

- generate-docs.ts: output to integrations/, merge trigger sections into each
  service page (standalone for trigger-only services), Actions heading, table
  block now generated, docsUrl -> /integrations, hand-written pages preserved.
- Nuked tools/ (213) and the interim integration-triggers/ (43); moved the
  custom-tools guide to building-agents/; knowledge/memory/file/table links and
  meta repointed to /integrations.
- Sidebar: integrations catalog now under Reference (was tools); removed the
  Workspaces integrations entry and the integration-triggers tree.
- block-icons: wait uses lucide Clock (the generated icons.tsx no longer carries
  a hand-added WaitIcon). Landing integrations data regenerated.

No redirects (fresh start). Native Core Blocks/Core Triggers unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): recover the hand-written manual-content intros on integration pages

The tools->integrations relocation generated fresh pages, so the generator never
saw the old tools/<service>.mdx to preserve its {/* MANUAL-CONTENT */} sections —
198 curated intros (AgentMail, etc.) were dropped. Reseeded each integrations
page from the pre-move tools page in git, re-ran the generator (which now merges
the manual intro into the new Actions/Triggers format), and repointed /tools/
links inside the recovered prose.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(scripts): rewrite the generator README for the integrations model

Brings scripts/README.md current: integration pages are derived from the
apps/sim block/tool/trigger registry (canonical-sources map), the golden rule
not to hand-edit generated pages, the MANUAL-CONTENT escape hatch, which pages
are hand-written/skipped, and the icons.tsx-overwrite gotcha.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: regenerate integration docs from staging-synced apps/sim

After merging staging, regenerated so the integration pages reflect current
source: correct block colors/configs (e.g. Gmail #FFFFFF), the new integrations
(sendblue, millionverifier, neverbounce, zerobounce), and staging's icon set.
Pages for integrations staging hid are removed; manual-content intros preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs-gen): don't let stale-doc cleanup delete hand-written integration pages

Staging's cleanupStaleToolDocs removes any integrations/*.mdx that isn't a visible
tools block — it only guarded `index`, so it deleted the hand-written
google/atlassian service-account pages. Now guards all HANDWRITTEN_INTEGRATION_DOCS.
Restored the two pages, and repointed /integrations/file links to /files (staging
hides the file block, so it has no integration page).

Note: staging recategorized a2a/mysql/postgresql tools -> 'blocks' (and hid file),
so they correctly drop out of the integration catalog and are currently
undocumented — an IA decision to revisit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs-gen): stop cleanup/writer filter mismatch from eating manual content

Comprehensive-review findings, all generator-consistency bugs:
- cleanup used staging's isIntegrationBlock while the writer kept the legacy
  filter, so integrations/{knowledge,memory,table}.mdx were deleted then
  regenerated without their manual intros every run. Both now honor a shared
  NATIVE_RESOURCE_BLOCK_TYPES set; intros reseeded.
- Trigger-only services (imap, circleback; category 'triggers') were likewise
  deleted each run; the canonical set now includes visible trigger-category
  blocks, the standalone writer preserves manual content, and their intros are
  reseeded.
- Mapped jsm -> jira_service_management, so JSM triggers merge into the JSM
  integration page instead of an orphan jsm.mdx (removed).
- Repointed lingering bare /tools links to /integrations; added missing
  pageType to integrations/index and building-agents/custom-tools.
Double-regen is now churn-free (idempotent) with all manual content intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): recover staging's enriched Table doc + never drop manual content

The merge resolution deleted staging's relocated blocks/table.mdx, which carried
substantial enrichment our integrations/table.mdx (reseeded from the older
tools/ version) lacked: Creating Tables (column types/constraints), Filter
Operators, Combining Filters, Sort Specification, Built-in Columns, Limits, and
Notes. Recomposed integrations/table.mdx with that content — Creating Tables
inside the intro manual section, the reference tail in a notes manual section.

Generator fix uncovered en route: a manual section whose insertion anchor is
missing in the generated markdown (e.g. notes with no "## Notes" heading) was
silently dropped on regen. Unplaceable sections now append at the end instead —
manual content is never lost. Verified idempotent across double regeneration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workspaces): de-philosophize the fundamentals prose

Rewrote in the plain register of the workflows overview: 'draws the boundary
for access' / 'Nothing crosses the boundary' / 'follow the same edge' become
direct statements (only members can access it; a workflow in one workspace
cannot read a table in another). '## The boundary' is now '## Access and
isolation'. All substance kept: every resource type, permission levels,
personal/organization/grandfathered kinds, deployments callout, VISUAL markers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(docs): restore #blocks and #triggers anchors on the workflows overview

The editorial trim renamed '## Blocks' -> '## Kinds of blocks' and
'## Triggers' -> '## How a workflow starts', silently breaking the ten
/workflows#blocks and /workflows#triggers anchor links pointed there when the
old index pages were folded in. Pinned the original ids with explicit heading
anchors. Found by the comparative prose review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: restore the genuinely useful reference bits the rewrite dropped

From the comparative prose review, restored in guidance register (no spec
dumps): temperature tiers on Agent (low/middle/high with ranges), loop/parallel
iteration references in the variables syntax-at-a-glance table, and a short
"Test it" section on the Webhook trigger (curl + check the run in Logs). The
fourth flagged loss (tag-resolver mechanics on connections) turned out to be
already covered — name normalization, case-sensitive paths, missing-output
behavior, and value formatting are all on the page; only the internal resolver
precedence chain was dropped, deliberately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): rework the Agent intro — encyclopedia register

Replaced the flat opening with a denser, factual one (no metaphor): what the
block does, and its centrality stated as fact — 'Most workflows are built
around one or more Agent blocks.' The agent-vs-Agent-block disambiguation moves
from an info callout into a second paragraph on the block's role in building
agents. Dropped the now-unused Callout import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(integrations): add the HubSpot setup guide for the Marketplace listing

Addresses HubSpot Marketplace review item A1: a public, HubSpot-specific setup
guide following their template — what the app does, install + connect through
the current flow (sidebar Integrations page -> HubSpot -> Add to Sim -> connect
dialog -> HubSpot OAuth), with real screenshots of each step and a placeholder
for the scope-approval shot; configure in a workflow (one-click skills/templates
+ the HubSpot block + trigger mode), use, disconnect (with data consequences),
uninstall from the HubSpot side, troubleshooting. Capability wording is by CRM
object rather than scope enumeration, so it stays accurate after the A2 scope
trim. Lives at /integrations/hubspot-setup, guarded as hand-written,
cross-linked from the HubSpot reference page's intro.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(integrations): rewrite the Integrations guide for the sidebar flow

Integrations moved out of Settings to a top-level sidebar page. Rewrote the
guide to the current journey: the Integrations page (Connected/Featured/search),
service pages with one-click skills and templates, + Add to Sim -> connect
dialog (display name + permissions) -> provider OAuth. Replaced the four
Settings-era screenshots with current captures (connect dialog illustrated via
HubSpot); block-side screenshots (account selector, manual credential ID) kept;
one VISUAL marker for the connection detail view pending a fresh capture.
Members/roles, credential-ID, reconnect/disconnect, email polling, and FAQ
substance unchanged apart from navigation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: move Building agents directly after Workflows in the sidebar

The agent-building journey follows straight from workflows (blocks, triggers,
deployment) rather than after the tour of every resource type. Tables/Files/
Knowledge Bases/Logs now follow it. Meta-only reorder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: fill the visual slots coverable by existing components

Six VISUAL markers filled with no new captures needed:
- building-agents overview: rendered the minimal lead-scoring agent
  (Start -> Agent with tool chips -> Response, Agent highlighted) as a
  WorkflowPreview (BUILD_AGENT_WORKFLOW)
- files guide: the read -> summarize -> write chain as a WorkflowPreview
  (FILE_SUMMARY_WORKFLOW)
- tables guide: the query -> classify -> write-back roundtrip as a
  WorkflowPreview (TABLE_ROUNDTRIP_WORKFLOW)
- choosing guide: the six-kind comparison grid as a markdown table
- knowledgebase guide: the Knowledge block's output as an OutputBundle
- workspace fundamentals: removed a duplicate nesting-diagram marker

42 -> 39 VISUAL markers remaining (screenshots + designed diagrams).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(components): run-inspector OutputBundle + lightbox with block inspector

Two visual-component upgrades, both mirroring the real app:

- OutputBundle is now a miniature of the run inspector: a Logs column (block
  rows with icon chips and durations, source selected) beside the Output panel's
  typed tree — keys with the app's type-badge semantics (string green, number
  blue, object gray, array purple, boolean orange), chevrons, indent guides,
  primitive values. Styling lifted from the terminal's structured-output.
  Dropped the "Read one value by name" footer (the prose teaches the tag).
  The three usages (data-flow, tables, knowledgebase) get real typed trees;
  data-flow's stale purple/gpt-4o example corrected en route.

- WorkflowPreview gains a lightbox + read-only block inspector: clicking a
  block (or the expand control) opens a 92vw/86vh overlay with zoom and pan,
  and a right-hand inspector panel showing the selected block's full
  configuration — canvas rows truncate, the inspector doesn't. Fields render as
  app-style controls (dropdown/textarea/input by heuristic) with dashed
  dividers, tool chips, and a Connections footer computed from the edges.
  Selection rings without dimming (new selectedBlock option in workflow-data).
  Esc/backdrop closes; body scroll locks while open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: regenerate after staging merge — AppConfig joins integrations/

Staging's new AWS AppConfig integration (#4928) generated its docs into the old
tools/ layout; re-homed to integrations/appconfig.mdx (Actions heading, meta
entry) via the generator. tools/ stays deleted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: redirect the retired tools/ and trigger URLs to integrations/

Revises the earlier fresh-start call: /tools/* are ~200 live, indexed URLs
referenced by deployed app versions' docsLink fields and marketplace listings,
so dropping them cold would 404 from the live product. next.config now 308s:
- /tools -> /integrations, /tools/:slug -> /integrations/:slug
  (custom-tools -> building-agents/custom-tools first)
- old /triggers/<service> -> /integrations/<service>, enumerated so the native
  trigger pages keep resolving; provider-slug mappings for jsm and the
  hyphenated Google/Microsoft slugs
- /blocks and /triggers index URLs -> the workflows overview anchors
Verified every class + native passthroughs against the dev server. Spec updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(getting-started): rewrite — current UI, cut the post-tutorial padding

The last old-guard page. Accuracy: Agent config now uses Messages (System/User
message) instead of the removed System Prompt/User Prompt fields, the default
model instead of GPT-4o, the banned 'no-code' phrasing is gone, the deploy card
points at /deployment, and frontmatter gets description + pageType. Weight: cut
the 'What You've Built' checklist, the 'Key Concepts You Learned' re-teach
section, the duplicate 'Resources' links, the Start-block hand-holding, and ten
dead icon imports; tightened every step preamble. 203 -> 113 lines with the
full 5-step tutorial, videos, and FAQ intact. (Videos still show the old UI
until the re-recording pass.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: de-fluff the Tier-1 heavy pages (logging, mcp, passing-files, permissions)

From the exhaustive fluff audit, keeping all substance:
- logging: merged the duplicated Console/Logs-page structure, snapshot concept
  stated once instead of three times, cut the generic Best Practices, trivial
  tab walkthrough condensed. Frontmatter added.
- mcp: intro + "What is MCP?" generic bullets folded into two sentences, cut
  the Common Use Cases catalog and the verify-your-config Troubleshooting
  checklists, merged the twice-stated Refresh behavior, security kept as one
  real warning.
- passing-files: marketing opener replaced with a factual lead, fixed the stale
  retired-Vision-block reference (now Agent with a vision model), dropped the
  FAQ item that restated the block catalog verbatim.
- permissions: heading-restating intro replaced with the two-layer model, cut
  the three "Perfect for: stakeholders..." persona lines and the generic Best
  Practices section, dropped the FAQ restating the limits table.
- connectors: audit over-flagged it — the categorized support matrix, API-key
  table, and config examples are genuine reference; frontmatter only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: tier-2 fluff trims (costs, enterprise, mailer, skills)

Conservative sweep from the audit, unambiguous cuts only: the costs CYA opener
and formula restatement, the enterprise marketing intro (now a functional
summary), mailer's restated convenience line and chat-upload comparison, and
skills' third restatement of progressive disclosure. Audit flags screened out
as misfires: mothership/tasks (immediate-vs-scheduled are two facts, not a
duplicate), self-hosting telemetry (real sizing data), and the recently
approved credential/HITL/workflow-block/trigger pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(skills): update to the Skills tab on the Integrations page + document import

Skills moved again — they now live on the Integrations page's Skills tab in the
workspace sidebar (the doc said "Open the Skills page"). Updated the create flow
(+ Add to Sim -> Add Skill dialog) with fresh screenshots of the tab and both
dialog tabs, and documented the previously-missing Import flow: upload a .md
with YAML frontmatter or a .zip containing SKILL.md, fetch from a GitHub URL, or
paste SKILL.md content (verified against the import route/component; name 64 /
description 1024 limits verified against the contract). Noted the curated
skills suggested on integration pages, cross-linked the Skills tab from the
Integrations guide, and refreshed the location FAQ. Mechanics (progressive
disclosure, load_skill, agent-block attachment) unchanged and still accurate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(building-agents): render the lead-scorer running example on choosing

The page narrated its running example through six sections without ever showing
it. Authored LEAD_SCORER_WORKFLOW (Start -> Enrich workflow-as-tool -> Function
reshape -> Agent with Search/Send Email/CRM tool chips -> Google Sheets append)
and rendered it after the intro, with highlightBlock re-renders in the three
sections that map to a node (deterministic block -> the Sheets append, agent
tool -> the Agent, workflow-as-tool -> Enrich) — the same pattern as the
workflows overview.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(tables): rewrite workflow columns around the real lead-scoring example

Rebuilt the page on the ai_startup_customers screenshots instead of captioning
them onto the old hypothetical: one running example throughout — Company Domain
fills domain, Company Info reads it into employee_count/description, Lead Score
Enrichment writes lead_score/priority/score_reasoning. Every section now
describes the actual UI: the grid with group headers, per-row run buttons, and
the 21-running toolbar; the Configure workflow panel (picker, column inputs,
output selection, Auto-run, Run after); the Company Info input/output mapping;
Not found cells explained where the screenshot shows them; the cascade section
describes the example itself. All placeholder markers on the page resolved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: regenerate after staging merge — Slack trigger update + file block re-visible

Staging's mothership v0.2 (#4923) expanded the Slack trigger payload
(interactivity, slash commands: event_type, command, action_id/value/actions,
response_url, trigger_id, callback_id, ...) — regenerated so it lands on the
unified integrations/slack page; the old-layout triggers/slack.mdx from
staging's generator was dropped in the merge. The file block is visible again
upstream, so integrations/file.mdx is back in the catalog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(tables): playbook prose pass on workflow columns + restore File block links

Workflow columns, against the docs-writing playbook: killed the banned
'Term — desc' bullets in the Configure list (term + verb form), restored the one
universal analog (spreadsheet macro), fixed the clipped 'On,/Off,' fragments,
replaced an invented <start.companyDomain> tag with the verified description,
and thinned em-dashes to four page-wide with no clustering. Also repointed
[File] block mentions back to /integrations/file now that the page exists again
(FileV5 is visible upstream); the Files-store links stay on /files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(tables): per-row execution inspection on workflow columns

Two new captures: the cell menu (View execution, Re-run cell, row actions) and
the Log Details trace for a single row's run. New 'Inspecting a row's run'
section ties cell values to real, traceable runs; corrected the re-run guidance
now that Re-run cell exists (the page previously said Run all rows was the only
way to retry).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): drop the confusing 'order by hand' sentence

'You never set the order by hand' read wrong (wiring connections is setting it
by hand), and the replacement was over-explanation. The first sentence already
carries it: Sim works out the order from the connections.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): fix the over-claim about independent blocks

'Two blocks that don't depend on each other run at the same time' is wrong —
independent blocks at different depths run at different times. Concurrency
follows from readiness, not independence: blocks whose dependencies have all
finished run together. Reworded to say that, tied to the image's two agents.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): accuracy audit of how-it-runs against the executor

Verified every claim on the page against apps/sim/executor. One claim was
materially false: "a failed block stops its own path but leaves independent
paths running" — in the engine, an unhandled block failure sets the error flag
and stops scheduling entirely (in-flight blocks finish, nothing new starts);
only a connected error port routes the failure and keeps the run alive. Now
says that. Two imprecisions tightened: a join waits for every feeder *that is
going to run* (deactivated-branch feeders don't hold it up, per the
edge-manager cascade), and Loop also repeats while a condition holds. Confirmed
accurate: per-block readiness scheduling (readyQueue + race, not layers),
branch-skip cascade and empty tags, the 25-hop call-chain cap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(logs): real captures on the overview + prose matched to the UI

The logs-debugging overview had six visual placeholders and no visuals. Three
real captures placed: the workspace Logs page as the hero (rows with status,
credits, trigger, duration), Log Details' Trace tab at the blocks section (the
CRM sync run's spans, with a one-line read of where the time went), and the
editor's live run console at the input/output section. Prose corrected to what
the UI shows: cost is in credits, failed runs are badged Error (dropped the
five-state enum the list doesn't display), and the Trace tab is named. The
row-anatomy marker is covered by the hero; the two designed-diagram markers
(debug-loop flowchart, failed-vs-success comparison) remain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): one reference syntax, named sources — untangle variables vs connection tags

An exhaustive sweep of "connection tag" found the docs asserting both that a
workflow variable is a connection tag (response.mdx used it as the umbrella for
all angle-bracket references) and that it isn't (variables.mdx). Ruled the
narrow definition canonical — a connection tag reads a block's output; the name
follows the connection — and restructured around the real model:

- variables.mdx: new "One syntax, named sources" section states that everything
  in angle brackets is one mechanism whose first segment names the source, with
  the load-bearing fact stated plainly: `variable` is literal, a connection tag
  starts with the block's own name. The syntax table drops the redundant
  dot-notation row, gets one row per source, and is ordered by resolution
  precedence with the order explained beneath it (absorbing the old Name
  conflicts section). The credentials pointer folds into the env-var section;
  trimmed the "never appears in outputs" overclaim.
- response.mdx: no longer calls a workflow variable a connection tag.
- connections.mdx: the owner page closes the loop — same syntax also reads
  variables and loop/parallel context; a connection tag is the block-output case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): verify the reference model against the resolver; fix one imprecision

Checked every claim in the new 'One syntax, named sources' section against
apps/sim/executor/variables: resolver chain order is Loop -> Parallel ->
WorkflowVariables -> Env -> Block (matches the table); 'variable'/'loop'/
'parallel' are literal prefixes (REFERENCE.PREFIX); block names normalize via
toLowerCase + strip spaces; an unmatched reference is genuinely left in place
(resolver returns undefined -> the replacer emits the raw match). One claim
tightened: {{KEY}} is a different syntax and can never collide with
angle-bracket references, so the precedence sentence now scopes collisions to
the angle-bracket sources with a concrete example (a block named 'variable').

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(building-agents): workflow-as-tool is agent-decided, not the Workflow block

The choosing page defined workflow-as-tool as the Workflow block (path-decided),
contradicting its own name and the comparison table's premise. Verified against
the product: workflow_executor is an agent tool — you pick the workflow in the
Agent block's tool list, the model decides when to call it and supplies the
inputMapping (user-or-llm), inputs arrive at the child's Start trigger.

Rewritten agent-first: the section defines it as a workflow handed to an agent
as one callable tool, the lead scorer gains a Deep Enrich workflow tool chip on
the agent (diagram updated), and the deterministic Workflow block becomes the
explicit contrast in a callout — same child workflow, the difference is who
decides, mirroring the block/agent-tool contrast. Table row corrected to
"The agent"; the summary paragraph follows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: theme-aware previews + enrichments vs workflow groups split

Light-mode support for every preview component (WorkflowPreview canvas, nodes,
containers, edges, lightbox, BlockPreview, OutputBundle, BlockInspector): a
wp-scope token block in the docs global stylesheet whose values mirror the OG
repository's globals.css in both modes (surfaces, borders, --workflow-edge,
text tiers, the --badge-* type-badge palette). Every hardcoded hex swapped to a
--wp-* var; brand colors, selection blue, and error red stay literal.

tables/workflow-columns: separated the two group kinds per the contract's
workflowGroupType enum ('manual' | 'enrichment'). New "Two kinds of groups"
section opens with the + New column menu capture (Enrichments above the types,
Workflow below); Enrichments documented from the code-defined registry (company
domain, company info, email verification, phone number, work email) including
the provider-cascade behavior that produces Not found cells; the Company Info
panel capture is now correctly labeled as an enrichment config; workflow groups
keep the Configure workflow panel. Shared machinery generalized under "How
groups run"; the cascade section names which stage is which kind; the two
portrait screenshots render smaller.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(tables): don't enumerate the enrichment catalog; don't assert a group's kind

Two corrections on workflow columns: the prose no longer lists the enrichment
catalog (growable, not procedurally tracked — it now describes the category and
points at the Enrichments panel; the provider-cascade/Not-found explanation
stays, it's behavior not catalog), and the page no longer asserts which kind
the example's Company Domain / Company Info groups are (Company Info may be a
user-built workflow, not the built-in). The input/output bindings capture moved
to "How groups run" as the kind-agnostic illustration; only Lead Score — whose
panel shows the workflow picker — is named as a workflow group.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(components): per-branch source handles — conditions and routers finally branch

WorkflowPreview's node only ever had one header source handle, so every
condition/router example fanned both edges out of a single point and never
showed the if/else rows the real canvas (and the BlockPreview hero specs)
render. PreviewBlock now supports `branches` (each rendered as a row with its
own right-edge source handle, id `branch-<id>`) and `showError` (red error
handle), mirroring the executor's per-branch condition-true/condition-false and
router-<route> handle model. A block with branches emits from them, not the
header.

Every affected example rewired (13 workflows): the three condition examples,
status-per-branch, credential routing, and the webhook-trigger check route
their edges through branch-if/branch-else with the expression on the If row and
an explicit else; the three router examples list their actual routes as branch
rows (Sales/Support/Billing, Product/Bug report, Enterprise/Self-serve); the
terminal gates (variables retry, evaluator gate, the three guardrails gates)
show dangling if/else branch rows like the canvas does.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(components): inspector shows branch rows

Moving condition expressions from rows into branches emptied the lightbox
inspector for condition/router blocks — it only mapped rows to fields. Branches
now map too: each branch renders as a field (If with its expression as code,
else as an empty control, router routes by name).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(components): branch handle ids match the app's workflow representation

Verified against the source after the branch-handles work: the canvas emits
condition-${cond.id} handles per condition row (workflow-block.tsx) and Router
V2 uses router-${routeId} port handles, and edges carry those ids as
sourceHandle — the docs' invented branch- prefix was a gratuitous divergence
that the planned fromWorkflowState() adapter would have had to translate. The
node now uses the authored branch id as the handle id directly, and every
example authors ids in the app's own scheme (condition-if/condition-else,
router-<route>), so example edges now match real workflow edges verbatim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: agent skills mint /integrations/ docs links and describe the new output

The add-integration/add-block/validate-integration skills — what Claude Code
follows when integrations land on staging — still taught the old layout:
docsLink templates pointing at docs.sim.ai/tools/{service} and 'generates
tools/{service}.mdx'. Updated so that once this PR merges, the instructions on
staging produce the new way by themselves: /integrations/ docsLinks, the
per-service page description, and the don't-hand-edit/manual-content pointer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): execution semantics, not simultaneity

The concurrency section drifted into 'run at the same time' framing across two
accuracy passes — but the semantics are non-blocking execution: a block starts
the moment its dependencies finish and waits on nothing else. Section retitled
'Blocks run as soon as they can', the rule stated in two plain sentences, the
duplicated pre-image example narration gone (the post-image caption carries it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): errors are execution semantics — own section on how-it-runs

Failure behavior was buried inside 'Watching a run' (the live-UI section). Now
a first-class 'When a block fails' section in the execution story: an error
fails the run (in-flight blocks finish, nothing new starts) unless the block's
error port is connected, in which case the run follows the error path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: data-driven additions from the platform-metrics read

Three targeted edits from the sim-internals analysis, each carrying an inline
{/* why */} provenance comment so future editorial passes know the data behind
it:

- workflows/how-it-runs gains "How long a run can take" — run timeouts are the
  only hard-error class provable at scale (2,415 five-minute timeouts in 14
  days); limits verified in lib/core/execution-limits/types.ts (5 min free /
  50 min paid sync, 90 min async, env-overridable).
- getting-started gains an "if the run doesn't go green" callout at the Test
  step — the largest funnel drop is created-workflow -> first-successful-run
  (92% -> 49%), and this is the stall point.
- function/api Best Practices: the existing error-path bullets get a guard
  comment (<1% of deployed workflows connect an error port — under-adopted,
  not under-needed) instead of duplicate bullets.
- visuals manifest: capture priority reordered by integration adoption
  (Sheets, Gmail, Telegram, WhatsApp, ...).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: regenerate after staging merge (integration validation batch + Gong tools)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: rename Building agents -> Agents; URLs match the settled IA

The section's pages now live where the sidebar says they do:
building-agents/ -> agents/, and the stray top-level /mcp and /skills fold in
as /agents/mcp and /agents/skills (they were always part of the agents story —
the URLs predated the IA settling). Sidebar section header is now "Agents",
link labels updated, and every old URL 308s: /building-agents(/*) -> /agents(/*),
/mcp, /skills, plus the existing capabilities/ and tools/custom-tools redirect
destinations retargeted. Verified: all five new pages render and every old-URL
class redirects correctly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(workflows): connections gets its video, an FAQ, and accurate output examples

The reorg dropped two things from the old tags page that belonged on the
connections reference: the connections.mp4 walkthrough (restored after the
intro) and the FAQ (rebuilt in the robust JSX form — resolver order, name
normalization, env-var syntax pointer, didn't-run behavior, array indexing,
Function-block formatting; answers aligned with the since-verified resolver
facts, including unmatched-references-left-in-place).

Editorial/accuracy pass on the output-shape tabs while in there: stale gpt-4o
and gpt-5 examples now claude-sonnet-4-6, the Agent tokens shape corrected to
the verified { input, output, total } (the page contradicted blocks/agent), and
the dubious cost: [] line dropped — the example now matches the real run
inspector.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: regenerate after staging merge — sim trigger, enrichment + logs blocks, re-shown DB integrations

Staging's #4941 added the Sim workspace-event trigger (hand-written page adopted
into Core Triggers), the Enrichment and Logs blocks (category 'blocks' — added
to NATIVE_RESOURCE_BLOCK_TYPES so they live in the integrations catalog like
table/knowledge/memory), and re-categorized mysql/postgresql/sftp/smtp/ssh back
to visible tools (their pages return to the catalog). Generator sets merged as
the union of both sides (sim in HANDWRITTEN_TRIGGER_DOCS + SKIP_TRIGGER_PROVIDERS,
enrichment in the icon allowlist).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: regenerate after staging merge (CodePipeline); suppress sim trigger from catalog

The native Sim workspace-event trigger is documented at triggers/sim — the
block writer no longer emits an integrations page for it (skip + canonical-set
exclusion). CodePipeline (#4945) lands in the catalog in the Actions format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(blocks): cross-link the Memory block from the Agent memory section

Final loss audit found the old page's pointer from built-in agent memory to the
standalone Memory block had been dropped; one line restores it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: URLs now mirror the sidebar — sections own their pages

Every page lives at a path matching its meta.json section, done now while none
of these URLs are publicly live (the last free window before merge):

- Workflows owns its accordions: /blocks/* -> /workflows/blocks/*,
  /triggers/{start,schedule,webhook,rss,table,sim} -> /workflows/triggers/*,
  /deployment/* -> /workflows/deployment/*
- Mothership owns Mailer: /mailer -> /mothership/mailer
- Workspaces & Access folds into Platform, sequenced concept-first with the
  reference tail last: /platform/{workspaces,organization,permissions,
  credentials,costs}, then platform/self-hosting/*, platform/enterprise/*
  (from /workspaces/fundamentals+organization, /permissions/roles-and-
  permissions, /credentials, /costs, /self-hosting/*, /enterprise/*)

All internal links swept (0 broken in a full-tree resolver sweep), root
meta.json repointed, and every previously-live URL 308s to its new home —
including retargeted destinations of existing redirects so chains stay
single-hop (verified: /execution/chat reaches /workflows/deployment/chat in
one hop), and the native-trigger rule ordered after the enumerated
integration-trigger redirects so /triggers/gmail still reaches
/integrations/gmail. Production build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: untrack .plans/ (local agent planning files)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(preview): tool chips use the EMCN ChipTag chrome

The canvas previews' tool chips were ad-hoc (5px radius, header surface, plain
border). The app's canonical chip chrome is the ChipTag family: 20px tall,
rounded-md, px-1, gap-1.5, --surface-5 light / --surface-4 dark with an inset
--border-1 ring and --text-body label. Mirrored those values into --wp-chip-*
tokens (both modes) and restyled the chip; the integration's brand-color icon
square stays, sized to the chip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(blocks): one-time shift of all docsLinks to the new docs URLs

Block definitions are the patterns coding agents copy from, so redirects alone
leave new blocks minting dead conventions. Every docs.sim.ai link in apps/sim
now points at the final URL scheme: /tools/<slug> -> /integrations/<slug>
(433 links), /blocks/<core> -> /workflows/blocks/<core> (knowledge/enrichment/
logs -> /integrations/*), native /triggers/* -> /workflows/triggers/*,
/mcp -> /agents/mcp, /self-hosting + /enterprise -> /platform/*, plus the
llms.txt listings and the blocks.test.ts assertions.

Verified every rewritten target against the docs tree: all resolve except ten
hidden blocks (vision, spotify, thinking, tts...) and a2a whose links were
already dead pre-reorg — no regressions introduced.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: ignore .plans/ (local agent planning files)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(files): align every File-block claim with the shipped file_v5 block

Accuracy audit against apps/sim/blocks/blocks/file.ts (FileV5Block, the visible
block) and tools/file/*:

- The block has FIVE operations, not four — Get Content was missing entirely.
- Read outputs file objects only; the page claimed it also returned extracted
  text. Text comes from Get Content (contents, per file) or Fetch
  (combinedContent) — table, prose, and the Fetch callout corrected.
- Functions CAN read files: sim.files.readText/readBase64 exist in the sandbox
  (isolated-vm-worker.cjs), so "doesn't reach into workspace storage" is gone;
  the section now teaches Get Content text or sim.files on the file object.
- Workspace file IDs are wf_<shortId> (workspace-file-manager.ts:511), not f_.
- Stale "such as Claude or GPT-4o" vision parenthetical dropped.
- "File block reference" card pointed at /files (the section overview); now
  /integrations/file.
- FILE_SUMMARY example agent consumed <file.combinedContent>, which Read never
  produces — now binds the file object to the Files input.
- passing-files.mdx: combinedContent scoped to Fetch, contents documented.

Verified intact: Write's numeric-suffix collision behavior, Fetch's auth
headers, Append-by-name, and the file-object shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: keyboard-shortcuts audited against the command registry; cut legacy workspace detail

Every binding verified against commands-utils.ts (the global registry),
workflow.tsx, and table-grid.tsx. Three fixes: tables Mod+A (select all rows)
doesn't exist — the real bindings are Shift+Space (select row, was misworded
as a toggle) and the undocumented Mod+Space (select column); the global
Mod+Shift+A row conflated two commands — add-agent (Mod+Shift+A) and
add-workflow (Mod+Shift+P) are separate. All 29 other documented shortcuts
confirmed accurate, including tables clipboard (native copy/cut/paste events)
and Mod+Y redo (tables only — correctly absent from the workflow editor
section).

Also drops the grandfathered_shared workspace paragraph — internal billing
taxonomy, not something a reader can act on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: apply Theodore's accuracy feedback

- getting-started: workflow creation is the + button next to Workflows in the
  sidebar (no "New Workflow" button exists); Exa/Linkup no longer need
  user-supplied API keys on hosted Sim (apiKey is hideWhenHosted in the Exa
  block) — step and FAQ updated.
- workflows overview: chat and API are entry points of the Start trigger, not
  separate triggers — the "swap in a chat/API trigger" sentence now matches
  triggers/start's own model.
- variables: names cannot contain periods — the resolver reads everything
  after the first dot as a path into the value (executor/variables/resolvers/
  workflow.ts splits on dots) — constraint now stated where name normalization
  is taught.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: Python sandbox package list (verified) + agent/agents cross-linking

Function block: the Python callout's "common packages like matplotlib" becomes
the actual package list, grouped by use. Sources verified 2026-06-10 and cited
in an inline provenance comment: E2B's code-interpreter template requirements
(the base Sim's mothership-shell template builds from) plus Sim's three pip
additions (awscli/yq/csvkit, per the copilot repo's template.ts via
sim-internals). Versions omitted so the list doesn't rot on routine bumps.

Agent surfaces deduplicated by direction: blocks/agent's Tools section now
links custom tools and MCP and points at the Agents concept page for tool
sourcing; agents/index drops its duplicated Auto/Force/None enumeration in
favor of the block reference, which owns config mechanics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Will Chen
2026-06-10 18:39:57 -07:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 167ec5ef1e
commit bc55fc3b50
669 changed files with 18126 additions and 15291 deletions
+2 -1
View File
@@ -36,4 +36,5 @@ bun-debug.log*
next-env.d.ts
# Fumadocs
/.source/
/.source/
.plans/
+3 -3
View File
@@ -5,11 +5,12 @@ import type { ApiPageProps } from 'fumadocs-openapi/ui'
import { createAPIPage } from 'fumadocs-openapi/ui'
import { Pre } from 'fumadocs-ui/components/codeblock'
import defaultMdxComponents from 'fumadocs-ui/mdx'
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page'
import { DocsBody, DocsPage, DocsTitle } from 'fumadocs-ui/page'
import { notFound } from 'next/navigation'
import { PageFooter } from '@/components/docs-layout/page-footer'
import { PageNavigationArrows } from '@/components/docs-layout/page-navigation-arrows'
import { LLMCopyButton } from '@/components/page-actions'
import { PageTypeBadge } from '@/components/page-type-badge'
import { StructuredData } from '@/components/structured-data'
import { CodeBlock } from '@/components/ui/code-block'
import { Heading } from '@/components/ui/heading'
@@ -173,7 +174,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
</div>
<DocsTitle className='mb-2'>{data.title}</DocsTitle>
<DocsDescription>{data.description}</DocsDescription>
</div>
<DocsBody>
<APIPage {...apiProps} />
@@ -222,8 +222,8 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
</div>
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
</div>
{data.pageType && <PageTypeBadge type={data.pageType} className='mb-3' />}
<DocsTitle className='mb-2'>{data.title}</DocsTitle>
<DocsDescription>{data.description}</DocsDescription>
</div>
<DocsBody>
<MDX
+77
View File
@@ -1850,6 +1850,83 @@ html.dark main article blockquote {
box-shadow: none !important;
}
/* Workflow-preview theme scope. Values mirror the app's tokens in
apps/sim/app/_styles/globals.css (light from :root/.light, dark from .dark)
so the docs previews match the OG repository in both modes. */
.wp-scope {
/* surfaces */
--wp-canvas: #fefefe; /* --bg */
--wp-panel: #fbfbfb; /* --surface-1 */
--wp-surface: #ffffff; /* --surface-2 */
--wp-header: #f7f7f7; /* --surface-3 */
--wp-btn: #f5f5f5; /* --surface-4 */
--wp-control: #f3f3f3; /* --surface-5 */
--wp-active: #ececec; /* --surface-active */
--wp-container-fill: rgba(0, 0, 0, 0.02);
/* borders */
--wp-border: #dedede; /* --border */
--wp-border-1: #e0e0e0; /* --border-1 */
--wp-chip-bg: #f3f3f3; /* --surface-5: ChipTag surface (light) */
--wp-chip-text: #3b3b3b; /* --text-body */
--wp-divider: #ededed; /* --divider */
--wp-edge: #e0e0e0; /* --workflow-edge */
/* text */
--wp-text: #1a1a1a; /* --text-primary */
--wp-text-2: #525252; /* --text-secondary */
--wp-text-3: #5c5c5c; /* --text-tertiary */
--wp-text-muted: #707070; /* --text-muted */
--wp-text-subtle: #8c8c8c; /* --text-subtle */
/* type badges (output inspector) */
--wp-badge-success-bg: #bbf7d0;
--wp-badge-success-text: #15803d;
--wp-badge-blue-bg: #bfdbfe;
--wp-badge-blue-text: #1d4ed8;
--wp-badge-orange-bg: #fed7aa;
--wp-badge-orange-text: #c2410c;
--wp-badge-purple-bg: #e9d5ff;
--wp-badge-purple-text: #7c3aed;
--wp-badge-gray-bg: #e7e5e4;
--wp-badge-gray-text: #57534e;
}
.dark .wp-scope {
--wp-canvas: #1b1b1b;
--wp-panel: #1e1e1e;
--wp-surface: #232323;
--wp-header: #242424;
--wp-btn: #292929;
--wp-control: #363636;
--wp-active: #2c2c2c;
--wp-container-fill: rgba(255, 255, 255, 0.02);
--wp-border: #333333;
--wp-border-1: #3d3d3d;
--wp-chip-bg: #292929; /* --surface-4: ChipTag surface (dark) */
--wp-chip-text: #cdcdcd; /* --text-body */
--wp-divider: #393939;
--wp-edge: #454545;
--wp-text: #e6e6e6;
--wp-text-2: #cccccc;
--wp-text-3: #b3b3b3;
--wp-text-muted: #787878;
--wp-text-subtle: #7d7d7d;
--wp-badge-success-bg: rgba(34, 197, 94, 0.2);
--wp-badge-success-text: #86efac;
--wp-badge-blue-bg: rgba(59, 130, 246, 0.2);
--wp-badge-blue-text: #93c5fd;
--wp-badge-orange-bg: rgba(249, 115, 22, 0.2);
--wp-badge-orange-text: #fdba74;
--wp-badge-purple-bg: rgba(168, 85, 247, 0.2);
--wp-badge-purple-text: #d8b4fe;
--wp-badge-gray-bg: #3a3a3a;
--wp-badge-gray-text: #a8a8a8;
}
/* Tailwind v4 content sources */
@source '../app/**/*.{js,ts,jsx,tsx,mdx}';
@source '../components/**/*.{js,ts,jsx,tsx,mdx}';
+44
View File
@@ -0,0 +1,44 @@
import type { DocsPageType } from '@/lib/source'
import { cn } from '@/lib/utils'
const CONFIG = {
tutorial: { label: 'Tutorial', className: 'text-[#33c482] border-[#33c482]/30 bg-[#33c482]/10' },
guide: {
label: 'Guide',
className: 'text-blue-600 border-blue-500/30 bg-blue-500/10 dark:text-blue-400',
},
reference: {
label: 'Reference',
className: 'text-violet-600 border-violet-500/30 bg-violet-500/10 dark:text-violet-400',
},
concept: {
label: 'Concept',
className: 'text-amber-600 border-amber-500/30 bg-amber-500/10 dark:text-amber-400',
},
} as const satisfies Record<DocsPageType, { label: string; className: string }>
interface PageTypeBadgeProps {
type: DocsPageType
className?: string
}
/**
* Small label that tells the reader which Diátaxis mode a page is — learning,
* task, lookup, or understanding. Rendered only when a page declares `type`.
*/
export function PageTypeBadge({ type, className }: PageTypeBadgeProps) {
const config = CONFIG[type]
if (!config) return null
return (
<span
className={cn(
'inline-flex items-center rounded-full border px-2 py-0.5 font-medium text-[11px] uppercase tracking-[0.04em]',
config.className,
className
)}
>
{config.label}
</span>
)
}
@@ -0,0 +1,220 @@
/**
* A hand-authored block, described as what the builder canvas displays.
*
* - `rows` are the visible sub-block rows; use `'-'` for an empty/unset field (the canvas
* shows a dash), or a representative value where the field has a default.
* - `branches` render one output handle per entry (Condition's if/else-if/else, Router's
* routes); when set, also set `hideSourceHandle: true` so the single output is replaced.
* - `showError: true` adds the bottom `Error` row + red handle (action blocks, not triggers).
* - `hideTargetHandle: true` for triggers (entry points — no input).
* - `bgColor` is the resolved hex; the Agent uses Sim green `#33C482` (`var(--brand)`).
*/
export interface BlockDisplaySpec {
name: string
/** Block type — drives the header icon (see `BLOCK_ICONS`). */
type: string
/** Resolved brand color (hex). */
bgColor: string
rows: Array<{ title: string; value: string }>
branches?: string[]
showError?: boolean
hideTargetHandle?: boolean
hideSourceHandle?: boolean
}
/**
* Display specs for the block reference previews — one per block, edited to match exactly
* what the builder canvas shows. Source of truth for the `<BlockPreview>` heroes.
*/
export const BLOCK_DISPLAY_SPECS: Record<string, BlockDisplaySpec> = {
agent: {
name: 'Agent',
type: 'agent',
bgColor: '#33C482',
showError: true,
rows: [
{ title: 'Messages', value: '-' },
{ title: 'Model', value: 'claude-sonnet-4-6' },
{ title: 'Files', value: '-' },
{ title: 'Tools', value: '-' },
{ title: 'Skills', value: '-' },
{ title: 'Memory', value: 'None' },
{ title: 'Response Format', value: '-' },
],
},
api: {
name: 'API',
type: 'api',
bgColor: '#2F55FF',
showError: true,
rows: [
{ title: 'URL', value: '-' },
{ title: 'Method', value: 'GET' },
{ title: 'Query Params', value: '-' },
{ title: 'Headers', value: '-' },
{ title: 'Body', value: '-' },
],
},
condition: {
name: 'Condition',
type: 'condition',
bgColor: '#FF752F',
showError: true,
hideSourceHandle: true,
rows: [],
branches: ['if', 'else if', 'else'],
},
credential: {
name: 'Credential',
type: 'credential',
bgColor: '#6366F1',
showError: true,
rows: [
{ title: 'Operation', value: 'Select Credential' },
{ title: 'Credential', value: '-' },
],
},
evaluator: {
name: 'Evaluator',
type: 'evaluator',
bgColor: '#4D5FFF',
showError: true,
rows: [
{ title: 'Evaluation Metrics', value: '-' },
{ title: 'Content', value: '-' },
{ title: 'Model', value: 'claude-sonnet-4-6' },
],
},
function: {
name: 'Function',
type: 'function',
bgColor: '#FF402F',
showError: true,
rows: [
{ title: 'Language', value: 'JavaScript' },
{ title: 'Code', value: '-' },
],
},
guardrails: {
name: 'Guardrails',
type: 'guardrails',
bgColor: '#3D642D',
showError: true,
rows: [
{ title: 'Content to Validate', value: '-' },
{ title: 'Validation Type', value: 'Valid JSON' },
],
},
response: {
name: 'Response',
type: 'response',
bgColor: '#2F55FF',
showError: true,
hideSourceHandle: true,
rows: [
{ title: 'Response Data Mode', value: 'Builder' },
{ title: 'Response Structure', value: '-' },
{ title: 'Status Code', value: '-' },
{ title: 'Response Headers', value: '-' },
],
},
router: {
name: 'Router',
type: 'router',
bgColor: '#28C43F',
showError: true,
hideSourceHandle: true,
rows: [{ title: 'Context', value: '-' }],
branches: ['route 1'],
},
variables: {
name: 'Variables',
type: 'variables',
bgColor: '#8B5CF6',
showError: true,
rows: [{ title: 'Variable Assignments', value: '-' }],
},
wait: {
name: 'Wait',
type: 'wait',
bgColor: '#F59E0B',
showError: true,
rows: [
{ title: 'Wait Amount', value: '-' },
{ title: 'Unit', value: 'Seconds' },
],
},
webhook: {
name: 'Webhook',
type: 'webhook',
bgColor: '#10B981',
showError: true,
rows: [
{ title: 'Webhook URL', value: '-' },
{ title: 'Payload', value: '-' },
{ title: 'Signing Secret', value: '-' },
{ title: 'Additional Headers', value: '-' },
],
},
workflow: {
name: 'Workflow',
type: 'workflow',
bgColor: '#6366F1',
showError: true,
rows: [
{ title: 'Select Workflow', value: '-' },
{ title: 'Input Variable', value: '-' },
],
},
human_in_the_loop: {
name: 'Human in the Loop',
type: 'human_in_the_loop',
bgColor: '#10B981',
showError: true,
rows: [
{ title: 'Display Data', value: '-' },
{ title: 'Notification', value: '-' },
{ title: 'Resume Form', value: '-' },
],
},
schedule: {
name: 'Schedule',
type: 'schedule',
bgColor: '#6366F1',
hideTargetHandle: true,
rows: [
{ title: 'Run frequency', value: 'Every X Minutes' },
{ title: 'Interval (minutes)', value: '-' },
],
},
rss: {
name: 'RSS Feed',
type: 'rss',
bgColor: '#F97316',
hideTargetHandle: true,
rows: [{ title: 'Feed URL', value: '-' }],
},
webhook_trigger: {
name: 'Webhook',
type: 'webhook',
bgColor: '#10B981',
hideTargetHandle: true,
rows: [
{ title: 'Webhook URL', value: '-' },
{ title: 'Require Authentication', value: '-' },
{ title: 'Input Format', value: '-' },
],
},
table: {
name: 'Table',
type: 'table',
bgColor: '#10B981',
hideTargetHandle: true,
rows: [
{ title: 'Table', value: '-' },
{ title: 'Event type', value: 'Row updated' },
{ title: 'Watch columns', value: '-' },
],
},
}
@@ -0,0 +1,103 @@
import type { SVGProps } from 'react'
import { Clock, Database, Layers, Repeat, Table } from 'lucide-react'
import {
ApiIcon,
ChartBarIcon,
CodeIcon,
ConditionalIcon,
ConnectIcon,
CredentialIcon,
HumanInTheLoopIcon,
ResponseIcon,
RssIcon,
ScheduleIcon,
ShieldCheckIcon,
VariableIcon,
WebhookIcon,
WorkflowIcon,
} from '@/components/icons'
/**
* The two Sim-specific block glyphs we need, ported verbatim from
* `apps/sim/components/icons.tsx` so the preview matches the real builder.
* Other block types fall back to lucide-react stand-ins for now.
*/
export function StartIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width='26'
height='16'
viewBox='0 0 26 16'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M7.8 13C9.23 13 10.45 12.49 11.47 11.47C12.49 10.45 13 9.23 13 7.8C13 6.37 12.49 5.15 11.47 4.13C10.45 3.11 9.23 2.6 7.8 2.6C6.37 2.6 5.15 3.11 4.13 4.13C3.11 5.15 2.6 6.37 2.6 7.8C2.6 9.23 3.11 10.45 4.13 11.47C5.15 12.49 6.37 13 7.8 13ZM7.8 15.6C5.63 15.6 3.79 14.84 2.28 13.33C0.76 11.81 0 9.97 0 7.8C0 5.63 0.76 3.79 2.28 2.28C3.79 0.76 5.63 0 7.8 0C9.75 0 11.45 0.62 12.89 1.85C14.33 3.09 15.2 4.64 15.5 6.5H24.7C25.07 6.5 25.38 6.62 25.63 6.87C25.88 7.12 26 7.43 26 7.8C26 8.17 25.87 8.48 25.63 8.73C25.38 8.98 25.07 9.1 24.7 9.1H15.5C15.2 10.96 14.33 12.51 12.89 13.75C11.44 14.98 9.75 15.6 7.8 15.6Z'
fill='currentColor'
/>
</svg>
)
}
export function AgentIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
width='21'
height='24'
viewBox='0 0 21 24'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M15.67 9.25H4.67C2.64 9.25 1 10.89 1 12.92V18.42C1 20.44 2.64 22.08 4.67 22.08H15.67C17.69 22.08 19.33 20.44 19.33 18.42V12.92C19.33 10.89 17.69 9.25 15.67 9.25Z'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path
d='M10.17 5.58C11.18 5.58 12 4.76 12 3.75C12 2.74 11.18 1.92 10.17 1.92C9.15 1.92 8.33 2.74 8.33 3.75C8.33 4.76 9.15 5.58 10.17 5.58Z'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path
d='M10.17 5.59V9.25M7.42 16.59V14.75M12.92 14.75V16.59'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
/>
</svg>
)
}
/** Block type → glyph. Brand glyphs from the app for core blocks; lucide stand-ins for the rest. */
export const BLOCK_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {
starter: StartIcon,
start_trigger: StartIcon,
agent: AgentIcon,
api: ApiIcon,
condition: ConditionalIcon,
credential: CredentialIcon,
evaluator: ChartBarIcon,
function: CodeIcon,
guardrails: ShieldCheckIcon,
human_in_the_loop: HumanInTheLoopIcon,
response: ResponseIcon,
router: ConnectIcon,
variables: VariableIcon,
wait: Clock,
webhook: WebhookIcon,
workflow: WorkflowIcon,
schedule: ScheduleIcon,
rss: RssIcon,
loop: Repeat,
parallel: Layers,
knowledge_base: Database,
knowledge: Database,
table: Table,
}
@@ -0,0 +1,229 @@
'use client'
import { BookOpen, ChevronDown, ChevronRight, Pencil } from 'lucide-react'
import { blockTypeToIconMap } from '@/components/ui/icon-mapping'
import { BLOCK_ICONS } from '@/components/workflow-preview/block-icons'
type FieldKind = 'select' | 'input' | 'textarea' | 'code' | 'slider' | 'toggle'
interface InspectorField {
label: string
required?: boolean
kind?: FieldKind
/** Shown inside the control. For 'toggle', "on"/"off". For 'slider', the number. */
value?: string
/** Muted placeholder when there's no value. */
placeholder?: string
/** Slider fill, 0–100. */
percent?: number
}
interface InspectorConnection {
name: string
type?: string
color?: string
}
interface InspectorTool {
type: string
name: string
bgColor: string
}
interface BlockInspectorProps {
/** Block name in the header, e.g. "Agent 1". */
name: string
/** Block type, for the header icon. */
type?: string
color?: string
fields: InspectorField[]
tools?: InspectorTool[]
connections?: InspectorConnection[]
/** Render as a borderless panel filling its parent (the lightbox sidebar). */
embedded?: boolean
}
function resolveIcon(type: string) {
return BLOCK_ICONS[type] ?? blockTypeToIconMap[type] ?? null
}
const CONTROL =
'flex w-full items-center justify-between gap-2 rounded-[10px] bg-[var(--wp-control)] px-3 py-2.5 text-[13px]'
function FieldControl({ field }: { field: InspectorField }) {
const kind = field.kind ?? 'input'
const hasValue = field.value !== undefined && field.value !== ''
const textColor = hasValue ? 'var(--wp-text)' : 'var(--wp-text-muted)'
const text = hasValue ? field.value : (field.placeholder ?? '—')
if (kind === 'textarea' || kind === 'code') {
return (
<div
className='w-full rounded-[10px] bg-[var(--wp-control)] px-3 py-2.5 text-[13px] leading-[1.55]'
style={{
color: textColor,
fontFamily: kind === 'code' ? 'var(--font-mono, monospace)' : undefined,
}}
>
{text}
</div>
)
}
if (kind === 'toggle') {
const on = field.value === 'on'
return (
<div className='flex items-center gap-2'>
<div
className='flex h-[18px] w-[32px] items-center rounded-full px-[2px] transition-colors'
style={{ background: on ? '#33C482' : 'var(--wp-border-1)' }}
>
<div
className='size-[14px] rounded-full bg-white'
style={{ marginLeft: on ? 'auto' : 0 }}
/>
</div>
<span className='text-[12px] text-[var(--wp-text-muted)]'>{on ? 'On' : 'Off'}</span>
</div>
)
}
if (kind === 'slider') {
const percent = field.percent ?? 50
return (
<div className='flex w-full items-center gap-3'>
<div className='relative h-[4px] flex-1 rounded-full bg-[var(--wp-border-1)]'>
<div
className='absolute inset-y-0 left-0 rounded-full bg-[#33C482]'
style={{ width: `${percent}%` }}
/>
<div
className='-translate-y-1/2 absolute top-1/2 size-[12px] rounded-full bg-white'
style={{ left: `calc(${percent}% - 6px)` }}
/>
</div>
<span className='text-[13px] text-[var(--wp-text)]'>{field.value}</span>
</div>
)
}
return (
<div className={CONTROL}>
<span className='truncate' style={{ color: textColor }}>
{text}
</span>
{kind === 'select' && (
<ChevronDown className='size-[14px] flex-shrink-0 text-[var(--wp-text-muted)]' />
)}
</div>
)
}
/**
* A read-only facsimile of the editor's right-hand block inspector: the block
* header, its configuration fields as static controls, and its connections.
* Hand-authored per usage, like {@link WorkflowPreview} examples.
*/
export function BlockInspector({
name,
type = 'agent',
color = '#33C482',
fields,
tools,
connections,
embedded = false,
}: BlockInspectorProps) {
const Icon = resolveIcon(type)
return (
<div
className={
embedded
? 'wp-scope flex h-full w-full flex-col overflow-y-auto bg-[var(--wp-panel)]'
: 'wp-scope not-prose my-6 w-full max-w-[380px] overflow-hidden rounded-xl border border-[var(--wp-border)] bg-[var(--wp-panel)]'
}
>
<div className='flex items-center gap-2.5 border-[var(--wp-border)] border-b px-3 py-2.5'>
<div
className='flex size-[22px] flex-shrink-0 items-center justify-center rounded-[6px]'
style={{ background: color }}
>
{Icon && <Icon className='size-[13px] text-white' />}
</div>
<span className='font-medium text-[14px] text-[var(--wp-text)]'>{name}</span>
<span className='ml-auto flex items-center gap-2.5 text-[var(--wp-text-muted)]'>
<Pencil className='size-[13px]' />
<BookOpen className='size-[13px]' />
</span>
</div>
<div className='flex flex-col px-3 py-1'>
{fields.map((field, i) => (
<div
key={field.label}
className='flex flex-col gap-2 py-3'
style={i > 0 ? { borderTop: '1px dashed var(--wp-divider)' } : undefined}
>
<span className='font-medium text-[13px] text-[var(--wp-text)]'>
{field.label}
{field.required && <span className='ml-1 text-[var(--wp-text-muted)]'>*</span>}
</span>
<FieldControl field={field} />
</div>
))}
{tools && tools.length > 0 && (
<div
className='flex flex-col gap-2 py-3'
style={fields.length > 0 ? { borderTop: '1px dashed var(--wp-divider)' } : undefined}
>
<span className='font-medium text-[13px] text-[var(--wp-text)]'>Tools</span>
<div className='flex flex-wrap gap-[6px]'>
{tools.map((tool) => {
const TIcon = resolveIcon(tool.type)
return (
<div
key={tool.type}
className='flex items-center gap-[5px] rounded-[6px] border border-[var(--wp-border-1)] bg-[var(--wp-control)] px-[7px] py-[4px]'
>
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center rounded-[4px]'
style={{ background: tool.bgColor }}
>
{TIcon && <TIcon className='size-[9px] text-white' />}
</div>
<span className='text-[12px] text-[var(--wp-text)]'>{tool.name}</span>
</div>
)
})}
</div>
</div>
)}
</div>
{connections && connections.length > 0 && (
<div className='border-[var(--wp-border)] border-t px-3 py-2.5'>
<div className='flex items-center gap-1.5 text-[12px] text-[var(--wp-text-muted)]'>
<ChevronDown className='size-[12px]' />
Connections
</div>
{connections.map((c) => {
const CIcon = c.type ? resolveIcon(c.type) : null
return (
<div key={c.name} className='mt-2 flex items-center gap-2'>
<div
className='flex size-[18px] flex-shrink-0 items-center justify-center rounded-[5px]'
style={{ background: c.color ?? 'var(--wp-border-1)' }}
>
{CIcon && <CIcon className='size-[10px] text-white' />}
</div>
<span className='text-[13px] text-[var(--wp-text)]'>{c.name}</span>
<ChevronRight className='size-[12px] text-[var(--wp-text-muted)]' />
</div>
)
})}
</div>
)}
</div>
)
}
@@ -0,0 +1,109 @@
'use client'
import {
BLOCK_DISPLAY_SPECS,
type BlockDisplaySpec,
} from '@/components/workflow-preview/block-display-specs'
import { BLOCK_ICONS } from '@/components/workflow-preview/block-icons'
/** Display scale for the hero — bump this one number to resize. */
const SCALE = 1.3
const DOT = '-translate-y-1/2 absolute top-1/2 h-5 w-[7px]'
const HEADER_DOT = '-translate-y-1/2 absolute top-[20px] h-5 w-[7px]'
/**
* Static, app-styled block card — the same visual as the canvas WorkflowBlock, but
* with non-interactive decorative handles (no ReactFlow, so it can't be panned/dragged).
*/
function BlockCard({ spec }: { spec: BlockDisplaySpec }) {
const Icon = BLOCK_ICONS[spec.type]
const branches = spec.branches ?? []
const hasContent = spec.rows.length > 0 || branches.length > 0 || Boolean(spec.showError)
return (
<div className='relative w-[250px] select-none rounded-[8px] border border-[var(--wp-border-1)] bg-[var(--wp-surface)]'>
{!spec.hideTargetHandle && (
<span className={`${HEADER_DOT} left-[-8px] rounded-l-[2px] bg-[var(--wp-edge)]`} />
)}
{!spec.hideSourceHandle && (
<span className={`${HEADER_DOT} right-[-8px] rounded-r-[2px] bg-[var(--wp-edge)]`} />
)}
<div
className={`flex items-center justify-between p-2 ${hasContent ? 'border-[var(--wp-border-1)] border-b' : ''}`}
>
<div className='flex min-w-0 flex-1 items-center gap-2.5'>
<div
className='flex size-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
style={{ background: spec.bgColor }}
>
{Icon && <Icon className='size-[16px] text-white' />}
</div>
<span className='truncate font-medium text-[16px] text-[var(--wp-text)]'>
{spec.name}
</span>
</div>
</div>
{hasContent && (
<div className='flex flex-col gap-2 p-2'>
{spec.rows.map((row) => (
<div key={row.title} className='flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)] capitalize'>
{row.title}
</span>
{row.value && (
<span className='flex min-w-0 flex-1 items-center justify-end font-normal text-[14px] text-[var(--wp-text)]'>
<span className='truncate'>{row.value}</span>
</span>
)}
</div>
))}
{branches.map((branch) => (
<div key={branch} className='relative flex items-center justify-between gap-2'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)] capitalize'>
{branch}
</span>
<span className='font-normal text-[14px] text-[var(--wp-text-3)]'>-</span>
<span className={`${DOT} right-[-16px] rounded-r-[2px] bg-[var(--wp-edge)]`} />
</div>
))}
{spec.showError && (
<div className='relative flex items-center'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)] capitalize'>
error
</span>
<span className={`${DOT} right-[-16px] rounded-r-[2px] bg-[#ef4444]`} />
</div>
)}
</div>
)}
</div>
)
}
interface BlockPreviewProps {
/** Block key from {@link BLOCK_DISPLAY_SPECS} (e.g. `agent`, `condition`, `webhook_trigger`). */
type: string
}
/**
* Renders a single block exactly as it appears on the builder canvas, from its
* hand-authored display spec — static (no canvas) and scaled up. Use as the hero on a
* block reference page: `<BlockPreview type="agent" />`. Edit specs in `block-display-specs.ts`.
*/
export function BlockPreview({ type }: BlockPreviewProps) {
const spec = BLOCK_DISPLAY_SPECS[type]
if (!spec) return null
return (
<div className='wp-scope not-prose my-6 flex justify-center overflow-x-auto rounded-xl border border-[var(--wp-border)] bg-[var(--wp-canvas)] px-6 py-10'>
<div style={{ zoom: SCALE }}>
<BlockCard spec={spec} />
</div>
</div>
)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
export { BlockPreview } from '@/components/workflow-preview/block-preview'
export {
API_FETCH_WORKFLOW,
BUILD_AGENT_WORKFLOW,
CLASSIFY_REPLY_WORKFLOW,
CLASSIFY_WORKFLOW,
CONDITION_MODERATE_WORKFLOW,
CONDITION_ONBOARD_WORKFLOW,
CONDITION_ROUTE_WORKFLOW,
CREDENTIAL_ROUTE_WORKFLOW,
CREDENTIAL_SHARE_WORKFLOW,
EVALUATOR_GATE_WORKFLOW,
FILE_SUMMARY_WORKFLOW,
FUNCTION_RESHAPE_WORKFLOW,
FUNCTION_VALIDATE_WORKFLOW,
GUARDRAILS_HALLUCINATION_WORKFLOW,
GUARDRAILS_JSON_WORKFLOW,
GUARDRAILS_PII_WORKFLOW,
HITL_APPROVAL_WORKFLOW,
HITL_MULTISTAGE_WORKFLOW,
HITL_VALIDATE_WORKFLOW,
LEAD_SCORER_WORKFLOW,
LOOP_WORKFLOW,
PARALLEL_WORKFLOW,
RESPONSE_API_WORKFLOW,
RESPONSE_ERROR_WORKFLOW,
RESPONSE_WEBHOOK_WORKFLOW,
ROUTER_CLASSIFY_WORKFLOW,
ROUTER_LEAD_WORKFLOW,
ROUTER_TRIAGE_WORKFLOW,
SUPPORT_KB_WORKFLOW,
TABLE_ENRICH_WORKFLOW,
TABLE_ROUNDTRIP_WORKFLOW,
VARIABLES_CONFIG_WORKFLOW,
VARIABLES_RETRY_WORKFLOW,
WAIT_FOLLOWUP_WORKFLOW,
WAIT_RATELIMIT_WORKFLOW,
WEBHOOK_NOTIFY_WORKFLOW,
WEBHOOK_TRIGGER_WORKFLOW,
WORKFLOW_CALL_WORKFLOW,
} from '@/components/workflow-preview/examples'
export { OutputBundle } from '@/components/workflow-preview/output-bundle'
export type {
PreviewBlock,
PreviewTool,
PreviewWorkflow,
} from '@/components/workflow-preview/workflow-data'
export { WorkflowPreview } from '@/components/workflow-preview/workflow-preview'
@@ -0,0 +1,169 @@
'use client'
import { ChevronDown, Clipboard, Download, Search } from 'lucide-react'
import { blockTypeToIconMap } from '@/components/ui/icon-mapping'
import { BLOCK_ICONS } from '@/components/workflow-preview/block-icons'
type ValueType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'null'
/** Dark-theme equivalents of the app's type badges (string=green, number=blue, …). */
const BADGE_COLORS: Record<ValueType, { bg: string; text: string }> = {
string: { bg: 'var(--wp-badge-success-bg)', text: 'var(--wp-badge-success-text)' },
number: { bg: 'var(--wp-badge-blue-bg)', text: 'var(--wp-badge-blue-text)' },
boolean: { bg: 'var(--wp-badge-orange-bg)', text: 'var(--wp-badge-orange-text)' },
array: { bg: 'var(--wp-badge-purple-bg)', text: 'var(--wp-badge-purple-text)' },
object: { bg: 'var(--wp-badge-gray-bg)', text: 'var(--wp-badge-gray-text)' },
null: { bg: 'var(--wp-badge-gray-bg)', text: 'var(--wp-badge-gray-text)' },
}
interface OutputNode {
key: string
type?: ValueType
/** Primitive value shown beneath the key when expanded. */
value?: string
/** Nested fields for object/array nodes. */
children?: OutputNode[]
/** Collapse the node (chevron points right, nothing rendered beneath). */
expanded?: boolean
/** Emphasize this row as the one being read by the tag below. */
highlight?: boolean
}
interface LogRow {
name: string
type?: string
color?: string
duration?: string
selected?: boolean
}
interface OutputBundleProps {
/** The block's name (unique within the workflow), e.g. "classify". */
blockName: string
blockType?: string
blockColor?: string
/** Duration shown on the selected log row. */
duration?: string
/** Override the Logs column; defaults to Start + this block (selected). */
logs?: LogRow[]
values: OutputNode[]
}
function resolveIcon(type: string) {
return BLOCK_ICONS[type] ?? blockTypeToIconMap[type] ?? null
}
function TypeBadge({ type }: { type: ValueType }) {
const c = BADGE_COLORS[type]
return (
<span
className='rounded-[4px] px-[5px] py-px text-[10px] leading-[14px]'
style={{ background: c.bg, color: c.text }}
>
{type}
</span>
)
}
function TreeNode({ node, depth = 0 }: { node: OutputNode; depth?: number }) {
const type = node.type ?? 'string'
const expanded = node.expanded ?? Boolean(node.children || node.value !== undefined)
return (
<div className='flex min-w-0 flex-col'>
<div className='flex min-h-[26px] items-center gap-2 rounded-[6px] px-1'>
<span
className='text-[13px]'
style={{ color: node.highlight ? '#33b4ff' : 'var(--wp-text)' }}
>
{node.key}
</span>
<TypeBadge type={type} />
<ChevronDown
className='h-[7px] w-[9px] flex-shrink-0 text-[var(--wp-text-muted)]'
style={expanded ? undefined : { transform: 'rotate(-90deg)' }}
/>
</div>
{expanded && (node.children || node.value !== undefined) && (
<div className='mt-0.5 ml-[5px] flex min-w-0 flex-col gap-0.5 border-[var(--wp-divider)] border-l pl-[10px]'>
{node.children
? node.children.map((child) => (
<TreeNode key={child.key} node={child} depth={depth + 1} />
))
: node.value !== undefined && (
<div className='py-0.5 text-[13px] text-[var(--wp-text-2)]'>{node.value}</div>
)}
</div>
)}
</div>
)
}
/**
* A miniature of the app's run inspector — the Logs list beside the Output
* panel's typed tree — teaching what a block's output is: named, typed values
* remembered under the block's name, read with a `<blockName.key>` tag.
*/
export function OutputBundle({
blockName,
blockType = 'agent',
blockColor = '#33C482',
duration = '1.2s',
logs,
values,
}: OutputBundleProps) {
const logRows: LogRow[] = logs ?? [
{ name: 'Start', type: 'start_trigger', color: '#2FB3FF', duration: '9ms' },
{ name: blockName, type: blockType, color: blockColor, duration, selected: true },
]
return (
<div className='wp-scope not-prose my-6 flex w-full max-w-[640px] flex-col gap-3'>
<div className='flex overflow-hidden rounded-xl border border-[var(--wp-border)] bg-[var(--wp-panel)]'>
<div className='flex w-[210px] flex-shrink-0 flex-col border-[var(--wp-border)] border-r px-2 py-2'>
<div className='px-2 pb-2 text-[12px] text-[var(--wp-text-muted)]'>Logs</div>
{logRows.map((row) => {
const Icon = row.type ? resolveIcon(row.type) : null
return (
<div
key={row.name}
className='flex h-[30px] items-center gap-2 rounded-[6px] px-2'
style={row.selected ? { background: 'var(--wp-active)' } : undefined}
>
<div
className='flex size-[18px] flex-shrink-0 items-center justify-center rounded-[5px]'
style={{ background: row.color ?? 'var(--wp-border-1)' }}
>
{Icon && <Icon className='size-[10px] text-white' />}
</div>
<span className='truncate text-[13px] text-[var(--wp-text)]'>{row.name}</span>
{row.duration && (
<span className='ml-auto text-[12px] text-[var(--wp-text-muted)]'>
{row.duration}
</span>
)}
</div>
)
})}
</div>
<div className='flex min-w-0 flex-1 flex-col px-3 py-2'>
<div className='flex items-center gap-3 pb-2'>
<span className='text-[13px] text-[var(--wp-text)]'>Output</span>
<span className='text-[13px] text-[var(--wp-text-muted)]'>Input</span>
<span className='ml-auto flex items-center gap-2 text-[var(--wp-text-subtle)]'>
<Search className='size-[12px]' />
<Clipboard className='size-[12px]' />
<Download className='size-[12px]' />
<ChevronDown className='size-[12px]' />
</span>
</div>
<div className='flex min-w-0 flex-col gap-0.5'>
{values.map((node) => (
<TreeNode key={node.key} node={node} />
))}
</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,215 @@
'use client'
import { memo } from 'react'
import { domAnimation, LazyMotion, m } from 'framer-motion'
import { Handle, type NodeProps, Position } from 'reactflow'
import { blockTypeToIconMap } from '@/components/ui/icon-mapping'
import { BLOCK_ICONS } from '@/components/workflow-preview/block-icons'
import {
BLOCK_STAGGER,
EASE_OUT,
type PreviewTool,
} from '@/components/workflow-preview/workflow-data'
/** Core-block glyph first, then the integration icon map so diagrams can show tools too. */
function resolveIcon(type: string) {
return BLOCK_ICONS[type] ?? blockTypeToIconMap[type] ?? null
}
interface PreviewBlockData {
name: string
blockType: string
bgColor: string
rows: Array<{ title: string; value: string }>
branches?: Array<{ id: string; label: string; value?: string }>
showError?: boolean
tools?: PreviewTool[]
hideTargetHandle?: boolean
hideSourceHandle?: boolean
index?: number
animate?: boolean
isHighlighted?: boolean
isDimmed?: boolean
}
/**
* Handle styling matching the real WorkflowBlock handles (--wp-edge mirrors
* the app's --workflow-edge in both modes).
*/
const HANDLE_BASE = '!z-[10] !border-none !bg-[var(--wp-edge)]'
const HANDLE_LEFT = `${HANDLE_BASE} !left-[-8px] !h-5 !w-[7px] !rounded-r-none !rounded-l-[2px]`
const HANDLE_RIGHT = `${HANDLE_BASE} !right-[-8px] !h-5 !w-[7px] !rounded-l-none !rounded-r-[2px]`
const HANDLE_BRANCH =
'!z-[10] !border-none !right-[-16px] !h-5 !w-[7px] !rounded-l-none !rounded-r-[2px] !bg-[var(--wp-edge)]'
const HANDLE_ERROR =
'!z-[10] !border-none !right-[-16px] !h-5 !w-[7px] !rounded-l-none !rounded-r-[2px] !bg-[#ef4444]'
/**
* Static preview block node matching the real WorkflowBlock styling.
* Renders a header (icon + name), sub-block rows, and tool chips.
*
* Colors come from preview-theme.module.css, mirroring the app's tokens in
* both light and dark mode.
*/
export const PreviewBlockNode = memo(function PreviewBlockNode({
data,
}: NodeProps<PreviewBlockData>) {
const {
name,
blockType,
bgColor,
rows,
branches,
showError,
tools,
hideTargetHandle,
hideSourceHandle,
index = 0,
animate = false,
isHighlighted = false,
isDimmed = false,
} = data
const Icon = resolveIcon(blockType)
const delay = animate ? index * BLOCK_STAGGER : 0
const hasBranches = Boolean(branches && branches.length > 0)
const hasContent =
rows.length > 0 || hasBranches || Boolean(showError) || (tools && tools.length > 0)
/* A block with branch handles emits from them, not from the header handle. */
const showHeaderSource = !hideSourceHandle && !hasBranches
return (
<LazyMotion features={domAnimation}>
<m.div
className='relative transition-opacity duration-300'
style={{ opacity: isDimmed ? 0.35 : 1 }}
initial={animate ? { opacity: 0 } : false}
animate={{ opacity: isDimmed ? 0.35 : 1 }}
transition={{ duration: 0.45, delay, ease: EASE_OUT }}
>
<div className='relative z-[20] w-[250px] select-none rounded-[8px] border border-[var(--wp-border-1)] bg-[var(--wp-surface)]'>
{isHighlighted && (
<div className='pointer-events-none absolute inset-0 z-40 rounded-[8px] ring-2 ring-[#33b4ff]' />
)}
{!hideTargetHandle && (
<Handle
type='target'
position={Position.Left}
id='target'
className={HANDLE_LEFT}
style={{ top: '20px', transform: 'translateY(-50%)' }}
isConnectableStart={false}
isConnectableEnd={false}
/>
)}
<div
className={`flex items-center justify-between p-2 ${hasContent ? 'border-[var(--wp-border-1)] border-b' : ''}`}
>
<div className='relative z-10 flex min-w-0 flex-1 items-center gap-2.5'>
<div
className='flex size-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
style={{ background: bgColor }}
>
{Icon && <Icon className='size-[16px] text-white' />}
</div>
<span className='truncate font-medium text-[16px] text-[var(--wp-text)]'>{name}</span>
</div>
</div>
{hasContent && (
<div className='flex flex-col gap-2 p-2'>
{rows.map((row) => (
<div key={row.title} className='flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)] capitalize'>
{row.title}
</span>
{row.value && (
<span className='flex min-w-0 flex-1 items-center justify-end gap-2 font-normal text-[14px] text-[var(--wp-text)]'>
<span className='truncate'>{row.value}</span>
</span>
)}
</div>
))}
{branches?.map((branch) => (
<div key={branch.id} className='relative flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)] capitalize'>
{branch.label}
</span>
<span className='flex min-w-0 flex-1 items-center justify-end font-normal text-[14px] text-[var(--wp-text)]'>
<span className='truncate'>{branch.value ?? '-'}</span>
</span>
<Handle
type='source'
position={Position.Right}
id={branch.id}
className={HANDLE_BRANCH}
isConnectableStart={false}
isConnectableEnd={false}
/>
</div>
))}
{showError && (
<div className='relative flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)] capitalize'>
error
</span>
<Handle
type='source'
position={Position.Right}
id='error'
className={HANDLE_ERROR}
isConnectableStart={false}
isConnectableEnd={false}
/>
</div>
)}
{tools && tools.length > 0 && (
<div className='flex items-center gap-2'>
<span className='flex-shrink-0 font-normal text-[14px] text-[var(--wp-text-3)]'>
Tools
</span>
<div className='flex flex-1 flex-wrap items-center justify-end gap-[5px]'>
{tools.map((tool) => {
const ToolIcon = resolveIcon(tool.type)
return (
<div
key={tool.type}
className='inline-flex h-5 items-center gap-1.5 rounded-md bg-[var(--wp-chip-bg)] px-1 shadow-[inset_0_0_0_1px_var(--wp-border-1)]'
>
<div
className='flex size-[14px] flex-shrink-0 items-center justify-center rounded-[4px]'
style={{ background: tool.bgColor }}
>
{ToolIcon && <ToolIcon className='size-[9px] text-white' />}
</div>
<span className='font-normal text-[12px] text-[var(--wp-chip-text)] leading-5'>
{tool.name}
</span>
</div>
)
})}
</div>
</div>
)}
</div>
)}
{showHeaderSource && (
<Handle
type='source'
position={Position.Right}
id='source'
className={HANDLE_RIGHT}
style={{ top: '20px', transform: 'translateY(-50%)' }}
isConnectableStart={false}
isConnectableEnd={false}
/>
)}
</div>
</m.div>
</LazyMotion>
)
})
@@ -0,0 +1,94 @@
'use client'
import { memo } from 'react'
import { Handle, type NodeProps, Position } from 'reactflow'
import { BLOCK_ICONS } from '@/components/workflow-preview/block-icons'
interface PreviewContainerData {
name: string
blockType: string
bgColor: string
rows: Array<{ title: string; value: string }>
hideTargetHandle?: boolean
hideSourceHandle?: boolean
}
const HANDLE_BASE = '!z-[10] !border-none !bg-[var(--wp-edge)]'
const HANDLE_LEFT = `${HANDLE_BASE} !left-[-8px] !h-5 !w-[7px] !rounded-r-none !rounded-l-[2px]`
const HANDLE_RIGHT = `${HANDLE_BASE} !right-[-8px] !h-5 !w-[7px] !rounded-l-none !rounded-r-[2px]`
const HANDLE_START = `${HANDLE_BASE} !right-[-8px] !h-4 !w-[7px] !rounded-l-none !rounded-r-[2px]`
/** Legible icon class for the header swatch — dark glyph on a light bg, white on a dark one. */
function iconClassFor(bg: string): string {
const hex = bg.replace('#', '')
if (hex.length !== 6) return 'text-white'
const r = Number.parseInt(hex.slice(0, 2), 16)
const g = Number.parseInt(hex.slice(2, 4), 16)
const b = Number.parseInt(hex.slice(4, 6), 16)
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
return luminance > 0.6 ? 'text-[#1c1c1c]' : 'text-white'
}
/**
* Container node for Loop / Parallel blocks, mirroring the app's subflow node:
* a solid-bordered box with a header (icon + name), an internal "Start" pill
* whose right handle feeds the first nested block, and target/source handles at
* the vertical center. React Flow positions the child blocks inside via parentNode.
*/
export const PreviewContainerNode = memo(function PreviewContainerNode({
data,
}: NodeProps<PreviewContainerData>) {
const { name, blockType, bgColor, hideTargetHandle, hideSourceHandle } = data
const Icon = BLOCK_ICONS[blockType]
return (
<div className='relative h-full w-full select-none rounded-[8px] border border-[var(--wp-border-1)] bg-[var(--wp-container-fill)]'>
{!hideTargetHandle && (
<Handle
type='target'
position={Position.Left}
id='target'
className={HANDLE_LEFT}
style={{ top: '50%', transform: 'translateY(-50%)' }}
isConnectableStart={false}
isConnectableEnd={false}
/>
)}
<div className='flex items-center gap-2.5 rounded-t-[8px] border-[var(--wp-border-1)] border-b bg-[var(--wp-header)] py-2 pr-3 pl-2'>
<div
className='flex size-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
style={{ background: bgColor }}
>
{Icon && <Icon className={`size-[16px] ${iconClassFor(bgColor)}`} />}
</div>
<span className='truncate font-medium text-[16px] text-[var(--wp-text)]'>{name}</span>
</div>
<div className='absolute top-[56px] left-4 flex items-center justify-center rounded-lg border border-[var(--wp-border-1)] bg-[var(--wp-header)] px-3 py-1.5'>
<span className='font-medium text-[13px] text-[var(--wp-text)]'>Start</span>
<Handle
type='source'
position={Position.Right}
id='loop-start-source'
className={HANDLE_START}
style={{ top: '50%', transform: 'translateY(-50%)' }}
isConnectableStart={false}
isConnectableEnd={false}
/>
</div>
{!hideSourceHandle && (
<Handle
type='source'
position={Position.Right}
id='source'
className={HANDLE_RIGHT}
style={{ top: '50%', transform: 'translateY(-50%)' }}
isConnectableStart={false}
isConnectableEnd={false}
/>
)}
</div>
)
})
@@ -0,0 +1,145 @@
import { type Edge, type Node, Position } from 'reactflow'
/**
* Tool entry displayed as a chip on a block (e.g. an Agent's attached tools).
*/
export interface PreviewTool {
name: string
type: string
bgColor: string
}
/**
* A single block in a preview workflow. Presentational shape — authored by hand
* for docs diagrams, not the app's full serialized block state.
*/
export interface PreviewBlock {
id: string
name: string
type: string
bgColor: string
rows: Array<{ title: string; value: string }>
/**
* Branch rows, each with its own right-edge source handle whose id is the
* branch id. Author ids in the app's own handle scheme so edges match the
* real workflow representation verbatim: `condition-<id>` on Condition
* blocks, `router-<routeId>` on Routers.
*/
branches?: Array<{ id: string; label: string; value?: string }>
/** Render an error row with a red source handle (id `error`). */
showError?: boolean
tools?: PreviewTool[]
position: { x: number; y: number }
hideTargetHandle?: boolean
hideSourceHandle?: boolean
/** When set, the block renders as a Loop/Parallel container sized to hold its children. */
size?: { width: number; height: number }
/** Id of the container block this block sits inside. Its position is relative to the container. */
parentId?: string
}
/**
* A workflow rendered as a read-only, app-styled diagram in the docs.
*/
export interface PreviewWorkflow {
id: string
name: string
blocks: PreviewBlock[]
edges: Array<{ id: string; source: string; target: string; sourceHandle?: string }>
}
export const BLOCK_STAGGER = 0.12
export const EASE_OUT: [number, number, number, number] = [0.16, 1, 0.3, 1]
const EDGE_STYLE = { stroke: 'var(--wp-edge)', strokeWidth: 1.5 } as const
const EDGE_STYLE_HIGHLIGHT = { stroke: '#33b4ff', strokeWidth: 2.5 } as const
/** Optional emphasis: light one block or one edge and dim everything else. */
export interface HighlightOptions {
highlightBlock?: string
highlightEdge?: string
/** Ring one block (selection) without dimming the rest. */
selectedBlock?: string
}
/**
* Converts a {@link PreviewWorkflow} to React Flow nodes and edges.
*
* @param workflow - The workflow definition
* @param animate - When true, node/edge data carries stagger metadata
* @param highlight - Optional block/edge to emphasize (dims the rest)
*/
export function toReactFlowElements(
workflow: PreviewWorkflow,
animate = false,
highlight: HighlightOptions = {}
): { nodes: Node[]; edges: Edge[] } {
const { highlightBlock, highlightEdge, selectedBlock } = highlight
const hasHighlight = Boolean(highlightBlock || highlightEdge)
const blockIndexMap = new Map(workflow.blocks.map((b, i) => [b.id, i]))
const blocksById = new Map(workflow.blocks.map((b) => [b.id, b]))
const nodes: Node[] = workflow.blocks.map((block, index) => {
const isContainer = Boolean(block.size)
// Nested blocks are authored relative to their container; render them at
// absolute coordinates (not React Flow parentNode children) so the edges
// between a container and its nested blocks render reliably and on top.
const parent = block.parentId ? blocksById.get(block.parentId) : undefined
const position = parent
? { x: parent.position.x + block.position.x, y: parent.position.y + block.position.y }
: block.position
return {
id: block.id,
type: isContainer ? 'previewContainer' : 'previewBlock',
position,
zIndex: isContainer ? 0 : 1,
...(block.size ? { style: { width: block.size.width, height: block.size.height } } : {}),
data: {
name: block.name,
blockType: block.type,
bgColor: block.bgColor,
rows: block.rows,
branches: block.branches,
showError: block.showError,
tools: block.tools,
hideTargetHandle: block.hideTargetHandle,
hideSourceHandle: block.hideSourceHandle,
index,
animate,
isHighlighted: highlightBlock === block.id || selectedBlock === block.id,
isDimmed: hasHighlight && highlightBlock !== block.id,
},
draggable: true,
selectable: false,
connectable: false,
sourcePosition: Position.Right,
targetPosition: Position.Left,
}
})
const edges: Edge[] = workflow.edges.map((e) => {
const sourceIndex = blockIndexMap.get(e.source) ?? 0
const isEdgeHighlight = highlightEdge === e.id
const dimmed = hasHighlight && !isEdgeHighlight
return {
id: e.id,
source: e.source,
target: e.target,
type: 'previewEdge',
animated: false,
style: {
...(isEdgeHighlight ? EDGE_STYLE_HIGHLIGHT : EDGE_STYLE),
opacity: dimmed ? 0.35 : 1,
},
sourceHandle: e.sourceHandle ?? 'source',
targetHandle: 'target',
data: {
animate,
delay: animate ? sourceIndex * BLOCK_STAGGER + BLOCK_STAGGER : 0,
},
}
})
return { nodes, edges }
}
@@ -0,0 +1,342 @@
'use client'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { domAnimation, LazyMotion, m } from 'framer-motion'
import { Maximize2, X } from 'lucide-react'
import ReactFlow, {
applyEdgeChanges,
applyNodeChanges,
Background,
BackgroundVariant,
type Edge,
type EdgeProps,
type EdgeTypes,
getSmoothStepPath,
type Node,
type NodeTypes,
type OnEdgesChange,
type OnNodesChange,
ReactFlowProvider,
} from 'reactflow'
import 'reactflow/dist/style.css'
import { BlockInspector } from '@/components/workflow-preview/block-inspector'
import { PreviewBlockNode } from '@/components/workflow-preview/preview-block-node'
import { PreviewContainerNode } from '@/components/workflow-preview/preview-container-node'
import {
EASE_OUT,
type PreviewBlock,
type PreviewWorkflow,
toReactFlowElements,
} from '@/components/workflow-preview/workflow-data'
interface WorkflowPreviewProps {
workflow: PreviewWorkflow
/** Canvas height in px. Default 260. */
height?: number
animate?: boolean
/** Emphasize one block by id, dimming the rest. */
highlightBlock?: string
/** Emphasize one edge by id, dimming the rest. */
highlightEdge?: string
}
/** Smooth-step edge, matching the app's connection styling. */
function PreviewEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
style,
data,
}: EdgeProps) {
const [edgePath] = getSmoothStepPath({
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
})
if (data?.animate) {
return (
<m.path
id={id}
className='react-flow__edge-path'
d={edgePath}
style={{ ...style, fill: 'none' }}
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{
pathLength: { duration: 0.4, delay: data.delay ?? 0, ease: EASE_OUT },
opacity: { duration: 0.15, delay: data.delay ?? 0 },
}}
/>
)
}
return (
<path
id={id}
className='react-flow__edge-path'
d={edgePath}
style={{ ...style, fill: 'none' }}
/>
)
}
const NODE_TYPES: NodeTypes = {
previewBlock: PreviewBlockNode,
previewContainer: PreviewContainerNode,
}
const EDGE_TYPES: EdgeTypes = { previewEdge: PreviewEdge }
const PRO_OPTIONS = { hideAttribution: true }
const FIT_VIEW_OPTIONS = { padding: 0.25, maxZoom: 1 } as const
const LIGHTBOX_FIT_VIEW_OPTIONS = { padding: 0.3, maxZoom: 1.4 } as const
/** Field titles rendered as multiline text in the inspector. */
const TEXTAREA_TITLES = new Set(['Messages', 'Prompt', 'Code', 'Data', 'Body', 'Display'])
/** Field titles rendered as dropdowns in the inspector. */
const SELECT_TITLES = new Set([
'Model',
'Operation',
'Method',
'Unit',
'Event type',
'Validation',
'Account',
'Table',
'Knowledge Base',
'Language',
'Workflow',
'Format',
])
function inspectorFieldsFor(block: PreviewBlock) {
const rowFields = block.rows.map((row) => ({
label: row.title,
kind:
TEXTAREA_TITLES.has(row.title) || row.value.length > 40
? ('textarea' as const)
: SELECT_TITLES.has(row.title)
? ('select' as const)
: ('input' as const),
value: row.value,
}))
const branchFields = (block.branches ?? []).map((branch) => ({
label: branch.label,
kind: 'code' as const,
value: branch.value,
placeholder: '—',
}))
return [...rowFields, ...branchFields]
}
function PreviewFlow({
workflow,
animate = false,
highlightBlock,
highlightEdge,
selectedBlock,
interactive = false,
onNodeClick,
}: WorkflowPreviewProps & {
selectedBlock?: string
interactive?: boolean
onNodeClick?: (blockId: string) => void
}) {
const { nodes: initialNodes, edges: initialEdges } = useMemo(
() => toReactFlowElements(workflow, animate, { highlightBlock, highlightEdge, selectedBlock }),
[workflow, animate, highlightBlock, highlightEdge, selectedBlock]
)
const [nodes, setNodes] = useState<Node[]>(initialNodes)
const [edges, setEdges] = useState<Edge[]>(initialEdges)
useEffect(() => {
setNodes(initialNodes)
setEdges(initialEdges)
}, [initialNodes, initialEdges])
const onNodesChange: OnNodesChange = useCallback(
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
[]
)
const onEdgesChange: OnEdgesChange = useCallback(
(changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
[]
)
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={onNodeClick ? (_, node) => onNodeClick(node.id) : undefined}
nodeTypes={NODE_TYPES}
edgeTypes={EDGE_TYPES}
defaultEdgeOptions={{ type: 'previewEdge' }}
elementsSelectable={false}
nodesDraggable
nodesConnectable={false}
zoomOnScroll={interactive}
zoomOnDoubleClick={interactive}
panOnScroll={false}
zoomOnPinch={interactive}
panOnDrag
preventScrolling={interactive}
autoPanOnNodeDrag={false}
proOptions={PRO_OPTIONS}
minZoom={0.2}
fitView
fitViewOptions={interactive ? LIGHTBOX_FIT_VIEW_OPTIONS : FIT_VIEW_OPTIONS}
className='h-full w-full'
>
<Background variant={BackgroundVariant.Dots} gap={20} size={1} color='var(--wp-border)' />
</ReactFlow>
)
}
/**
* Read-only, app-styled workflow diagram for docs pages. Renders a
* {@link PreviewWorkflow} with ReactFlow — draggable, non-editable, no app
* runtime. Clicking a block (or the expand control) opens a full-screen
* lightbox with zoom and pan, plus a read-only inspector panel showing the
* selected block's full configuration — canvas rows truncate, the inspector
* doesn't.
*
* @example
* <WorkflowPreview workflow={CLASSIFY_WORKFLOW} />
*/
export function WorkflowPreview({
workflow,
height = 260,
animate = false,
highlightBlock,
highlightEdge,
}: WorkflowPreviewProps) {
const [expanded, setExpanded] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
useEffect(() => {
if (!expanded) return
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') setExpanded(false)
}
document.addEventListener('keydown', onKey)
const previousOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
return () => {
document.removeEventListener('keydown', onKey)
document.body.style.overflow = previousOverflow
}
}, [expanded])
const selectedBlock = selectedId
? (workflow.blocks.find((b) => b.id === selectedId) ?? null)
: null
const incoming = selectedBlock
? workflow.edges
.filter((e) => e.target === selectedBlock.id)
.map((e) => workflow.blocks.find((b) => b.id === e.source))
.filter((b): b is PreviewBlock => Boolean(b))
: []
const openWith = (blockId: string | null) => {
setSelectedId(blockId)
setExpanded(true)
}
return (
<LazyMotion features={domAnimation}>
<div
className='wp-scope not-prose group relative my-6 overflow-hidden rounded-xl border border-[var(--wp-border)] bg-[var(--wp-canvas)]'
style={{ height }}
>
<ReactFlowProvider key={`${workflow.id}-${highlightBlock ?? ''}-${highlightEdge ?? ''}`}>
<PreviewFlow
workflow={workflow}
animate={animate}
highlightBlock={highlightBlock}
highlightEdge={highlightEdge}
onNodeClick={(id) => openWith(id)}
/>
</ReactFlowProvider>
<button
type='button'
aria-label='Expand workflow preview'
onClick={() => openWith(null)}
className='absolute top-2 right-2 z-10 flex size-[28px] items-center justify-center rounded-[6px] border border-[var(--wp-border-1)] bg-[var(--wp-btn)] text-[var(--wp-text-muted)] opacity-0 transition-opacity duration-150 hover:text-[var(--wp-text)] group-hover:opacity-100'
>
<Maximize2 className='size-[13px]' />
</button>
</div>
{expanded && (
<div
className='fixed inset-0 z-50 flex items-center justify-center bg-black/70 p-6 backdrop-blur-sm'
onClick={() => setExpanded(false)}
onKeyDown={() => {}}
role='presentation'
>
<div
className='wp-scope relative flex h-[86vh] w-[92vw] overflow-hidden rounded-xl border border-[var(--wp-border)] bg-[var(--wp-canvas)]'
onClick={(e) => e.stopPropagation()}
onKeyDown={() => {}}
role='presentation'
>
<div className='relative min-w-0 flex-1'>
<div className='pointer-events-none absolute top-0 right-0 left-0 z-10 flex items-center justify-between px-4 py-3'>
<span className='text-[13px] text-[var(--wp-text-muted)]'>{workflow.name}</span>
<button
type='button'
aria-label='Close'
onClick={() => setExpanded(false)}
className='pointer-events-auto flex size-[28px] items-center justify-center rounded-[6px] border border-[var(--wp-border-1)] bg-[var(--wp-btn)] text-[var(--wp-text-muted)] transition-colors hover:text-[var(--wp-text)]'
>
<X className='size-[14px]' />
</button>
</div>
<ReactFlowProvider key={`${workflow.id}-lightbox`}>
<PreviewFlow
workflow={workflow}
highlightBlock={highlightBlock}
highlightEdge={highlightEdge}
selectedBlock={selectedId ?? undefined}
interactive
onNodeClick={(id) => setSelectedId(id)}
/>
</ReactFlowProvider>
</div>
<div className='w-[340px] flex-shrink-0 border-[var(--wp-border)] border-l'>
{selectedBlock ? (
<BlockInspector
embedded
name={selectedBlock.name}
type={selectedBlock.type}
color={selectedBlock.bgColor}
fields={inspectorFieldsFor(selectedBlock)}
tools={selectedBlock.tools}
connections={incoming.map((b) => ({
name: b.name,
type: b.type,
color: b.bgColor,
}))}
/>
) : (
<div className='flex h-full items-center justify-center px-6 text-center text-[13px] text-[var(--wp-text-muted)]'>
Select a block to see its full configuration
</div>
)}
</div>
</div>
</div>
)}
</LazyMotion>
)
}
@@ -0,0 +1,93 @@
---
title: Choosing what to use
description: Pick between a deterministic block, an agent tool, a custom tool, an MCP server, a workflow-as-tool, and a skill.
pageType: concept
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { WorkflowPreview, LEAD_SCORER_WORKFLOW } from '@/components/workflow-preview'
When you build an agent, several features overlap: a deterministic block and an agent tool can run the same integration, and a custom tool, an MCP tool, and a workflow-as-tool can all give an agent the same action. This page lays out the differences so you pick the right one. They vary along three lines: whether the action is **deterministic** (always runs) or **model-decided** (an agent chooses), whether it lives in one workflow or is **reusable** across your workspace, and whether it comes from Sim or an **external** provider.
The running example is a workflow that scores inbound sales leads. It reads a new lead, enriches it, decides on a score, logs the result, and notifies the team. Each option below builds part of it:
<WorkflowPreview workflow={LEAD_SCORER_WORKFLOW} height={210} />
{/* VISUAL: decision tree. Always happens? → deterministic block. Else agent chooses? → agent tool. Reuse across workspace? → custom tool. External toolset? → MCP. Whole workflow? → workflow-as-tool. Reusable instructions? → skill. */}
## Deterministic block
A **block** is a single step that runs at a fixed point on the path, with no model deciding whether to. It always runs when the workflow reaches it. Use one when the action must happen every time: an API call, a data transform, a branch.
In the lead scorer, a [Google Sheets](/integrations) block always appends the scored lead to a tracking sheet, and a [Function](/workflows/blocks/function) block always reshapes the enrichment response into the fields the next step expects. There is no judgment call. The work is guaranteed.
<WorkflowPreview workflow={LEAD_SCORER_WORKFLOW} height={170} highlightBlock="log" />
Most steps in a workflow are blocks. Reach for the kinds below only when you want a model to decide, or you want to reuse something.
## Agent tool
An **agent tool** is an action you hand to an [Agent](/workflows/blocks/agent) block. The agent reads the task and decides whether and when to call it. The same catalog of [integrations](/integrations) that exist as standalone blocks can also be attached to an agent as tools.
In the lead scorer, the Agent has a Search tool and a Send Email tool. For a lead with a thin profile it runs Search to gather context; for a strong lead it calls Send Email. A thin, obvious lead might trigger neither. The agent chooses per run.
<WorkflowPreview workflow={LEAD_SCORER_WORKFLOW} height={170} highlightBlock="score" />
Each tool carries a `usageControl` setting. **Auto** lets the model decide (the default). **Force** makes the agent call the tool every run, for actions that should never be skipped, like always logging the decision. **None** removes the tool from that agent.
<Callout type="info">
A block and an agent tool can be the same underlying integration. The difference is who decides. A block runs because the path reached it. An agent tool runs because the agent chose it.
</Callout>
## Custom tool
A **custom tool** is a tool you define once with an object schema and a snippet of JavaScript or Python, then reuse across your workspace. It needs no external account. Use one when you have logic that several agents or workflows would otherwise duplicate.
In the lead scorer, a `normalizeCompanyDomain` custom tool cleans a raw website into a canonical domain. The same tool serves the lead scorer, a deduplication workflow, and a reporting agent. Define it in the workspace, then pick it from any Agent block's tool list.
## MCP server
**MCP** (Model Context Protocol) is a standard for connecting an external tool provider. Connect an [MCP server](/agents/mcp) and its tools appear in the agent's tool list as a set. Use it to bring in a complete toolset that Sim does not provide natively, rather than wiring each action by hand.
In the lead scorer, your CRM vendor ships an MCP server. After you connect it once, the agent can read accounts and update records through the vendor's own tools. The difference from a custom tool is who maintains it: a custom tool is code you wrote, while an MCP server is a toolbox someone else maintains.
## Workflow-as-tool
A **workflow-as-tool** is a whole workflow handed to an agent as one callable tool. You pick the workflow in the [Agent](/workflows/blocks/agent) block's tool list; the agent decides when to call it and supplies the inputs, which arrive at the child's [Start](/workflows/triggers/start) trigger, and the child's result comes back as the tool's output. Use it when a multi-step procedure should be at the agent's disposal, not on the path.
In the lead scorer, the agent has a `Deep Enrich` workflow as a tool — its own five-step procedure. For a thin lead, the agent calls it to fill out the profile before scoring; for a complete lead, it never runs. The agent weighs a whole procedure the same way it weighs a single action.
<Callout type="info">
A workflow can also run as a fixed step: the [Workflow](/workflows/blocks/workflow) block calls a child workflow because the path reached it. Same child workflow, same Start trigger — the difference, as with blocks and agent tools, is who decides. The Enrich step in the diagram is that deterministic case.
</Callout>
<WorkflowPreview workflow={LEAD_SCORER_WORKFLOW} height={170} highlightBlock="enrich" />
## Skill
A **skill** is reusable instructions, a written playbook an agent can follow. Each skill has a short name and description that are always visible to the agent, plus a longer body the agent loads only when it decides the skill applies. Use one to capture how something should be done, separate from the tools that do it.
In the lead scorer, a `lead-scoring-rubric` skill spells out the bands and disqualifiers. The agent sees the skill's name and description on every run, and when a lead is ambiguous it loads the full rubric and applies it. The distinction is simple: a tool is an action the agent takes, and a skill is guidance the agent reads. Manage skills in your [workspace](/agents/skills).
| | Who decides it runs | Where it lives | How you author it |
| --- | --- | --- | --- |
| **Block** | The path | The workflow | Drag in and configure |
| **Agent tool** | The agent | On the Agent block | Pick from the integrations |
| **Custom tool** | The agent | The workspace | Write the code once |
| **MCP server** | The agent | An external server | Connect it |
| **Workflow-as-tool** | The agent | Its own workflow | Build it, then pick it in the tool list |
| **Skill** | The agent | The workspace | Write the instructions |
## Putting it together
The lead scorer uses six kinds at once: blocks for the steps that must always run, agent tools for the calls the model should weigh, a custom tool for shared logic, an MCP server for the CRM, a deep-enrichment workflow the agent calls when a lead needs it, and a skill for the scoring rules. In general, start with a block, then move to an agent tool when a model should decide. Reach for the rest only when you need reuse, an external toolset, a sub-workflow, or written guidance.
## Next
<Cards>
<Card title="Building agents" href="/agents" description="The features that let an agent act, follow instructions, and use your data." />
<Card title="Agent block reference" href="/workflows/blocks/agent" description="Every model, setting, and output field." />
<Card title="Using MCP tools" href="/agents/mcp" description="Connect an external MCP server to your agents." />
<Card title="Skills" href="/agents/skills" description="Write reusable instructions agents can load." />
</Cards>
@@ -0,0 +1,163 @@
---
title: Custom Tools
description: Create your own tools with JavaScript code and use them in Agent blocks
pageType: guide
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { FAQ } from '@/components/ui/faq'
Custom tools let you write your own JavaScript functions and make them available as callable tools in Agent blocks. This is useful when you need functionality that isn't covered by Sim's built-in integrations — for example, calling an internal API, performing a custom calculation, or transforming data in a specific way.
## How Custom Tools Work
A custom tool has two parts:
1. **Schema** — A JSON definition describing the tool's name, description, and parameters (using the OpenAI function-calling format). This tells the AI agent what the tool does and what inputs it expects.
2. **Code** — A JavaScript function body that runs when the agent calls the tool. Parameters defined in the schema are available as variables in your code.
When an Agent block has access to a custom tool, the AI model decides when to call it based on the schema description and the conversation context — just like built-in tools.
## Creating a Custom Tool
<Steps>
<Step>
### Open Custom Tools settings
Navigate to **Settings → Custom Tools** in your workspace and click **Add**.
</Step>
<Step>
### Define the schema
In the **Schema** tab, define your tool using JSON in the OpenAI function-calling format:
```json
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature units"
}
},
"required": ["city"]
}
}
}
```
<Callout type="info">
You can use the AI wand button to generate a schema from a natural language description of what the tool should do.
</Callout>
</Step>
<Step>
### Write the code
Switch to the **Code** tab and write the JavaScript function body. Parameters from your schema are available directly as variables:
```javascript
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${units === 'celsius' ? 'metric' : 'imperial'}&appid={{OPENWEATHER_API_KEY}}`
);
const data = await response.json();
return {
temperature: data.main.temp,
description: data.weather[0].description,
humidity: data.main.humidity
};
```
<Callout type="info">
You can also use the AI wand to generate code from a description. Environment variables are referenced with `&#123;&#123;KEY&#125;&#125;` syntax.
</Callout>
</Step>
<Step>
### Save
Click **Save** to create the tool. It's now available to use in any Agent block across your workspace.
</Step>
</Steps>
## Using Custom Tools in Workflows
Once created, custom tools appear alongside built-in tools when configuring an Agent block:
1. Open an Agent block
2. Click **Add Tools**
3. Find your custom tool in the tool list
4. The agent will call the tool when it determines it's relevant to the task
## Code Environment
### Available Features
- **Async/await** — Your code runs in an async context, so you can use `await` directly
- **fetch()** — Make HTTP requests to external APIs
- **Node.js built-ins** — Access to `crypto`, `Buffer`, and other standard modules
- **Environment variables** — Use `{{KEY}}` syntax to inject secrets
### Limitations
- **No npm packages** — External libraries like `axios` or `lodash` are not available. Use built-in APIs instead
- **Parameters by name** — Schema parameters are available directly as variables (e.g., `city`), not via a `params` object
### Returning Results
Return a value from your code to send it back to the agent:
```javascript
const result = await fetch(`https://api.example.com/data?q=${query}`);
const data = await result.json();
return data;
```
The returned value becomes the tool output that the agent sees and can use in its response.
## Managing Custom Tools
From **Settings → Custom Tools** you can:
- **Search** tools by name, function name, or description
- **Edit** any tool's schema or code
- **Delete** tools that are no longer needed
<Callout type="warn">
Deleting a custom tool removes it from all Agent blocks that reference it. Make sure no active workflows depend on the tool before deleting.
</Callout>
## Permissions
| Action | Required Permission |
|--------|-------------------|
| View custom tools | **Read**, **Write**, or **Admin** |
| Create or edit tools | **Write** or **Admin** |
| Delete tools | **Admin** |
<FAQ items={[
{ question: "Can I use custom tools in standalone blocks (not agents)?", answer: "No. Custom tools are designed for use within Agent blocks, where the AI model decides when to call them. For deterministic tool execution, use the Function block instead." },
{ question: "How do I pass API keys to my custom tool code?", answer: "Use environment variables with double curly brace syntax: {{MY_API_KEY}}. Create the environment variable in Settings → Secrets, and it will be injected at execution time without appearing in logs." },
{ question: "Can I use external npm packages?", answer: "No. Custom tool code runs in a sandboxed environment with access to built-in Node.js modules and fetch(), but not external packages. For complex dependencies, consider calling an external API that wraps the functionality you need." },
{ question: "What's the difference between custom tools and the Function block?", answer: "Custom tools are called by AI agents when they decide the tool is relevant — the agent chooses when to use it. Function blocks run deterministically at a fixed point in the workflow. Use custom tools for agent-driven actions and Function blocks for predictable data transformations." },
{ question: "Are custom tools shared across the workspace?", answer: "Yes. Custom tools are workspace-scoped, so all workspace members can use them in their workflows." },
]} />
@@ -0,0 +1,73 @@
---
title: Overview
description: An agent is a workflow that reasons and acts, built around Agent blocks and the features you give them.
pageType: concept
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { WorkflowPreview, BUILD_AGENT_WORKFLOW } from '@/components/workflow-preview'
An **agent** is a [workflow](/workflows) that reasons and acts on its own. It reads an input, decides what to do, and carries it out, calling tools and using your data along the way. You build a custom agent in Sim by composing a workflow whose thinking runs through one or more [Agent blocks](/workflows/blocks/agent).
An **Agent block** is the reasoning step inside that workflow: a model reads the values available to it, decides, and returns a result that later blocks read by reference. A simple agent is a single Agent block; a larger one wires several together with other blocks. The Agent block is where the model thinks; the rest of the workflow is what it acts on and through.
<WorkflowPreview workflow={BUILD_AGENT_WORKFLOW} height={200} highlightBlock="agent" />
The example throughout is an agent that scores inbound sales leads.
## The Agent block
You set up the reasoning step by giving the Agent block a **model** and a **prompt**. The model is the LLM that powers it; you pick one from the available providers, and the default is `claude-sonnet-4-6`. The prompt is a system message that defines who the agent is and how it should behave, plus a user message that carries the input, usually a reference like `<start.input>`.
When it runs, the Agent block reasons, calls any tools it needs, and stores its result under its own name. By default that result is free text in `content`, read by a later block as `<agent.content>`, alongside run details like the model used, token counts, tool calls, and cost. Every setting and output field is in the [Agent block reference](/workflows/blocks/agent).
## What you give an agent
On its own, an Agent block can only reason and write text. You extend it so it can act, follow your rules, use your data, remember, and return results other blocks can rely on. Each feature maps to something you'd want an agent to do.
### Take an action: tools
To let an agent do something in the world, give it **tools**. A tool is an action the agent can call, like sending an email, searching the web, updating a CRM record, or running another workflow. You attach tools to the Agent block, and the agent decides which to call for the task in front of it. In the lead scorer, the agent has a search tool to gather context on a thin profile and an email tool to reach out to a strong lead.
Tools come from a few places:
- **[Integrations](/integrations)** are the catalog of external services: Gmail, Slack, Airtable, Linear, and hundreds more.
- **[Custom tools](/agents/custom-tools)** are tools you define once with a schema and a snippet of code, then reuse.
- **[MCP tools](/agents/mcp)** come from an external provider you connect through the Model Context Protocol.
- **[Workflow-as-tool](/workflows)** makes another workflow callable, so the agent runs a whole procedure as one step.
<Callout type="info">
The same integration can run two ways. As a [block](/workflows#blocks) it runs because the path reached it. As an agent tool it runs because the agent chose it. Per-tool usage controls (force a tool, or disable one) are in the [Agent block reference](/workflows/blocks/agent).
</Callout>
### Follow a procedure: skills
To give an agent instructions it can follow, write a [skill](/agents/skills). A skill is a reusable playbook with a short name and description the agent always sees, plus a longer body it loads only when the skill applies. In the lead scorer, a `lead-scoring-rubric` skill spells out the bands and disqualifiers, and the agent reads the full rubric only when a lead is ambiguous. A tool is an action the agent takes; a skill is guidance it reads.
### Use your documents: knowledge
To let an agent answer from your own content, connect a [knowledge base](/knowledgebase). The agent searches it and grounds its answers in what it finds, instead of relying only on the model's general training. Give the lead scorer a knowledge base of past deals and it can compare a new lead against ones you've closed before.
### Remember across runs: memory
To let an agent reuse information from one run to the next, give it [memory](/integrations/memory), which stores and recalls values keyed to a conversation. Without it, each run starts fresh; with it, an agent in a chat carries what was said earlier into later messages.
### Return a usable result: structured output
To make an agent's result something later blocks can act on, give it a **structured output**: a typed object you define instead of free text. In the lead scorer, the agent returns `{ score, tier, reason }`, and a later [Condition](/workflows/blocks/condition) block reads `<agent.tier>` to branch. See [how blocks pass data](/workflows/data-flow) for reading fields.
## Choosing what to use
Start with one Agent block and a prompt, then add only what the task needs: a tool when the agent should act, a skill when it needs written guidance, a knowledge base when it should answer from your documents, memory when it should remember, and a structured output when a later block has to read its result.
<Card title="Choosing what to use" href="/agents/choosing" description="A side-by-side guide to tools, skills, knowledge, and the rest, with one worked example." />
## Next
<Cards>
<Card title="Agent block reference" href="/workflows/blocks/agent" description="Every model, setting, and output field of the Agent block." />
<Card title="Choosing what to use" href="/agents/choosing" description="Pick the right feature for each task." />
<Card title="Custom tools" href="/agents/custom-tools" description="Define a reusable tool with a schema and code." />
<Card title="Using MCP tools" href="/agents/mcp" description="Connect an external MCP server to your agents." />
</Cards>
@@ -1,22 +1,14 @@
---
title: Using MCP Tools
title: Using MCP tools
description: Connect external tools and services using the Model Context Protocol
pageType: guide
---
import { Image } from '@/components/ui/image'
import { Video } from '@/components/ui/video'
import { Callout } from 'fumadocs-ui/components/callout'
The Model Context Protocol ([MCP](https://modelcontextprotocol.com/)) allows you to connect external tools and services using a standardized protocol, enabling you to integrate APIs and services directly into your workflows. With MCP, you can extend Sim's capabilities by adding custom integrations that work seamlessly with your agents and workflows.
## What is MCP?
MCP is an open standard that enables AI assistants to securely connect to external data sources and tools. It provides a standardized way to:
- Connect to databases, APIs, and file systems
- Access real-time data from external services
- Execute custom tools and scripts
- Maintain secure, controlled access to external resources
The Model Context Protocol ([MCP](https://modelcontextprotocol.com/)) is an open standard for connecting AI to external tools and data. Add an MCP server to your workspace and its tools become available to your agents — a way to integrate services Sim doesn't have a built-in integration for.
## Adding an MCP Server as a Tool
@@ -92,10 +84,6 @@ Tool validation badges appear on servers with issues — for example, if a tool
Self-hosted deployments can restrict which MCP server domains are allowed by setting the `ALLOWED_MCP_DOMAINS` environment variable (comma-separated list). When set, only servers on approved domains can be added. When unset, all domains are allowed.
### Refresh Tools
To auto-refresh an MCP tool already in use by an agent, go to **Settings → MCP Tools**, open the server's details, and click **Refresh**. This fetches the latest tool schemas and automatically updates any agent blocks using those tools with the new parameter definitions.
## Using MCP Tools in Agents
Once MCP servers are configured, their tools become available within your agent blocks:
@@ -145,11 +133,7 @@ For more granular control, you can use the dedicated MCP Tool block to execute s
/>
</div>
The MCP Tool block allows you to:
- Execute any configured MCP tool directly
- Pass specific parameters to the tool
- Use the tool's output in subsequent workflow steps
- Chain multiple MCP tools together
The MCP Tool block runs one configured tool with parameters you set explicitly, and its output is readable by later blocks like any other.
## When to Use MCP Tool vs Agent
@@ -172,46 +156,9 @@ MCP functionality requires specific workspace permissions:
| View available MCP tools | **Read**, **Write**, or **Admin** |
| Execute MCP Tool blocks | **Read**, **Write**, or **Admin** |
## Common Use Cases
### Database Integration
Connect to databases to query, insert, or update data within your workflows.
### API Integrations
Access external APIs and web services that don't have built-in Sim integrations.
### File System Access
Read, write, and manipulate files on local or remote file systems.
### Custom Business Logic
Execute custom scripts or tools specific to your organization's needs.
### Real-time Data Access
Fetch live data from external systems during workflow execution.
## Security Considerations
- MCP servers run with the permissions of the user who configured them
- Always verify MCP server sources before installation
- Use environment variables for sensitive configuration data
- Review MCP server capabilities before granting access to agents
## Troubleshooting
### MCP Server Not Appearing
- Verify the server configuration is correct
- Check that you have the required permissions
- Ensure the MCP server is running and accessible
### Tool Execution Failures
- Verify tool parameters are correctly formatted
- Check MCP server logs for error messages
- Ensure required authentication is configured
### Permission Errors
- Confirm your workspace permission level
- Check if the MCP server requires additional authentication
- Verify the server is properly configured for your workspace
<Callout type="warn">
MCP servers run with the permissions of the user who configured them — only connect servers you trust, and review a server's tools before handing them to agents.
</Callout>
import { FAQ } from '@/components/ui/faq'
@@ -1,5 +1,7 @@
---
title: Agent Skills
title: Agent skills
description: Reusable instruction packages your agents load on demand, managed from the Skills tab.
pageType: guide
---
import { Callout } from 'fumadocs-ui/components/callout'
@@ -14,15 +16,15 @@ Skills use **progressive disclosure** to keep agent context lean:
2. **Activation** — When the agent decides a skill is relevant, it calls the `load_skill` tool to load the full instructions into context
3. **Execution** — The agent follows the loaded instructions to complete the task
This means you can attach many skills to an agent without bloating its context window. The agent only loads what it needs.
## Creating Skills
Go to **Settings** and select **Skills** under the Tools section.
Skills live on the **Integrations** page: click **Integrations** in the workspace sidebar, then switch to the **Skills** tab. It lists every skill in the workspace, searchable by name.
![Manage Skills](/static/skills/manage-skills.png)
![The Skills tab on the Integrations page](/static/skills/skills-tab.png)
Click **Add** to create a new skill with three fields:
Click **+ Add to Sim** to open the **Add Skill** dialog. The **Create** tab takes three fields:
![The Add Skill dialog, Create tab](/static/skills/add-skill-create.png)
| Field | Description |
|-------|-------------|
@@ -34,6 +36,18 @@ Click **Add** to create a new skill with three fields:
The description is critical — it's the only thing the agent sees before deciding to load a skill. Be specific about when and why the skill should be used.
</Callout>
### Importing skills
The **Import** tab brings in an existing skill in the open [SKILL.md](https://agentskills.io/specification) format, three ways:
![The Add Skill dialog, Import tab](/static/skills/add-skill-import.png)
- **Upload a file** — a `.md` file with YAML frontmatter, or a `.zip` containing a `SKILL.md`.
- **Import from GitHub** — paste a GitHub URL to a `SKILL.md` and click **Fetch**.
- **Paste content** — paste the `SKILL.md` directly. The frontmatter carries the `name` and `description`; the markdown body is the content.
Integration pages suggest **curated skills** for their service — open one (HubSpot, for example) and add a suggested skill with one click.
### Writing Good Skill Content
Skill content follows the same conventions as [SKILL.md files](https://agentskills.io/specification):
@@ -142,5 +156,5 @@ import { FAQ } from '@/components/ui/faq'
{ question: "When should I use skills vs. agent instructions?", answer: "Use skills for knowledge that applies across multiple workflows or changes frequently. Skills are reusable packages that can be attached to any agent. Use agent instructions for task-specific context that is unique to a single agent and workflow. If you find yourself copying the same instructions into multiple agents, that content should be a skill instead." },
{ question: "Can permission groups disable skills for certain users?", answer: "Yes. On Enterprise-entitled workspaces, any workspace admin can create a permission group with the disableSkills option enabled. When a user is assigned to such a group in a workspace, the skills dropdown in agent blocks is disabled and they cannot add or use skills in workflows belonging to that workspace." },
{ question: "What is the recommended maximum length for skill content?", answer: "Keep skills focused and under 500 lines. If a skill grows too large, split it into multiple specialized skills. Shorter, focused skills are more effective because the agent can load exactly what it needs. A broad skill with too much content can overwhelm the agent and reduce the quality of its responses." },
{ question: "Where do I create and manage skills?", answer: "Go to Settings and select Skills under the Tools section. From there you can add new skills with a name (kebab-case identifier, max 64 characters), description (max 1024 characters), and content (full instructions in markdown). You can also edit or delete existing skills from this page." },
{ question: "Where do I create and manage skills?", answer: "Click Integrations in the workspace sidebar and switch to the Skills tab. Add Skill creates one from a name (kebab-case, max 64 characters), description (max 1024 characters), and markdown content — or imports an existing SKILL.md from a file, a GitHub URL, or pasted content. Existing skills are edited and deleted from the same tab." },
]} />
-171
View File
@@ -1,171 +0,0 @@
---
title: Agent
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Agent block connects your workflow to Large Language Models (LLMs). It processes natural language inputs, calls external tools, and generates structured or unstructured outputs.
<div className="flex justify-center">
<Image
src="/static/blocks/agent.png"
alt="Agent Block Configuration"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration Options
### System Prompt
The system prompt establishes the agent's operational parameters and behavioral constraints. This configuration defines the agent's role, response methodology, and processing boundaries for all incoming requests.
```markdown
You are a helpful assistant that specializes in financial analysis.
Always provide clear explanations and cite sources when possible.
When responding to questions about investments, include risk disclaimers.
```
### User Prompt
The user prompt represents the primary input data for inference processing. This parameter accepts natural language text or structured data that the agent will analyze and respond to. Input sources include:
- **Static Configuration**: Direct text input specified in the block configuration
- **Dynamic Input**: Data passed from upstream blocks through connection interfaces
- **Runtime Generation**: Programmatically generated content during workflow execution
### Model Selection
The Agent block supports multiple LLM providers through a unified inference interface. Available models include:
- **OpenAI**: GPT-5.1, GPT-5, GPT-4o, o1, o3, o4-mini, gpt-4.1
- **Anthropic**: Claude 4.5 Sonnet, Claude Opus 4.1
- **Google**: Gemini 2.5 Pro, Gemini 2.0 Flash
- **Other Providers**: Groq, Cerebras, xAI, Azure OpenAI, OpenRouter
- **Local Models**: Ollama or VLLM compatible models
### Temperature
Controls response randomness and creativity:
- **Low (0-0.3)**: Deterministic and focused. Best for factual tasks and accuracy.
- **Medium (0.3-0.7)**: Balanced creativity and focus. Good for general use.
- **High (0.7-2.0)**: Creative and varied. Ideal for brainstorming and content generation.
### Max Output Tokens
Controls the maximum length of the model's response. Each model defaults to its full max output token limit (e.g., 64,000 tokens for Claude Sonnet 4.5). You can override this with a custom value using the Max Tokens setting. For Anthropic models, when non-streaming requests exceed the SDK's internal threshold, the provider automatically uses internal streaming to avoid timeouts.
### API Key
Your API key for the selected LLM provider. This is securely stored and used for authentication.
### Tools
Extend agent capabilities with external integrations. Select from 60+ pre-built tools or define custom functions.
**Available Categories:**
- **Communication**: Gmail, Slack, Telegram, WhatsApp, Microsoft Teams
- **Data Sources**: Notion, Google Sheets, Airtable, Supabase, Pinecone
- **Web Services**: Firecrawl, Google Search, Exa AI, browser automation
- **Development**: GitHub, Jira, Linear
- **AI Services**: OpenAI, Perplexity, Hugging Face, ElevenLabs
**Execution Modes:**
- **Auto**: Model decides when to use tools based on context
- **Required**: Tool must be called in every request
- **None**: Tool is completely filtered out and not sent to the model — effectively disables the tool
### Response Format
The Response Format parameter enforces structured output generation through JSON Schema validation. This ensures consistent, machine-readable responses that conform to predefined data structures:
```json
{
"name": "user_analysis",
"schema": {
"type": "object",
"properties": {
"sentiment": {
"type": "string",
"enum": ["positive", "negative", "neutral"]
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"required": ["sentiment", "confidence"]
}
}
```
This configuration constrains the model's output to comply with the specified schema, preventing free-form text responses and ensuring structured data generation.
### Accessing Results
After an agent completes, you can access its outputs:
- **`<agent.content>`**: The agent's response text or structured data
- **`<agent.tokens>`**: Token usage statistics (prompt, completion, total)
- **`<agent.toolCalls>`**: Details of any tools the agent used during execution
- **`<agent.cost>`**: Estimated cost of the API call (if available)
## Advanced Features
### Memory + Agent: Conversation History
Use a `Memory` block with a consistent `id` (for example, `chat`) to persist messages between runs, and include that history in the Agent's prompt.
- Add the user's message before the Agent
- Read the conversation history for context
- Append the Agent's reply after it runs
See the [`Memory`](/tools/memory) block reference for details.
## Outputs
- **`<agent.content>`**: Agent's response text
- **`<agent.model>`**: Model identifier used for the request
- **`<agent.tokens>`**: Token usage statistics
- **`<agent.toolCalls>`**: Tool execution details
- **`<agent.cost>`**: Estimated API call cost
## Example Use Cases
**Customer Support Automation** - Handle inquiries with database and tool access
```
API (Ticket) → Agent (Postgres, KB, Linear) → Gmail (Reply) → Memory (Save)
```
**Multi-Model Content Analysis** - Analyze content with different AI models
```
Function (Process) → Agent (GPT-4o Technical) → Agent (Claude Sentiment) → Function (Report)
```
**Tool-Powered Research Assistant** - Research with web search and document access
```
Input → Agent (Google Search, Notion) → Function (Compile Report)
```
## Best Practices
- **Be specific in system prompts**: Clearly define the agent's role, tone, and limitations. The more specific your instructions are, the better the agent will be able to fulfill its intended purpose.
- **Choose the right temperature setting**: Use lower temperature settings (0-0.3) when accuracy is important, or increase temperature (0.7-2.0) for more creative or varied responses
- **Leverage tools effectively**: Integrate tools that complement the agent's purpose and enhance its capabilities. Be selective about which tools you provide to avoid overwhelming the agent. For tasks with little overlap, use another Agent block for the best results.
<FAQ items={[
{ question: "What LLM providers does the Agent block support?", answer: "The Agent block supports OpenAI, Anthropic, Google (Gemini), xAI (Grok), DeepSeek, Groq, Cerebras, Azure OpenAI, Azure Anthropic, Google Vertex AI, AWS Bedrock, OpenRouter, and local models via Ollama or VLLM. You can type or select any supported model from the model combobox." },
{ question: "What are the memory options for the Agent block?", answer: "The Agent block has four memory modes: None (no memory, each request is independent), Conversation (full conversation history keyed by a conversation ID), Sliding Window by messages (keeps the N most recent messages), and Sliding Window by tokens (keeps messages up to a token limit). Memory requires a conversation ID to persist across runs." },
{ question: "What is the difference between the tool execution modes (Auto, Required, None)?", answer: "In Auto mode, the model decides when to call a tool based on context. In Required mode, the model must call the tool on every request. In None mode, the tool is completely filtered out and never sent to the model — it effectively disables that tool without removing it from the configuration." },
{ question: "How does the Response Format work?", answer: "Response Format enforces structured output by providing a JSON Schema. When set, the model's response is constrained to match the schema exactly. Fields from the structured response can be accessed directly by downstream blocks using <agent.fieldName> syntax. If no response format is set, the agent returns its standard outputs: content, model, tokens, and toolCalls." },
{ question: "What does the Reasoning Effort / Thinking Level setting do?", answer: "These are advanced settings that appear only for models that support extended reasoning. Reasoning Effort (for OpenAI o-series and GPT-5 models) and Thinking Level (for Anthropic Claude and Gemini models with thinking) control how much compute the model spends reasoning before responding. Higher levels produce more thorough answers but cost more tokens and take longer." },
{ question: "How does max output tokens work with Anthropic models specifically?", answer: "The Agent block uses each Anthropic model's full max output token limit by default (e.g., 64,000 for Claude Sonnet 4.5). You can override this with the Max Tokens setting. For non-streaming requests that exceed the SDK's internal threshold, the provider automatically uses internal streaming to avoid timeouts." },
{ question: "Can I use the Agent block with a custom or self-hosted model?", answer: "Yes. You can use any Ollama or VLLM-compatible model by typing the model name directly into the model combobox. This lets you connect to locally hosted or custom-deployed models as long as they expose a compatible API endpoint." },
]} />
-157
View File
@@ -1,157 +0,0 @@
---
title: API
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The API block connects your workflow to external services through HTTP requests. Supports GET, POST, PUT, DELETE, and PATCH methods for interacting with REST APIs.
<div className="flex justify-center">
<Image
src="/static/blocks/api.png"
alt="API Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration Options
### URL
The endpoint URL for the API request. This can be:
- A static URL entered directly in the block
- A dynamic URL connected from another block's output
- A URL with path parameters
### Method
Select the HTTP method for your request:
- **GET**: Retrieve data from the server
- **POST**: Send data to the server to create a resource
- **PUT**: Update an existing resource on the server
- **DELETE**: Remove a resource from the server
- **PATCH**: Partially update an existing resource
### Query Parameters
Define key-value pairs that will be appended to the URL as query parameters. For example:
```
Key: apiKey
Value: your_api_key_here
Key: limit
Value: 10
```
These would be added to the URL as `?apiKey=your_api_key_here&limit=10`.
### Headers
Configure HTTP headers for your request. Common headers include:
```
Key: Content-Type
Value: application/json
Key: Authorization
Value: Bearer your_token_here
```
### Request Body
For methods that support a request body (POST, PUT, PATCH), you can define the data to send. The body can be:
- JSON data entered directly in the block
- Data connected from another block's output
- Dynamically generated during workflow execution
### Accessing Results
After an API request completes, you can access its outputs:
- **`<api.data>`**: The response body data from the API
- **`<api.status>`**: HTTP status code (200, 404, 500, etc.)
- **`<api.headers>`**: Response headers from the server
## Advanced Features
### Dynamic URL Construction
Build URLs dynamically using variables from previous blocks:
```javascript
// In a Function block before the API
const userId = <start.userId>;
const apiUrl = `https://api.example.com/users/${userId}/profile`;
```
### Request Retries
The API block supports **configurable retries** (see the block’s **Advanced** settings):
- **Retries**: Number of retry attempts (additional tries after the first request)
- **Retry delay (ms)**: Initial delay before retrying (uses exponential backoff)
- **Max retry delay (ms)**: Maximum delay between retries
- **Retry non-idempotent methods**: Allow retries for **POST/PATCH** (may create duplicate requests)
Retries are attempted for:
- Network/connection failures and timeouts (with exponential backoff)
- Rate limits (**429**) and server errors (**5xx**)
### Response Validation
Validate API responses before processing:
```javascript
// In a Function block after the API
if (<api.status> === 200) {
const data = <api.data>;
// Process successful response
} else {
// Handle error response
console.error(`API Error: ${<api.status>}`);
}
```
## Outputs
- **`<api.data>`**: Response body data from the API
- **`<api.status>`**: HTTP status code
- **`<api.headers>`**: Response headers
## Example Use Cases
**Fetch User Profile Data** - Retrieve user information from external service
```
Function (Build ID) → API (GET /users/{id}) → Function (Format) → Response
```
**Payment Processing** - Process payment through Stripe API
```
Function (Validate) → API (Stripe) → Condition (Success) → Supabase (Update)
```
## Best Practices
- **Use environment variables for sensitive data**: Don't hardcode API keys or credentials
- **Handle errors gracefully**: Connect error handling logic for failed requests
- **Validate responses**: Check status codes and response formats before processing data
- **Respect rate limits**: Be mindful of API rate limits and implement appropriate throttling
<FAQ items={[
{ question: "What is the default request timeout?", answer: "The default timeout is 300,000 milliseconds (5 minutes). You can configure it up to a maximum of 600,000 milliseconds (10 minutes) in the block's Advanced settings." },
{ question: "Which HTTP errors trigger automatic retries?", answer: "Retries are attempted for network/connection failures, timeouts, rate limit responses (HTTP 429), and server errors (5xx). Client errors like 400 or 404 are not retried." },
{ question: "How does retry backoff work?", answer: "Retries use exponential backoff starting from the configured retry delay (default 500ms). Each subsequent retry doubles the delay, up to the maximum retry delay (default 30,000ms)." },
{ question: "Are POST and PATCH requests retried by default?", answer: "No. POST and PATCH are non-idempotent methods, so retries are disabled for them by default to avoid creating duplicate resources. You can enable retries for these methods with the 'Retry non-idempotent methods' toggle in Advanced settings, but be aware this may cause duplicate requests." },
{ question: "What headers are included automatically?", answer: "Standard headers such as User-Agent, Accept, and Cache-Control are added automatically. Any custom headers you configure will be merged with these defaults, and your custom values will override automatic headers with the same name." },
{ question: "Can I send form data or file uploads?", answer: "The API block primarily sends JSON request bodies through the UI. The underlying HTTP tool also supports form data natively — if you pass form data parameters, it will construct a proper multipart/form-data request automatically. For most use cases, the JSON body field in the block UI is sufficient." },
]} />
@@ -1,144 +0,0 @@
---
title: Condition
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Condition block branches workflow execution based on boolean expressions. Evaluate conditions using previous block outputs and route to different paths without requiring an LLM.
<div className="flex justify-center">
<Image
src="/static/blocks/condition.png"
alt="Condition Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration Options
### Conditions
Define one or more conditions that will be evaluated. Each condition includes:
- **Expression**: A JavaScript/TypeScript expression that evaluates to true or false
- **Path**: The destination block to route to if the condition is true
- **Description**: Optional explanation of what the condition checks
You can create multiple conditions that are evaluated in order, with the first matching condition determining the execution path.
### Condition Expression Format
Conditions use JavaScript syntax and can reference input values from previous blocks.
<Tabs items={['Score Threshold', 'Text Analysis', 'Multiple Conditions']}>
<Tab>
```javascript
// Check if a score is above a threshold
<agent.score> > 75
```
</Tab>
<Tab>
```javascript
// Check if a text contains specific keywords
<agent.text>.includes('urgent') || <agent.text>.includes('emergency')
```
</Tab>
<Tab>
```javascript
// Check multiple conditions
<agent.age> >= 18 && <agent.country> === 'US'
```
</Tab>
</Tabs>
### Accessing Results
After a condition evaluates, you can access its outputs:
- **`<condition.conditionResult>`**: Boolean result of the condition evaluation
- **`<condition.selectedOption>`**: ID of the condition that was matched
- **`<condition.selectedPath>`**: Details of the chosen routing destination
## Advanced Features
### Complex Expressions
Use JavaScript operators and functions in conditions:
```javascript
// String operations
<user.email>.endsWith('@company.com')
// Array operations
<api.tags>.includes('urgent')
// Mathematical operations
<agent.confidence> * 100 > 85
// Date comparisons
new Date(<api.created_at>) > new Date('2024-01-01')
```
### Multiple Condition Evaluation
Conditions are evaluated in order until one matches:
```javascript
// Condition 1: Check for high priority
<ticket.priority> === 'high'
// Condition 2: Check for urgent keywords
<ticket.subject>.toLowerCase().includes('urgent')
// Condition 3: Default fallback
true
```
### Error Handling
If a condition expression references an undefined variable or throws a runtime error, the block will throw an error and the execution will fail (or follow the error path if one is connected). Use optional chaining (`?.`) or explicit null checks in your expressions to handle missing values safely.
## Outputs
- **`<condition.conditionResult>`**: Boolean result of the evaluation
- **`<condition.selectedOption>`**: ID of the matched condition
- **`<condition.selectedPath>`**: Details of the chosen routing destination
## Example Use Cases
**Customer Support Routing** - Route tickets based on priority
```
API (Ticket) → Condition (priority === 'high') → Agent (Escalation) or Agent (Standard)
```
**Content Moderation** - Filter content based on analysis
```
Agent (Analyze) → Condition (toxicity > 0.7) → Moderation or Publish
```
**User Onboarding Flow** - Personalize onboarding based on user type
```
Function (Process) → Condition (account_type === 'enterprise') → Advanced or Simple
```
## Best Practices
- **Order conditions correctly**: Place more specific conditions before general ones to ensure specific logic takes precedence over fallbacks
- **Use the else branch when needed**: If no conditions match and the else branch is not connected, the workflow branch will end gracefully. Connect the else branch if you need a fallback path for unmatched cases
- **Keep expressions simple**: Use clear, straightforward boolean expressions for better readability and easier debugging
- **Document your conditions**: Add descriptions to explain the purpose of each condition for better team collaboration and maintenance
- **Test edge cases**: Verify conditions handle boundary values correctly by testing with values at the edges of your condition ranges
<FAQ items={[
{ question: "Does the Condition block use an LLM?", answer: "No. The Condition block evaluates boolean expressions using JavaScript syntax directly — it does not call any AI model. This makes it fast, deterministic, and free of API costs. If you need AI-powered routing decisions, use the Router block instead." },
{ question: "What happens if no condition matches?", answer: "If none of your defined conditions evaluate to true, the workflow follows the else branch. If the else branch is not connected to any downstream block, that workflow path ends gracefully without an error. Add a fallback condition of simply true as the last condition to guarantee a match." },
{ question: "In what order are conditions evaluated?", answer: "Conditions are evaluated from top to bottom in the order they are defined. The first condition that evaluates to true determines the execution path. Subsequent conditions are not evaluated after a match is found, so place more specific conditions before general ones." },
{ question: "What JavaScript features can I use in condition expressions?", answer: "You can use standard JavaScript operators and methods including comparison operators (===, !==, >, <), logical operators (&&, ||, !), string methods (.includes(), .endsWith(), .toLowerCase()), array methods (.includes(), .length), mathematical operations, and Date comparisons. Reference block outputs using <blockName.output> syntax." },
{ question: "How does the Condition block handle null or undefined values?", answer: "If a condition expression references an undefined variable or throws a runtime error, the Condition block will throw an error and the execution will fail (or follow the error path if one is connected). Use optional chaining (?.) or explicit null checks in your expressions to handle missing values safely." },
]} />
@@ -1,103 +0,0 @@
---
title: Evaluator
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Evaluator block uses AI to score and assess content quality against custom metrics. Perfect for quality control, A/B testing, and ensuring AI outputs meet specific standards.
<div className="flex justify-center">
<Image
src="/static/blocks/evaluator.png"
alt="Evaluator Block Configuration"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration Options
### Evaluation Metrics
Define custom metrics to evaluate content against. Each metric includes:
- **Name**: A short identifier for the metric
- **Description**: A detailed explanation of what the metric measures
- **Range**: The numeric range for scoring (e.g., 1-5, 0-10)
Example metrics:
```
Accuracy (1-5): How factually accurate is the content?
Clarity (1-5): How clear and understandable is the content?
Relevance (1-5): How relevant is the content to the original query?
```
### Content
The content to be evaluated. This can be:
- Directly provided in the block configuration
- Connected from another block's output (typically an Agent block)
- Dynamically generated during workflow execution
### Model Selection
Choose an AI model to perform the evaluation:
- **OpenAI**: GPT-4o, o1, o3, o4-mini, gpt-4.1
- **Anthropic**: Claude Sonnet 4.5
- **Google**: Gemini 2.5 Pro, Gemini 2.0 Flash
- **Other Providers**: Groq, Cerebras, xAI, DeepSeek
- **Local Models**: Ollama or VLLM compatible models
Use models with strong reasoning capabilities like GPT-4o or Claude Sonnet 4.5 for best results.
### API Key
Your API key for the selected LLM provider. This is securely stored and used for authentication.
## Example Use Cases
**Content Quality Assessment** - Evaluate content before publication
```
Agent (Generate) → Evaluator (Score) → Condition (Check threshold) → Publish or Revise
```
**A/B Testing Content** - Compare multiple AI-generated responses
```
Parallel (Variations) → Evaluator (Score Each) → Function (Select Best) → Response
```
**Customer Support Quality Control** - Ensure responses meet quality standards
```
Agent (Support Response) → Evaluator (Score) → Function (Log) → Condition (Review if Low)
```
## Outputs
- **`<evaluator.content>`**: Summary of the evaluation with scores
- **`<evaluator.model>`**: Model used for evaluation
- **`<evaluator.tokens>`**: Token usage statistics
- **`<evaluator.cost>`**: Estimated evaluation cost
## Best Practices
- **Use specific metric descriptions**: Clearly define what each metric measures to get more accurate evaluations
- **Choose appropriate ranges**: Select scoring ranges that provide enough granularity without being overly complex
- **Connect with Agent blocks**: Use Evaluator blocks to assess Agent block outputs and create feedback loops
- **Use consistent metrics**: For comparative analysis, maintain consistent metrics across similar evaluations
- **Combine multiple metrics**: Use several metrics to get a comprehensive evaluation
<FAQ items={[
{ question: "What format does the Evaluator return scores in?", answer: "The Evaluator returns a JSON object where each key is the lowercase version of your metric name and the value is a numeric score within the range you defined. For example, a metric named 'Accuracy' with range 1-5 would appear as { \"accuracy\": 4 } in the output." },
{ question: "Which models work best for evaluation?", answer: "Models with strong reasoning capabilities produce the most consistent evaluations. GPT-4o and Claude Sonnet are recommended. The default model is Claude Sonnet 4.5." },
{ question: "Can I evaluate non-text content?", answer: "The content field accepts any string input. If you pass JSON or structured data, the Evaluator will automatically detect and format it before evaluation. However, the evaluation is text-based — it cannot directly evaluate images or audio." },
{ question: "What happens if a metric name is invalid or incomplete?", answer: "Metrics missing a name or range are automatically filtered out. The Evaluator only scores metrics that have both a valid name and a defined min/max range." },
{ question: "Does the Evaluator use structured output?", answer: "Yes. The Evaluator generates a JSON Schema response format based on your metrics and enforces strict mode, so the LLM is constrained to return only the expected metric scores as numbers — no extra text or explanations." },
{ question: "How are evaluation costs calculated?", answer: "Costs are based on the token usage of the underlying LLM call. The Evaluator outputs include token counts (prompt, completion, total) and cost breakdown (input, output, total) so you can track spending per evaluation." },
]} />
@@ -1,248 +0,0 @@
---
title: Function
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Function block executes custom JavaScript, TypeScript, or Python code in your workflows. Transform data, perform calculations, or implement custom logic.
<div className="flex justify-center">
<Image
src="/static/blocks/function.png"
alt="Function Block with Code Editor"
width={500}
height={400}
className="my-6"
/>
</div>
## Outputs
- **`<function.result>`**: The value returned from your function
- **`<function.stdout>`**: Console.log() output from your code
## Example Use Cases
**Data Processing Pipeline** - Transform API response into structured data
```
API (Fetch) → Function (Process & Validate) → Function (Calculate Metrics) → Response
```
**Business Logic Implementation** - Calculate loyalty scores and tiers
```
Agent (Get History) → Function (Calculate Score) → Function (Determine Tier) → Condition (Route)
```
**Data Validation and Sanitization** - Validate and clean user input
```
Input → Function (Validate & Sanitize) → API (Save to Database)
```
### Example: Loyalty Score Calculator
<Tabs items={['JavaScript', 'Python']}>
<Tab value="JavaScript">
```javascript title="loyalty-calculator.js"
// Process customer data and calculate loyalty score
const { purchaseHistory, accountAge, supportTickets } = <agent>;
// Calculate metrics
const totalSpent = purchaseHistory.reduce((sum, purchase) => sum + purchase.amount, 0);
const purchaseFrequency = purchaseHistory.length / (accountAge / 365);
const ticketRatio = supportTickets.resolved / supportTickets.total;
// Calculate loyalty score (0-100)
const spendScore = Math.min(totalSpent / 1000 * 30, 30);
const frequencyScore = Math.min(purchaseFrequency * 20, 40);
const supportScore = ticketRatio * 30;
const loyaltyScore = Math.round(spendScore + frequencyScore + supportScore);
return {
customer: <agent.name>,
loyaltyScore,
loyaltyTier: loyaltyScore >= 80 ? "Platinum" : loyaltyScore >= 60 ? "Gold" : "Silver",
metrics: { spendScore, frequencyScore, supportScore }
};
```
</Tab>
<Tab value="Python">
```python title="loyalty-calculator.py"
import json
# Reference outputs from other blocks using angle bracket syntax
data = json.loads('<agent>')
purchase_history = data["purchaseHistory"]
account_age = data["accountAge"]
support_tickets = data["supportTickets"]
# Calculate metrics
total_spent = sum(p["amount"] for p in purchase_history)
purchase_frequency = len(purchase_history) / (account_age / 365)
ticket_ratio = support_tickets["resolved"] / support_tickets["total"]
# Calculate loyalty score (0-100)
spend_score = min(total_spent / 1000 * 30, 30)
frequency_score = min(purchase_frequency * 20, 40)
support_score = ticket_ratio * 30
loyalty_score = round(spend_score + frequency_score + support_score)
tier = "Platinum" if loyalty_score >= 80 else "Gold" if loyalty_score >= 60 else "Silver"
result = {
"customer": data["name"],
"loyaltyScore": loyalty_score,
"loyaltyTier": tier,
"metrics": {
"spendScore": spend_score,
"frequencyScore": frequency_score,
"supportScore": support_score
}
}
print(json.dumps(result))
```
</Tab>
</Tabs>
## Python Support
The Function block supports Python as an alternative to JavaScript. Python code runs in a secure [E2B](https://e2b.dev) cloud sandbox.
<div className="flex justify-center">
<Image
src="/static/blocks/function-python.png"
alt="Function block with Python selected"
width={400}
height={500}
className="my-6"
/>
</div>
### Enabling Python
Select **Python** from the language dropdown in the Function block. Python execution requires E2B to be enabled on your Sim instance.
<Callout type="warn">
If you don't see Python as an option in the language dropdown, E2B is not enabled. This only applies to self-hosted instances — E2B is enabled by default on sim.ai.
</Callout>
<Callout type="info">
Python code always runs in the E2B sandbox, even for simple scripts without imports. This ensures a secure, isolated execution environment.
</Callout>
### Returning Results
In Python, print your result as JSON to stdout. The Function block captures stdout and makes it available via `<function.result>`:
```python title="example.py"
import json
data = {"status": "processed", "count": 42}
print(json.dumps(data))
```
### Available Libraries
The E2B sandbox includes the Python standard library (`json`, `re`, `datetime`, `math`, `os`, `collections`, etc.) and common packages like `matplotlib` for visualization. Charts generated with matplotlib are captured as images automatically.
<Callout type="info">
The exact set of pre-installed packages depends on the E2B sandbox configuration. If a package you need isn't available, consider calling an external API from your code instead.
</Callout>
### Matplotlib Charts
When your Python code generates matplotlib figures, they are automatically captured and returned as base64-encoded PNG images in the output:
```python title="chart.py"
import matplotlib.pyplot as plt
import json
data = json.loads('<api.data>')
plt.figure(figsize=(10, 6))
plt.bar(data["labels"], data["values"])
plt.title("Monthly Revenue")
plt.xlabel("Month")
plt.ylabel("Revenue ($)")
plt.savefig("chart.png")
plt.show()
```
{/* TODO: Screenshot of Python code execution output in the logs panel */}
### JavaScript vs. Python
| | JavaScript | Python |
|--|-----------|--------|
| **Execution** | Local VM (fast) or E2B sandbox (with imports) | Always E2B sandbox |
| **Returning results** | `return { ... }` | `print(json.dumps({ ... }))` |
| **HTTP requests** | `fetch()` built-in | `requests` or `httpx` |
| **Best for** | Quick transforms, JSON manipulation | Data science, charting, complex math |
## Best Practices
### Large Inputs and Payload Limits
Function blocks receive their code, parameters, resolved references, and previous block context in an internal execution request. Sim can safely reference oversized workflow outputs, such as large `loop.results` or `parallel.results`, when you select a smaller nested field like `<loop.results[0][0].id>`. Larger values are stored in execution storage and passed around as small references until code explicitly reads them.
File outputs are metadata-first by default. Referencing `<file.name>`, `<file.url>`, or similar metadata does not hydrate file contents. In JavaScript functions without imports, a direct base64 reference like `<readfile.file.base64>` is automatically rewritten to a lazy server-side read so the base64 string does not cross the Function request body.
You can also call the helper explicitly:
```javascript
const file = <readfile.file>;
const base64 = await sim.files.readBase64(file);
```
`sim.files.readBase64(file)`, `sim.files.readText(file)`, `sim.files.readBase64Chunk(file, { offset, length })`, and `sim.files.readTextChunk(file, { offset, length })` read from server-side execution storage under memory caps. `sim.values.read(ref)` explicitly reads a large execution value reference, and `sim.values.readArray(ref)` reads a manifest-backed large array. These helpers are available only in JavaScript functions without imports. JavaScript with imports, Python, and shell do not support these lazy helpers yet.
Very large full reads can still fail by design; use chunk helpers or return a file when you need to handle more data.
Use text chunks for text-like files such as logs, CSV, JSONL, and markdown:
```javascript
const file = <readfile.file>;
const firstMegabyte = await sim.files.readTextChunk(file, {
offset: 0,
length: 1024 * 1024,
});
return firstMegabyte.split('\n').slice(0, 10);
```
Use base64 chunks for binary files such as images, PDFs, audio, archives, or APIs that expect base64 input:
```javascript
const file = <readfile.file>;
const firstMegabyteBase64 = await sim.files.readBase64Chunk(file, {
offset: 0,
length: 1024 * 1024,
});
return { name: file.name, chunk: firstMegabyteBase64 };
```
Chunk `offset` and `length` are byte-based. For Unicode text, a chunk can split a multi-byte character at the boundary; use text chunks for approximate text processing and prefer smaller structured references when exact parsing matters.
Avoid passing a full large object into a Function block when you only need one field. For example, prefer `<api.data.customerId>` over `<api.data>` when the API response is large. If a JavaScript Function without imports references a whole large execution value, Sim automatically rewrites it to `sim.values.read(...)` at runtime under memory caps. If the value is a manifest-backed array, Sim rewrites it to `sim.values.readArray(...)` so array variables can stay compact between blocks.
For large generated data, write the result to a file or table with `outputPath`, `outputSandboxPath`, or `outputTable` instead of returning the entire payload inline.
- **Keep functions focused**: Write functions that do one thing well to improve maintainability and debugging
- **Handle errors gracefully**: Use try/catch blocks to handle potential errors and provide meaningful error messages
- **Test edge cases**: Ensure your code handles unusual inputs, null values, and boundary conditions correctly
- **Optimize for performance**: Be mindful of computational complexity and memory usage for large datasets
- **Use console.log() for debugging**: Leverage stdout output to debug and monitor function execution
<FAQ items={[
{ question: "What languages does the Function block support?", answer: "The Function block supports JavaScript and Python. JavaScript is the default. Python support requires the E2B feature to be enabled, as Python code always runs in a secure E2B sandbox environment." },
{ question: "When does code run locally vs. in a sandbox?", answer: "JavaScript code without external imports runs in a local isolated VM for fast execution. JavaScript code that uses import or require statements requires E2B and runs in a secure sandbox. Python code always runs in the E2B sandbox regardless of whether it has imports." },
{ question: "How do I reference outputs from other blocks inside my code?", answer: "Use the angle bracket syntax directly in your code, like <agent.content> or <api.data>. Do not wrap these references in quotes — the system replaces them with actual values before execution. For environment variables, use double curly braces: {{API_KEY}}." },
{ question: "What does the Function block return?", answer: "The Function block has two outputs: result (the return value of your code, accessed via <function.result>) and stdout (anything logged with console.log(), accessed via <function.stdout>). Make sure your code includes a return statement if you need to pass data to downstream blocks." },
{ question: "Can I make HTTP requests from a Function block?", answer: "Yes. The fetch() API is available in the JavaScript execution environment. You can use async/await with fetch to call external APIs. However, you cannot use libraries like axios or request — only the built-in fetch is supported. Your code runs inside an async context automatically, so you can use await directly." },
{ question: "Is there a timeout for Function block execution?", answer: "Yes. Function blocks have a configurable execution timeout. If your code exceeds the timeout, the execution is terminated and the block reports an error. Keep this in mind when making external API calls or processing large datasets." },
]} />
@@ -1,216 +0,0 @@
---
title: Guardrails
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
import { Video } from '@/components/ui/video'
import { FAQ } from '@/components/ui/faq'
The Guardrails block validates and protects your AI workflows by checking content against multiple validation types. Ensure data quality, prevent hallucinations, detect PII, and enforce format requirements before content moves through your workflow.
<div className="flex justify-center">
<Image
src="/static/blocks/guardrails.png"
alt="Guardrails Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Validation Types
### JSON Validation
Validates that content is properly formatted JSON. Perfect for ensuring structured LLM outputs can be safely parsed.
**Use Cases:**
- Validate JSON responses from Agent blocks before parsing
- Ensure API payloads are properly formatted
- Check structured data integrity
**Output:**
- `passed`: `true` if valid JSON, `false` otherwise
- `error`: Error message if validation fails (e.g., "Invalid JSON: Unexpected token...")
### Regex Validation
Checks if content matches a specified regular expression pattern.
**Use Cases:**
- Validate email addresses
- Check phone number formats
- Verify URLs or custom identifiers
- Enforce specific text patterns
**Configuration:**
- **Regex Pattern**: The regular expression to match against (e.g., `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` for emails)
**Output:**
- `passed`: `true` if content matches pattern, `false` otherwise
- `error`: Error message if validation fails
### Hallucination Detection
Uses Retrieval-Augmented Generation (RAG) with LLM scoring to detect when AI-generated content contradicts or isn't grounded in your knowledge base.
**How It Works:**
1. Queries your knowledge base for relevant context
2. Sends both the AI output and retrieved context to an LLM
3. LLM assigns a confidence score (0-10 scale)
- **0** = Full hallucination (completely ungrounded)
- **10** = Fully grounded (completely supported by knowledge base)
4. Validation passes if score ≥ threshold (default: 3)
**Configuration:**
- **Knowledge Base**: Select from your existing knowledge bases
- **Model**: Choose LLM for scoring (requires strong reasoning - GPT-4o, Claude 3.7 Sonnet recommended)
- **API Key**: Authentication for selected LLM provider (auto-hidden for hosted/Ollama or VLLM compatible models)
- **Confidence**: Minimum score to pass (0-10, default: 3)
- **Top K** (Advanced): Number of knowledge base chunks to retrieve (default: 5)
**Output:**
- `passed`: `true` if confidence score ≥ threshold
- `score`: Confidence score (0-10)
- `reasoning`: LLM's explanation for the score
- `error`: Error message if validation fails
**Use Cases:**
- Validate Agent responses against documentation
- Ensure customer support answers are factually accurate
- Verify generated content matches source material
- Quality control for RAG applications
### PII Detection
Detects personally identifiable information using Microsoft Presidio. Supports over 30 entity types across multiple countries and languages.
<div className="flex justify-center">
<Image
src="/static/blocks/guardrails-2.png"
alt="PII Detection Configuration"
width={700}
height={450}
className="my-6"
/>
</div>
**How It Works:**
1. Pass content to validate (e.g., `<agent1.content>`)
2. Select PII types to detect using the modal selector
3. Choose the action (Block Request or Mask PII)
4. Content is scanned for matching PII entities
5. Returns detection results and optionally masked text
<div className="mx-auto w-3/5 overflow-hidden rounded-lg">
<Video src="guardrails.mp4" width={500} height={350} />
</div>
**Configuration:**
- **PII Types to Detect**: Select from grouped categories via modal selector
- **Common**: Person name, Email, Phone, Credit card, IP address, etc.
- **USA**: SSN, Driver's license, Passport, Bank account number, ITIN
- **UK**: NHS number, National insurance number
- **Spain**: NIF, NIE
- **Italy**: Fiscal code, Driver's license, Identity card, Passport
- **Poland**: PESEL
- **Singapore**: NRIC/FIN
- **Australia**: ABN, ACN, TFN, Medicare
- **India**: Aadhaar, PAN, Vehicle registration, Voter number, Passport
- **Action**:
- **Block Request**: Only identify PII (default)
- **Mask PII**: Replace detected PII with masked values
- **Language**: Detection language (default: English)
**Output:**
- `passed`: `false` if any selected PII types are detected
- `detectedEntities`: Array of detected PII with type, location, and confidence
- `maskedText`: Content with PII masked (only if mode = "Mask")
- `error`: Error message if validation fails
**Use Cases:**
- Block content containing sensitive personal information
- Mask PII before logging or storing data
- Compliance with GDPR and other privacy regulations
- Sanitize user inputs before processing
## Configuration
### Content to Validate
The input content to validate. This typically comes from:
- Agent block outputs: `<agent.content>`
- Function block results: `<function.result>`
- API responses: `<api.output>`
- Any other block output
### Validation Type
Choose from four validation types:
- **Valid JSON**: Check if content is properly formatted JSON
- **Regex Match**: Verify content matches a regex pattern
- **Hallucination Check**: Validate against knowledge base with LLM scoring
- **PII Detection**: Detect and optionally mask personally identifiable information
## Outputs
All validation types return:
- **`<guardrails.passed>`**: Boolean indicating if validation passed
- **`<guardrails.validationType>`**: The type of validation performed
- **`<guardrails.input>`**: The original input that was validated
- **`<guardrails.error>`**: Error message if validation failed (optional)
Additional outputs by type:
**Hallucination Check:**
- **`<guardrails.score>`**: Confidence score (0-10)
- **`<guardrails.reasoning>`**: LLM's explanation
**PII Detection:**
- **`<guardrails.detectedEntities>`**: Array of detected PII entities
- **`<guardrails.maskedText>`**: Content with PII masked (if mode = "Mask")
## Example Use Cases
**Validate JSON Before Parsing** - Ensure Agent output is valid JSON
```
Agent (Generate) → Guardrails (Validate) → Condition (Check passed) → Function (Parse)
```
**Prevent Hallucinations** - Validate customer support responses against knowledge base
```
Agent (Response) → Guardrails (Check KB) → Condition (Score ≥ 3) → Send or Flag
```
**Block PII in User Inputs** - Sanitize user-submitted content
```
Input → Guardrails (Detect PII) → Condition (No PII) → Process or Reject
```
## Best Practices
- **Chain with Condition blocks**: Use `<guardrails.passed>` to branch workflow logic based on validation results
- **Use JSON validation before parsing**: Always validate JSON structure before attempting to parse LLM outputs
- **Choose appropriate PII types**: Only select the PII entity types relevant to your use case for better performance
- **Set reasonable confidence thresholds**: For hallucination detection, adjust threshold based on your accuracy requirements (higher = stricter)
- **Use strong models for hallucination detection**: GPT-4o or Claude 3.7 Sonnet provide more accurate confidence scoring
- **Mask PII for logging**: Use "Mask" mode when you need to log or store content that may contain PII
- **Test regex patterns**: Validate your regex patterns thoroughly before deploying to production
- **Monitor validation failures**: Track `<guardrails.error>` messages to identify common validation issues
<Callout type="info">
Guardrails validation happens synchronously in your workflow. For hallucination detection, choose faster models (like GPT-4o-mini) if latency is critical.
</Callout>
<FAQ items={[
{ question: "Can I run multiple validation types on the same content?", answer: "Each Guardrails block performs one validation type at a time. To apply multiple validations, chain several Guardrails blocks in sequence — for example, first validate JSON format, then check for PII." },
{ question: "What does the hallucination confidence score mean?", answer: "The score ranges from 0 to 10. A score of 0 means the content is completely ungrounded (full hallucination), and 10 means it is fully supported by the knowledge base. Validation passes when the score meets or exceeds your configured threshold (default: 3)." },
{ question: "How many knowledge base chunks are retrieved for hallucination detection?", answer: "By default, 5 chunks are retrieved. You can adjust this up to 20 in the Advanced settings using the 'Number of Chunks to Retrieve' slider. More chunks provide broader context but increase latency and token usage." },
{ question: "What PII detection engine is used?", answer: "PII detection is powered by Microsoft Presidio. It supports over 30 entity types across multiple countries including the US, UK, Spain, Italy, Poland, Singapore, Australia, and India." },
{ question: "What is the difference between Block and Mask modes for PII?", answer: "Block mode fails the validation (passed = false) if any selected PII types are detected. Mask mode also detects PII but replaces it with masked values in the output, making the content safe to use downstream. Both modes return the list of detected entities." },
{ question: "Which languages does PII detection support?", answer: "PII detection supports English, Spanish, Italian, Polish, and Finnish. The language setting affects the NLP models used for entity recognition, so selecting the correct language improves detection accuracy." },
{ question: "Does JSON validation check schema structure or just syntax?", answer: "JSON validation only checks that the content is syntactically valid JSON (i.e., it can be parsed without errors). It does not validate against a specific schema. For schema validation, use a Function block after the Guardrails check." },
]} />
-150
View File
@@ -1,150 +0,0 @@
---
title: Overview
description: The building components of your AI workflows
---
import { Card, Cards } from 'fumadocs-ui/components/card'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Video } from '@/components/ui/video'
import { FAQ } from '@/components/ui/faq'
Blocks are the building components you connect together to create AI workflows. Think of them as specialized modules that each handle a specific task—from chatting with AI models to making API calls or processing data.
<div className="w-full max-w-2xl mx-auto overflow-hidden rounded-lg">
<Video src="connections.mp4" width={700} height={450} />
</div>
## Core Block Types
Sim provides essential block types that handle the core functions of AI workflows:
### Processing Blocks
- **[Agent](/blocks/agent)** - Chat with AI models (OpenAI, Anthropic, Google, local models)
- **[Function](/blocks/function)** - Run custom JavaScript/TypeScript code
- **[API](/blocks/api)** - Connect to external services via HTTP requests
### Logic Blocks
- **[Condition](/blocks/condition)** - Branch workflow paths based on boolean expressions
- **[Router](/blocks/router)** - Use AI to intelligently route requests to different paths
- **[Evaluator](/blocks/evaluator)** - Score and assess content quality using AI
### Control Flow Blocks
- **[Variables](/blocks/variables)** - Set and manage workflow-scoped variables
- **[Wait](/blocks/wait)** - Pause workflow execution for a specified time delay
- **[Human in the Loop](/blocks/human-in-the-loop)** - Pause for human approval and feedback before continuing
### Output Blocks
- **[Response](/blocks/response)** - Format and return final results from your workflow
## How Blocks Work
Each block has three main components:
**Inputs**: Data coming into the block from other blocks or user input
**Configuration**: Settings that control how the block behaves
**Outputs**: Data the block produces for other blocks to use
<Steps>
<Step>
<strong>Receive Input</strong>: Block receives data from connected blocks or user input
</Step>
<Step>
<strong>Process</strong>: Block processes the input according to its configuration
</Step>
<Step>
<strong>Output Results</strong>: Block produces output data for the next blocks in the workflow
</Step>
</Steps>
## Connecting Blocks
You create workflows by connecting blocks together. The output of one block becomes the input of another:
- **Drag to connect**: Drag from an output port to an input port
- **Multiple connections**: One output can connect to multiple inputs
- **Branching paths**: Some blocks can route to different paths based on conditions
<div className="w-full max-w-2xl mx-auto overflow-hidden rounded-lg">
<Video src="connections.mp4" width={700} height={450} />
</div>
## Common Patterns
### Sequential Processing
Connect blocks in a chain where each block processes the output of the previous one:
```
User Input → Agent → Function → Response
```
### Conditional Branching
Use Condition or Router blocks to create different paths:
```
User Input → Router → Agent A (for questions)
→ Agent B (for commands)
```
### Quality Control
Use Evaluator blocks to assess and filter outputs:
```
Agent → Evaluator → Condition → Response (if good)
→ Agent (retry if bad)
```
## Block Configuration
Each block type has specific configuration options:
**All Blocks**:
- Input/output connections
- Error handling behavior
- Execution timeout settings
**AI Blocks** (Agent, Router, Evaluator):
- Model selection (OpenAI, Anthropic, Google, local)
- API keys and authentication
- Temperature and other model parameters
- System prompts and instructions
**Logic Blocks** (Condition, Function):
- Custom expressions or code
- Variable references
- Execution environment settings
**Integration Blocks** (API, Response):
- Endpoint configuration
- Headers and authentication
- Request/response formatting
<Cards>
<Card title="Agent Block" href="/blocks/agent">
Connect to AI models and create intelligent responses
</Card>
<Card title="Function Block" href="/blocks/function">
Run custom code to process and transform data
</Card>
<Card title="API Block" href="/blocks/api">
Integrate with external services and APIs
</Card>
<Card title="Condition Block" href="/blocks/condition">
Create branching logic based on data evaluation
</Card>
<Card title="Human in the Loop Block" href="/blocks/human-in-the-loop">
Pause for human approval and feedback before continuing
</Card>
<Card title="Variables Block" href="/blocks/variables">
Set and manage workflow-scoped variables
</Card>
<Card title="Wait Block" href="/blocks/wait">
Pause workflow execution for specified time delays
</Card>
</Cards>
<FAQ items={[
{ question: "How many block types are available in Sim?", answer: "Sim has over 200 blocks in its registry, spanning core workflow blocks (Agent, Function, Condition, Router, etc.), integration blocks for third-party services (Gmail, Slack, GitHub, Notion, and many more), and trigger blocks that start workflows from external events like webhooks or schedules. Loop and parallel execution are built into the execution engine as container constructs on the canvas, rather than being standalone registry blocks." },
{ question: "Can one block's output connect to multiple downstream blocks?", answer: "Yes. A single output port can connect to multiple input ports on different blocks. This lets you fan out data from one processing step to several parallel paths without needing to duplicate the block." },
{ question: "What happens if a block in the middle of a workflow fails?", answer: "When a block encounters an error, the workflow stops executing along that path. Blocks that support error handling (like the Router) can route to an error path so you can handle failures gracefully instead of halting the entire workflow." },
{ question: "What is the difference between Processing blocks and Logic blocks?", answer: "Processing blocks (Agent, Function, API) transform or generate data — they do the actual work. Logic blocks (Condition, Router, Evaluator) make decisions about which path the workflow should take based on the data, without modifying it themselves." },
{ question: "Can I use blocks from different categories together in one workflow?", answer: "Absolutely. A typical workflow combines blocks from multiple categories. For example, you might use a trigger block to start the workflow, an Agent block to process input, a Condition block to branch logic, and an integration block like Gmail to send results." },
{ question: "Are there container blocks that can hold other blocks inside them?", answer: "Yes. Loop and Parallel are execution engine constructs that appear as container regions on the canvas. You drag other blocks inside them. Loop containers execute their contained blocks repeatedly, while Parallel containers execute their contained blocks concurrently across multiple branches. Unlike registry blocks, these are handled directly by the execution engine." },
]} />
-258
View File
@@ -1,258 +0,0 @@
---
title: Loop
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Loop block is a container that executes blocks repeatedly. Iterate over collections, repeat operations a fixed number of times, or continue while a condition is met.
<Callout type="info">
Loop blocks are container nodes that hold other blocks inside them. The contained blocks execute multiple times based on your configuration.
</Callout>
## Configuration Options
### Loop Type
Choose between four types of loops:
<Tabs items={['For Loop', 'ForEach Loop', 'While Loop', 'Do-While Loop']}>
<Tab>
**For Loop (Iterations)** - A numeric loop that executes a fixed number of times:
<div className="flex justify-center">
<Image
src="/static/blocks/loop-1.png"
alt="For Loop with iterations"
width={500}
height={400}
className="my-6"
/>
</div>
Use this when you need to repeat an operation a specific number of times.
```
Example: Run 5 times
- Iteration 1
- Iteration 2
- Iteration 3
- Iteration 4
- Iteration 5
```
</Tab>
<Tab>
**ForEach Loop (Collection)** - A collection-based loop that iterates over each item in an array or object:
<div className="flex justify-center">
<Image
src="/static/blocks/loop-2.png"
alt="ForEach Loop with collection"
width={500}
height={400}
className="my-6"
/>
</div>
Use this when you need to process a collection of items.
```
Example: Process ["apple", "banana", "orange"]
- Iteration 1: Process "apple"
- Iteration 2: Process "banana"
- Iteration 3: Process "orange"
```
</Tab>
<Tab>
**While Loop (Condition-based)** - Continues executing while a condition evaluates to true:
<div className="flex justify-center">
<Image
src="/static/blocks/loop-3.png"
alt="While Loop with condition"
width={500}
height={400}
className="my-6"
/>
</div>
Use this when you need to loop until a specific condition is met. The condition is checked **before** each iteration.
```
Example: While {"<variable.i>"} < 10
- Check condition → Execute if true
- Inside loop: Increment {"<variable.i>"}
- Inside loop: Variables assigns i = {"<variable.i>"} + 1
- Check condition → Execute if true
- Check condition → Exit if false
```
</Tab>
<Tab>
**Do-While Loop (Condition-based)** - Executes at least once, then continues while a condition is true:
<div className="flex justify-center">
<Image
src="/static/blocks/loop-4.png"
alt="Do-While Loop with condition"
width={500}
height={400}
className="my-6"
/>
</div>
Use this when you need to execute at least once, then loop until a condition is met. The condition is checked **after** each iteration.
```
Example: Do-while {"<variable.i>"} < 10
- Execute blocks
- Inside loop: Increment {"<variable.i>"}
- Inside loop: Variables assigns i = {"<variable.i>"} + 1
- Check condition → Continue if true
- Check condition → Exit if false
```
</Tab>
</Tabs>
## How to Use Loops
### Creating a Loop
1. Drag a Loop block from the toolbar onto your canvas
2. Configure the loop type and parameters
3. Drag other blocks inside the loop container
4. Connect the blocks as needed
### Referencing Loop Data
There's an important distinction between referencing loop data from **inside** vs **outside** the loop:
<Tabs items={['Inside the Loop', 'Outside the Loop']}>
<Tab>
**Inside the loop**, use `<loop.>` references to access the current iteration context:
- **`<loop.index>`**: Current iteration number (0-based)
- **`<loop.currentItem>`**: Current item being processed (forEach only)
- **`<loop.items>`**: Full collection being iterated (forEach only)
```
// Inside a Function block within the loop
const idx = <loop.index>; // 0, 1, 2, ...
const item = <loop.currentItem>; // Current item
```
<Callout type="info">
These references are only available for blocks **inside** the loop container. They give you access to the current iteration's context.
</Callout>
</Tab>
<Tab>
**Outside the loop** (after it completes), reference the loop block by its name to access aggregated results:
- **`<LoopBlockName.results>`**: Array of results from all iterations
```
// If your loop block is named "Process Items"
const allResults = <processitems.results>;
// Returns: [result1, result2, result3, ...]
```
<Callout type="info">
After the loop completes, use the loop's block name (not `loop.`) to access the collected results. The block name is normalized (lowercase, no spaces).
</Callout>
</Tab>
</Tabs>
## Example Use Cases
**Processing API Results** - ForEach loop processes customer records from an API
```
API (Fetch) → Loop (ForEach) → Agent (Analyze) → Function (Store)
```
**Iterative Content Generation** - For loop generates multiple content variations
```
Loop (5x) → Agent (Generate) → Evaluator (Score) → Function (Select Best)
```
**Counter with While Loop** - While loop processes items with counter
```
Variables (i=0) → Loop (While i<10) → Agent (Process) → Variables (i++)
```
## Advanced Features
### Limitations
<Callout type="info">
Container blocks (Loops and Parallels) support nesting. You can place loops inside loops, parallels inside loops, and any combination of container blocks to build complex multi-dimensional workflows.
</Callout>
<Callout type="info">
Loops execute sequentially, not in parallel. If you need concurrent execution, use the Parallel block instead.
</Callout>
## Inputs and Outputs
<Tabs items={['Configuration', 'Variables', 'Results']}>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Loop Type</strong>: Choose between 'for', 'forEach', 'while', or 'doWhile'
</li>
<li>
<strong>Iterations</strong>: Number of times to execute (for loops)
</li>
<li>
<strong>Collection</strong>: Array or object to iterate over (forEach loops)
</li>
<li>
<strong>Condition</strong>: Boolean expression to evaluate (while/do-while loops)
</li>
</ul>
</Tab>
<Tab>
Available **inside** the loop only:
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<loop.index>"}</strong>: Current iteration number (0-based)
</li>
<li>
<strong>{"<loop.currentItem>"}</strong>: Current item being processed (forEach only)
</li>
<li>
<strong>{"<loop.items>"}</strong>: Full collection (forEach only)
</li>
</ul>
</Tab>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<blockname.results>"}</strong>: Array of all iteration results (accessed via block name)
</li>
<li>
<strong>Structure</strong>: Results maintain iteration order
</li>
<li>
<strong>Access</strong>: Available in blocks after the loop completes
</li>
</ul>
</Tab>
</Tabs>
## Best Practices
- **Set reasonable limits**: Keep iteration counts reasonable to avoid long execution times
- **Use ForEach for collections**: When processing arrays or objects, use ForEach instead of For loops
- **Handle errors gracefully**: Consider adding error handling inside loops for robust workflows
<FAQ items={[
{ question: "What is the maximum number of iterations a loop can run?", answer: "For loops (fixed count) and ForEach loops are capped at 1,000 iterations/items (from executor constants). While loops and Do-While loops with a condition have no hard iteration cap — they run until the condition evaluates to false. Do-While loops without a condition fall back to a fixed iteration count, which is capped at 1,000. Always ensure your While/Do-While conditions will eventually become false to avoid infinite loops." },
{ question: "Do loops execute iterations in parallel or sequentially?", answer: "Loops execute all iterations sequentially, one after another. If you need concurrent execution across items, use the Parallel block instead. You can also nest a Parallel block inside a Loop if you need both iteration patterns." },
{ question: "How do I access the current item inside a ForEach loop?", answer: "Inside the loop, use <loop.currentItem> to get the current item being processed and <loop.index> for the zero-based iteration number. These references are only available to blocks placed inside the loop container — blocks outside the loop cannot access them." },
{ question: "How do I access loop results after it finishes?", answer: "After the loop completes, reference the loop block by its normalized name (lowercase, no spaces) using <blockname.results>. This returns an array of all iteration results in order. For example, if your loop block is named 'Process Items', use <processitems.results>. Do not use <loop.> syntax outside the loop — that only works inside." },
{ question: "Can I nest loops inside each other?", answer: "Yes. Container blocks (Loops and Parallels) fully support nesting. You can place loops inside loops, parallels inside loops, loops inside parallels, and any combination. Each nested container maintains its own scope and iteration context independently." },
{ question: "What is the difference between a While loop and a Do-While loop?", answer: "A While loop checks its condition before each iteration, so it may execute zero times if the condition is false initially. A Do-While loop executes its body at least once, then checks the condition after each iteration to decide whether to continue. Use Do-While when you need guaranteed first execution." },
{ question: "What happens if a ForEach loop receives an empty collection?", answer: "If the ForEach loop's collection is empty, the loop body is skipped entirely and the loop outputs an empty results array. The workflow continues normally to any blocks connected after the loop." },
]} />
@@ -1,241 +0,0 @@
---
title: Parallel
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Parallel block is a container that executes multiple instances concurrently for faster workflow processing. Process items simultaneously instead of sequentially.
<Callout type="info">
Parallel blocks are container nodes that execute their contents multiple times simultaneously, unlike loops which execute sequentially.
</Callout>
## Configuration Options
### Parallel Type
Choose between two types of parallel execution:
<Tabs items={['Count-based', 'Collection-based']}>
<Tab>
**Count-based Parallel** - Execute a fixed number of parallel instances:
<div className="flex justify-center">
<Image
src="/static/blocks/parallel-1.png"
alt="Count-based parallel execution"
width={500}
height={400}
className="my-6"
/>
</div>
Use this when you need to run the same operation multiple times concurrently.
If the total count is larger than the batch size, Sim runs the work in serial batches while preserving the original result order.
```
Example: Run 5 parallel instances
- Instance 1 ┐
- Instance 2 ├─ All execute simultaneously
- Instance 3 │
- Instance 4 │
- Instance 5 ┘
```
</Tab>
<Tab>
**Collection-based Parallel** - Distribute a collection across parallel instances:
<div className="flex justify-center">
<Image
src="/static/blocks/parallel-2.png"
alt="Collection-based parallel execution"
width={500}
height={400}
className="my-6"
/>
</div>
Each instance processes one item from the collection. Large collections run in serial batches while preserving each item's original index.
```
Example: Process ["task1", "task2", "task3"] in parallel
- Instance 1: Process "task1" ┐
- Instance 2: Process "task2" ├─ All execute simultaneously
- Instance 3: Process "task3" ┘
```
</Tab>
</Tabs>
## How to Use Parallel Blocks
### Creating a Parallel Block
1. Drag a Parallel block from the toolbar onto your canvas
2. Configure the parallel type and parameters
3. Drag a single block inside the parallel container
4. Connect the block as needed
### Referencing Parallel Data
There's an important distinction between referencing parallel data from **inside** vs **outside** the parallel block:
<Tabs items={['Inside the Parallel', 'Outside the Parallel']}>
<Tab>
**Inside the parallel**, use `<parallel.>` references to access the current instance context:
- **`<parallel.index>`**: Current instance number (0-based)
- **`<parallel.currentItem>`**: Item for this instance (collection-based only)
- **`<parallel.items>`**: Full collection being distributed (collection-based only)
```
// Inside a Function block within the parallel
const idx = <parallel.index>; // 0, 1, 2, ...
const item = <parallel.currentItem>; // This instance's item
```
<Callout type="info">
These references are only available for blocks **inside** the parallel container. They give you access to the current instance's context.
</Callout>
</Tab>
<Tab>
**Outside the parallel** (after it completes), reference the parallel block by its name to access aggregated results:
- **`<ParallelBlockName.results>`**: Array of results from all instances
```
// If your parallel block is named "Process Tasks"
const allResults = <processtasks.results>;
// Returns: [result1, result2, result3, ...]
```
<Callout type="info">
After the parallel completes, use the parallel's block name (not `parallel.`) to access the collected results. The block name is normalized (lowercase, no spaces).
</Callout>
</Tab>
</Tabs>
## Example Use Cases
**Batch API Processing** - Process multiple API calls simultaneously
```
Parallel (Collection) → API (Call Endpoint) → Function (Aggregate)
```
**Multi-Model AI Processing** - Get responses from multiple AI models concurrently
```
Parallel (["gpt-4o", "claude-3.7-sonnet", "gemini-2.5-pro"]) → Agent → Evaluator (Select Best)
```
## Advanced Features
### Result Aggregation
Results from all parallel instances are automatically collected and accessible via the block name:
```javascript
// In a Function block after a parallel named "Process Tasks"
const allResults = <processtasks.results>;
// Returns: [result1, result2, result3, ...]
```
For large result sets, reference only the entry or field you need, such as `<processtasks.results[10][0].id>`. Sim keeps aggregate results indexable by storing oversized entries in execution storage and hydrating them only when an indexed server-side path is explicitly referenced.
### Batch Size
Parallel blocks run up to 20 branches at a time by default. Increase the total count or collection size to process more work; Sim will execute the next batch after the current batch finishes. You can lower the batch size to reduce concurrency for rate-limited APIs.
### Instance Isolation
Each parallel instance runs independently:
- Separate variable scopes
- No shared state between instances
- Failures in one instance don't affect others
### Limitations
<Callout type="info">
Container blocks (Loops and Parallels) support nesting. You can place parallels inside parallels, loops inside parallels, and any combination of container blocks to build complex multi-dimensional workflows.
</Callout>
<Callout type="info">
While parallel execution is faster, be mindful of:
- API rate limits when making concurrent requests
- Memory usage with large datasets
- Maximum of 20 concurrent instances per batch to prevent resource exhaustion
</Callout>
## Parallel vs Loop
Understanding when to use each:
| Feature | Parallel | Loop |
|---------|----------|------|
| Execution | Concurrent | Sequential |
| Speed | Faster for independent operations | Slower but ordered |
| Order | No guaranteed order | Maintains order |
| Use case | Independent operations | Dependent operations |
| Resource usage | Higher | Lower |
## Inputs and Outputs
<Tabs items={['Configuration', 'Variables', 'Results']}>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>Parallel Type</strong>: Choose between 'count' or 'collection'
</li>
<li>
<strong>Count</strong>: Number of instances to run (count-based)
</li>
<li>
<strong>Collection</strong>: Array or object to distribute (collection-based)
</li>
<li>
<strong>Batch size</strong>: Number of branches to run concurrently, from 1 to 20
</li>
</ul>
</Tab>
<Tab>
Available **inside** the parallel only:
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<parallel.index>"}</strong>: Instance number (0-based)
</li>
<li>
<strong>{"<parallel.currentItem>"}</strong>: Item for this instance (collection-based only)
</li>
<li>
<strong>{"<parallel.items>"}</strong>: Full collection (collection-based only)
</li>
</ul>
</Tab>
<Tab>
<ul className="list-disc space-y-2 pl-6">
<li>
<strong>{"<blockname.results>"}</strong>: Array of all instance results (accessed via block name)
</li>
<li>
<strong>Access</strong>: Available in blocks after the parallel completes
</li>
</ul>
</Tab>
</Tabs>
## Best Practices
- **Independent operations only**: Ensure operations don't depend on each other
- **Handle rate limits**: Add delays or throttling for API-heavy workflows
- **Error handling**: Each instance should handle its own errors gracefully
<FAQ items={[
{ question: "What is the maximum number of concurrent instances?", answer: "The maximum is 20 concurrent instances. This limit exists to prevent resource exhaustion and ensure stable execution." },
{ question: "Can parallel instances share state with each other?", answer: "No. Each parallel instance runs in complete isolation with its own variable scope. There is no shared state between instances, and one instance cannot read or write data from another during execution." },
{ question: "What happens if one parallel instance fails?", answer: "Failures in one instance do not affect other instances. Each instance runs independently, so the remaining instances will continue to execute normally." },
{ question: "Can I nest Parallel blocks inside other Parallel or Loop blocks?", answer: "Yes. Container blocks (Parallels and Loops) support nesting. You can place parallels inside parallels, loops inside parallels, and any combination to build multi-dimensional workflows." },
{ question: "How do I access results after the Parallel block completes?", answer: "Use <blockname.results> where blockname is the normalized name of your Parallel block (lowercase, no spaces). This returns an array containing the results from all instances." },
{ question: "Is the order of results guaranteed?", answer: "No. Because instances execute concurrently, the order of results in the output array is not guaranteed to match the input order. If ordering matters, include an index or identifier in each instance's output." },
{ question: "What is the difference between count-based and collection-based parallel?", answer: "Count-based runs a fixed number of identical instances (e.g., run 5 times). Collection-based distributes items from an array or object across instances, with each instance processing one item. Use count-based for repeated operations and collection-based for batch processing." },
]} />
@@ -1,123 +0,0 @@
---
title: Response
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Response block formats and sends structured HTTP responses back to API callers. Use it to return workflow results with proper status codes and headers.
<div className="flex justify-center">
<Image
src="/static/blocks/response.png"
alt="Response Block Configuration"
width={500}
height={400}
className="my-6"
/>
</div>
<Callout type="info">
Response blocks are exit points — when a Response block executes, it ends the workflow and sends the HTTP response immediately. Multiple Response blocks can be placed on different branches (e.g. after a Router or Condition), but only the first one to execute determines the API response.
</Callout>
## Configuration Options
### Response Data
The response data is the main content that will be sent back to the API caller. This should be formatted as JSON and can include:
- Static values
- Dynamic references to workflow variables using the `<variable.name>` syntax
- Nested objects and arrays
- Any valid JSON structure
### Status Code
A free-text input field where you can enter any valid HTTP status code (the default placeholder is 200). Common examples include:
- **200**: OK - Standard success response
- **201**: Created - Resource successfully created
- **400**: Bad Request - Invalid request parameters
- **404**: Not Found - Resource doesn't exist
- **500**: Internal Server Error - Server-side error
Any valid HTTP status code can be entered directly into the field.
### Response Headers
Configure additional HTTP headers to include in the response.
Headers are configured as key-value pairs:
| Key | Value |
|-----|-------|
| Content-Type | application/json |
| Cache-Control | no-cache |
| X-API-Version | 1.0 |
## Example Use Cases
**API Endpoint Response** - Return structured data from a search API
```
Agent (Search) → Function (Format & Paginate) → Response (200, JSON)
```
**Webhook Confirmation** - Acknowledge webhook receipt and processing
```
Webhook Trigger → Function (Process) → Response (200, Confirmation)
```
**Error Response Handling** - Return appropriate error responses
```
Condition (Error Detected) → Router → Response (400/500, Error Details)
```
## Outputs
Response blocks are exit points — when one executes, no further blocks run. The block defines outputs (`data`, `status`, `headers`) which are used to construct the HTTP response sent back to the API caller.
<Callout type="warning">
If a Response block is placed on a parallel branch, there are no guarantees about whether other parallel blocks will run or not. Execution order across parallel branches is non-deterministic, so a parallel block may execute before or after the Response block on any given run. Avoid placing Response blocks in parallel with blocks that have important side effects.
</Callout>
## Variable References
Use the `<variable.name>` syntax to dynamically insert workflow variables into your response:
```json
{
"user": {
"id": "<variable.userId>",
"name": "<variable.userName>",
"email": "<variable.userEmail>"
},
"query": "<variable.searchQuery>",
"results": "<variable.searchResults>",
"totalFound": "<variable.resultCount>",
"processingTime": "<variable.executionTime>ms"
}
```
<Callout type="warning">
Variable names are case-sensitive and must match exactly with the variables available in your workflow.
</Callout>
## Best Practices
- **Use meaningful status codes**: Choose appropriate HTTP status codes that accurately reflect the outcome of the workflow
- **Structure your responses consistently**: Maintain a consistent JSON structure across all your API endpoints for better developer experience
- **Include relevant metadata**: Add timestamps and version information to help with debugging and monitoring
- **Handle errors gracefully**: Use conditional logic in your workflow to set appropriate error responses with descriptive messages
- **Validate variable references**: Ensure all referenced variables exist and contain the expected data types before the Response block executes
<FAQ items={[
{ question: "Can I have multiple Response blocks in a workflow?", answer: "Yes. You can place multiple Response blocks on different branches (e.g. after a Router or Condition block). The first Response block to execute determines the API response and ends the workflow. This is useful for returning different responses based on conditions — for example, a 200 on the success branch and a 500 on the error branch." },
{ question: "What triggers require a Response block?", answer: "The Response block is designed for use with the API Trigger. When your workflow is invoked via the API, the Response block sends the structured HTTP response back to the caller. Other trigger types (like webhooks or schedules) do not require a Response block." },
{ question: "What is the difference between Builder and Editor mode?", answer: "Builder mode provides a visual interface for constructing your response structure with fields and types. Editor mode gives you a raw JSON code editor where you can write the response body directly. Builder mode is recommended for most use cases." },
{ question: "What is the default status code?", answer: "If you do not specify a status code, the Response block defaults to 200 (OK). You can set any valid HTTP status code including error codes like 400, 404, or 500." },
{ question: "Can the Response block connect to downstream blocks?", answer: "No. Response blocks are exit points — they end workflow execution and send the HTTP response. No further blocks can execute after a Response block." },
]} />
-130
View File
@@ -1,130 +0,0 @@
---
title: Router
---
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Router block uses AI to intelligently route workflows based on content analysis. Unlike Condition blocks that use simple rules, Routers understand context and intent. Each route you define creates a separate output port, allowing you to connect different paths to different downstream blocks.
<div className="flex justify-center">
<Image
src="/static/blocks/router.png"
alt="Router Block with Multiple Route Ports"
width={500}
height={400}
className="my-6"
/>
</div>
## Router vs Condition
**Use Router when:**
- AI-powered content analysis is needed
- Working with unstructured or varying content
- Intent-based routing is required (e.g., "route support tickets to departments")
**Use Condition when:**
- Simple rule-based decisions are sufficient
- Working with structured data or numeric comparisons
- Fast, deterministic routing is needed
## Configuration Options
### Context
The context that the Router will analyze to make routing decisions. This is the input data that gets evaluated against your route descriptions. It can be:
- A direct user query or input
- Output from a previous block
- A system-generated message
- Any text content that needs intelligent routing
### Routes
Define the possible paths that the Router can take. Each route consists of:
- **Route Title**: A name for the route (e.g., "Sales", "Support", "Technical")
- **Route Description**: A clear description of when this route should be selected (e.g., "Route here when the query is about pricing, purchasing, or sales inquiries")
Each route you add creates a **separate output port** on the Router block. Connect each port to the appropriate downstream block for that route.
### Model Selection
Choose an AI model to power the routing decision:
- **OpenAI**: GPT-4o, o1, o3, o4-mini, gpt-4.1
- **Anthropic**: Claude Sonnet 4.5
- **Google**: Gemini 2.5 Pro, Gemini 2.0 Flash
- **Other Providers**: Groq, Cerebras, xAI, DeepSeek
- **Local Models**: Ollama or VLLM compatible models
Use models with strong reasoning capabilities like GPT-4o or Claude Sonnet 4.5 for best results.
### API Key
Your API key for the selected LLM provider. This is securely stored and used for authentication.
## Outputs
- **`<router.context>`**: The context that was analyzed
- **`<router.model>`**: Model used for decision-making
- **`<router.tokens>`**: Token usage statistics
- **`<router.cost>`**: Estimated routing cost
- **`<router.selectedRoute>`**: The ID of the selected route
- **`<router.reasoning>`**: Explanation of why this route was chosen
- **`<router.selectedPath>`**: Details of the chosen destination block
## Example Use Cases
**Customer Support Triage** - Route tickets to specialized departments
```
Input (Ticket) → Router
├── [Sales Route] → Agent (Sales Team)
├── [Technical Route] → Agent (Engineering)
└── [Billing Route] → Agent (Finance)
```
**Content Classification** - Classify and route user-generated content
```
Input (Feedback) → Router
├── [Product Feedback] → Workflow (Product Team)
└── [Bug Report] → Workflow (Technical Team)
```
**Lead Qualification** - Route leads based on qualification criteria
```
Input (Lead) → Router
├── [Enterprise] → Agent (Enterprise Sales)
└── [Self-serve] → Workflow (Automated Onboarding)
```
## Error Handling
When the Router cannot determine an appropriate route for the given context, it will route to the **error path** instead of arbitrarily selecting a route. This happens when:
- The context doesn't clearly match any of the defined route descriptions
- The AI determines that none of the available routes are appropriate
## Best Practices
- **Write clear route descriptions**: Each route description should clearly explain when that route should be selected. Be specific about the criteria.
- **Make routes mutually exclusive**: When possible, ensure route descriptions don't overlap to prevent ambiguous routing decisions.
- **Connect an error path**: Handle cases where no route matches by connecting an error handler for graceful fallback behavior.
- **Use descriptive route titles**: Route titles appear in the workflow canvas, so make them meaningful for readability.
- **Test with diverse inputs**: Ensure the Router handles various input types, edge cases, and unexpected content.
- **Monitor routing performance**: Review routing decisions regularly and refine route descriptions based on actual usage patterns.
- **Choose appropriate models**: Use models with strong reasoning capabilities for complex routing decisions.
<FAQ items={[
{ question: "How does the Router decide which route to take?", answer: "The Router sends your context and all route descriptions to an LLM, which analyzes the input and selects the route whose description best matches. The LLM is prompted to be deterministic: it always prefers selecting a route over returning no match, and only reports NO_MATCH if the context is completely unrelated to every route description." },
{ question: "What happens when the Router cannot match any route?", answer: "When the LLM determines that the context does not match any defined route, it returns NO_MATCH and the Router directs execution to the error path. Connect an error handler to this path for graceful fallback behavior rather than letting the workflow fail silently." },
{ question: "Does the Router cost money to run?", answer: "Yes. The Router uses an LLM API call for every routing decision, which consumes tokens and incurs costs. You can monitor this via the <router.tokens> and <router.cost> outputs. If your routing logic can be expressed as simple boolean conditions, use the Condition block instead — it is free and faster." },
{ question: "Can I see why the Router chose a particular route?", answer: "Yes. The Router V2 block outputs a reasoning field (<router.reasoning>) that contains a brief 1-2 sentence explanation of why the selected route was chosen. This is useful for debugging and understanding routing decisions." },
{ question: "What models work best for routing?", answer: "Models with strong reasoning capabilities like GPT-4o or Claude Sonnet 4.5 tend to produce the most accurate routing decisions. For simpler routing scenarios with clearly distinct routes, a faster and cheaper model like GPT-4o-mini or Gemini Flash may be sufficient." },
{ question: "How many routes can I define?", answer: "There is no hard limit on the number of routes. Each route you define creates a separate output port on the block. However, keep in mind that more routes with overlapping descriptions make it harder for the LLM to distinguish between them, so aim for clear, mutually exclusive route descriptions." },
]} />
@@ -1,93 +0,0 @@
---
title: Variables
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Variables block updates workflow variables during execution. Variables must first be initialized in your workflow's Variables section, then you can use this block to update their values as your workflow runs.
<div className="flex justify-center">
<Image
src="/static/blocks/variables.png"
alt="Variables Block"
width={500}
height={400}
className="my-6"
/>
</div>
<Callout>
Access variables anywhere in your workflow using `<variable.variableName>` syntax.
</Callout>
## How to Use Variables
### 1. Initialize in Workflow Variables
First, create your variables in the workflow's Variables section (accessible from the workflow settings):
```
customerEmail = ""
retryCount = 0
currentStatus = "pending"
```
### 2. Update with Variables Block
Use the Variables block to update these values during execution:
```
customerEmail = <api.email>
retryCount = <variable.retryCount> + 1
currentStatus = "processing"
```
### 3. Access Anywhere
Reference variables in any block:
```
Agent prompt: "Send email to <variable.customerEmail>"
Condition: <variable.retryCount> < 5
API body: {"status": "<variable.currentStatus>"}
```
## Example Use Cases
**Loop Counter and State** - Track progress through iterations
```
Loop → Agent (Process) → Variables (itemsProcessed + 1) → Variables (Store lastResult)
```
**Retry Logic** - Track API retry attempts
```
API (Try) → Variables (retryCount + 1) → Condition (retryCount < 3)
```
**Dynamic Configuration** - Store user context for workflow
```
API (Fetch Profile) → Variables (userId, userTier) → Agent (Personalize)
```
## Outputs
The Variables block does not produce traditional block outputs. Variables are accessed globally via `<variable.variableName>` syntax from any block in the workflow, not through block output connections.
## Best Practices
- **Initialize in workflow settings**: Always create variables in the workflow Variables section before using them
- **Update dynamically**: Use Variables blocks to update values based on block outputs or calculations
- **Use in loops**: Perfect for tracking state across iterations
- **Name descriptively**: Use clear names like `currentIndex`, `totalProcessed`, or `lastError`
<FAQ items={[
{ question: "Do variables persist between workflow executions?", answer: "No. Variables are workflow-scoped and only persist for the duration of a single execution. Each new execution starts with the initial values defined in your workflow's Variables section." },
{ question: "Can multiple Variables blocks update the same variable?", answer: "Yes. All Variables blocks share the same namespace. A later Variables block can overwrite a value set by an earlier one by using the same variable name." },
{ question: "How do I reference a variable in other blocks?", answer: "Use the <variable.variableName> syntax in any block's input field. For example, <variable.retryCount> or <variable.customerEmail>." },
{ question: "Do Variables blocks produce outputs I can wire to other blocks?", answer: "Variables do not appear as traditional block outputs in the connection UI. Instead, they are accessed globally via the <variable.variableName> prefix from any block in the workflow." },
{ question: "What naming convention should I use for variables?", answer: "Use descriptive names in camelCase or snake_case. Variable names are case-sensitive, so 'retryCount' and 'RetryCount' are treated as different variables." },
{ question: "Can I use block outputs to set variable values?", answer: "Yes. You can reference any block output when setting a variable value, such as setting customerEmail to <api.email> or incrementing a counter based on previous values." },
]} />
-73
View File
@@ -1,73 +0,0 @@
---
title: Wait
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
The Wait block pauses your workflow for a specified amount of time before continuing to the next block. Use it to add delays between actions, respect API rate limits, or space out operations.
<div className="flex justify-center">
<Image
src="/static/blocks/wait.png"
alt="Wait Block"
width={500}
height={400}
className="my-6"
/>
</div>
## Configuration
### Wait Amount
Enter the duration to pause execution:
- **Input**: Positive number
- **Maximum**: 600 seconds (10 minutes) or 10 minutes
### Unit
Choose the time unit:
- **Seconds**: For short, precise delays
- **Minutes**: For longer pauses
<Callout type="info">
Wait blocks can be cancelled by stopping the workflow. The maximum wait time is 10 minutes.
</Callout>
## Outputs
- **`<wait.waitDuration>`**: The wait duration in milliseconds
- **`<wait.status>`**: Status of the wait ('waiting', 'completed', or 'cancelled')
## Example Use Cases
**API Rate Limiting** - Stay within API rate limits between requests
```
API (Request 1) → Wait (2s) → API (Request 2)
```
**Timed Notifications** - Send follow-up messages after a delay
```
Function (Send Email) → Wait (5min) → Function (Follow-up)
```
**Processing Delays** - Wait for external system to complete processing
```
API (Trigger Job) → Wait (30s) → API (Check Status)
```
## Best Practices
- **Keep waits reasonable**: Use Wait for delays up to 10 minutes. For longer delays, consider scheduled workflows
- **Monitor execution time**: Remember that waits extend total workflow duration
<FAQ items={[
{ question: "What is the maximum wait time?", answer: "The maximum wait time is 600 seconds (10 minutes). You can specify the duration in either seconds or minutes." },
{ question: "Can a Wait block be cancelled?", answer: "Yes. Wait blocks are interruptible via workflow cancellation. If the workflow is stopped while a Wait block is active, the wait is cancelled and the status output will reflect 'cancelled'." },
{ question: "What happens if I enter a value exceeding the maximum?", answer: "The wait is capped at 600 seconds. If you enter a value greater than 600 seconds (or greater than 10 minutes), it will be limited to the maximum allowed duration." },
{ question: "Does the Wait block consume resources while paused?", answer: "The Wait block performs a simple sleep for the configured duration. It does not actively consume compute resources during the wait, but the workflow execution remains open until the wait completes." },
{ question: "What outputs does the Wait block provide?", answer: "The Wait block outputs the wait duration in milliseconds (waitDuration) and a status string that indicates whether the wait is 'waiting', 'completed', or 'cancelled'." },
]} />
@@ -1,60 +0,0 @@
---
title: Basics
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Video } from '@/components/ui/video'
import { FAQ } from '@/components/ui/faq'
## How Connections Work
Connections are the pathways that allow data to flow between blocks in your workflow. In Sim, connections define how information passes from one block to another, enabling data flow throughout your workflow.
<Callout type="info">
Each connection represents a directed relationship where data flows from a source block's output
to a destination block's input.
</Callout>
### Creating Connections
<Steps>
<Step>
<strong>Select Source Block</strong>: Click on the output port of the block you want to connect
from
</Step>
<Step>
<strong>Draw Connection</strong>: Drag to the input port of the destination block
</Step>
<Step>
<strong>Confirm Connection</strong>: Release to create the connection
</Step>
</Steps>
<div className="mx-auto w-full overflow-hidden rounded-lg my-6">
<Video src="connections-build.mp4" width={700} height={450} />
</div>
### Connection Flow
The flow of data through connections follows these principles:
1. **Directional Flow**: Data always flows from outputs to inputs
2. **Execution Order**: Blocks execute in order based on their connections
3. **Data Transformation**: Data may be transformed as it passes between blocks
4. **Conditional Paths**: Some blocks (like Router and Condition) can direct flow to different paths
<Callout type="warning">
Deleting a connection will immediately stop data flow between the blocks. Make sure this is
intended before removing connections.
</Callout>
<FAQ items={[
{ question: "How does Sim determine the order blocks execute in?", answer: "Sim builds a directed acyclic graph (DAG) from your connections. Blocks with no unresolved incoming edges execute first. Once a block completes, the engine removes its edge from downstream blocks and queues any block whose incoming edges are all satisfied. This means execution order is entirely determined by how you wire your connections." },
{ question: "Can a block have multiple incoming connections?", answer: "Yes. A block with multiple incoming connections will wait until all source blocks have completed before it executes. The engine tracks incoming edges and only marks a block as ready when every incoming edge has been resolved." },
{ question: "Can a block send its output to multiple downstream blocks?", answer: "Yes. A single block can have outgoing connections to multiple destination blocks. When the source block completes, all connected downstream blocks that are ready (all their other incoming edges are satisfied) will be queued for execution." },
{ question: "What happens to downstream blocks when a Condition or Router block picks a specific path?", answer: "The engine activates only the edge matching the selected condition or route. Edges on unselected paths are deactivated, and any blocks reachable only through those deactivated edges are cascadingly skipped for that execution." },
{ question: "Are connections between blocks inside a Loop or Parallel block handled differently?", answer: "Yes. The engine inserts sentinel nodes (start and end) around Loop and Parallel subflows. Connections that cross a loop boundary are redirected through these sentinels, and Loop back-edges are wired automatically so blocks inside the loop re-execute on each iteration." },
{ question: "Is there an error-handling path for connections?", answer: "Yes. Connections can use an error source handle. If a block produces an error, only edges marked with the error handle are activated, while the normal source edges are deactivated. This lets you route errors to a dedicated error-handling branch in your workflow." },
]} />
@@ -1,198 +0,0 @@
---
title: Data Structure
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { FAQ } from '@/components/ui/faq'
When you connect blocks, understanding the data structure of different block outputs is important because the output data structure from the source block determines what values are available in the destination block. Each block type produces a specific output structure that you can reference in downstream blocks.
<Callout type="info">
Understanding these data structures is essential for effectively using connection tags and
accessing the right data in your workflows.
</Callout>
## Block Output Structures
Different block types produce different output structures. Here's what you can expect from each block type:
<Tabs items={['Agent Output', 'API Output', 'Function Output', 'Evaluator Output', 'Condition Output', 'Router Output']}>
<Tab>
```json
{
"content": "The generated text response",
"model": "gpt-4o",
"tokens": {
"prompt": 120,
"completion": 85,
"total": 205
},
"toolCalls": [...],
"cost": [...],
"usage": [...]
}
```
### Agent Block Output Fields
- **content**: The main text response generated by the agent
- **model**: The AI model used (e.g., "gpt-4o", "claude-3-opus")
- **tokens**: Token usage statistics
- **prompt**: Number of tokens in the prompt
- **completion**: Number of tokens in the completion
- **total**: Total tokens used
- **toolCalls**: Array of tool calls made by the agent (if any)
- **cost**: Array of cost objects for each tool call (if any)
- **usage**: Token usage statistics for the entire response
</Tab>
<Tab>
```json
{
"data": "Response data",
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
}
}
```
### API Block Output Fields
- **data**: The response data from the API (can be any type)
- **status**: HTTP status code of the response
- **headers**: HTTP headers returned by the API
</Tab>
<Tab>
```json
{
"result": "Function return value",
"stdout": "Console output",
}
```
### Function Block Output Fields
- **result**: The return value of the function (can be any type)
- **stdout**: Console output captured during function execution
</Tab>
<Tab>
```json
{
"content": "Evaluation summary",
"model": "gpt-5",
"tokens": {
"prompt": 120,
"completion": 85,
"total": 205
},
"metric1": 8.5,
"metric2": 7.2,
"metric3": 9.0
}
```
### Evaluator Block Output Fields
- **content**: Summary of the evaluation
- **model**: The AI model used for evaluation
- **tokens**: Token usage statistics
- **[metricName]**: Score for each metric defined in the evaluator (dynamic fields)
</Tab>
<Tab>
```json
{
"conditionResult": true,
"selectedPath": {
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
"blockType": "agent",
"blockTitle": "Follow-up Agent"
},
"selectedOption": "condition-1"
}
```
### Condition Block Output Fields
- **conditionResult**: Boolean result of the condition evaluation
- **selectedPath**: Information about the selected path
- **blockId**: ID of the next block in the selected path
- **blockType**: Type of the next block
- **blockTitle**: Title of the next block
- **selectedOption**: ID of the selected condition
</Tab>
<Tab>
```json
{
"content": "Routing decision",
"model": "gpt-4o",
"tokens": {
"prompt": 120,
"completion": 85,
"total": 205
},
"selectedPath": {
"blockId": "2acd9007-27e8-4510-a487-73d3b825e7c1",
"blockType": "agent",
"blockTitle": "Customer Service Agent"
}
}
```
### Router Block Output Fields
- **content**: The routing decision text
- **model**: The AI model used for routing
- **tokens**: Token usage statistics
- **selectedPath**: Information about the selected path
- **blockId**: ID of the selected destination block
- **blockType**: Type of the selected block
- **blockTitle**: Title of the selected block
</Tab>
</Tabs>
## Custom Output Structures
Some blocks may produce custom output structures based on their configuration:
1. **Agent Blocks with Response Format**: When using a response format in an Agent block, the output structure will match the defined schema instead of the standard structure.
2. **Function Blocks**: The `result` field can contain any data structure returned by your function code.
3. **API Blocks**: The `data` field will contain whatever the API returns, which could be any valid JSON structure.
<Callout type="warning">
Always check the actual output structure of your blocks during development to ensure you're
referencing the correct fields in your connections.
</Callout>
## Nested Data Structures
Many block outputs contain nested data structures. You can access these using dot notation in connection tags:
```
<blockName.path.to.nested.data>
```
For example:
- `<agent1.tokens.total>` - Access the total tokens from an Agent block
- `<api1.data.results[0].id>` - Access the ID of the first result from an API response
- `<function1.result.calculations.total>` - Access a nested field in a Function block's result
<FAQ items={[
{ question: "What output fields does an Agent block produce?", answer: "An Agent block outputs content (the text response), model (the model used, e.g. gpt-4o), tokens (an object with prompt, completion, and total counts), and optionally toolCalls, cost, and usage arrays when tools are invoked." },
{ question: "What does the API block output look like?", answer: "The API block returns data (the response body, which can be any type), status (the HTTP status code as a number), and headers (an object containing the response HTTP headers)." },
{ question: "What does a Function block return?", answer: "A Function block outputs result (the return value of your function, which can be any type) and stdout (any console output captured during execution)." },
{ question: "How does the Condition block output differ from the Router block?", answer: "The Condition block outputs conditionResult (a boolean), selectedPath (with blockId, blockType, and blockTitle of the next block), and selectedOption (the ID of the matched condition). The Router block outputs content (the routing decision text), model, tokens, and selectedPath, but does not include conditionResult or selectedOption." },
{ question: "What happens to the Agent block output when a response format schema is configured?", answer: "When you define a response format on an Agent block, the output structure matches your defined schema instead of the standard content/model/tokens structure. Always verify the actual output shape when using response formats." },
{ question: "How do I access deeply nested data from an API response?", answer: "Use dot notation with bracket indices in your connection tags. For example, <api1.data.results[0].id> navigates into the data field, then into the results array at index 0, and retrieves the id property." },
]} />
@@ -1,52 +0,0 @@
---
title: Overview
description: Connect your blocks to one another.
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { ConnectIcon } from '@/components/icons'
import { Video } from '@/components/ui/video'
import { FAQ } from '@/components/ui/faq'
Connections are the pathways that allow data to flow between blocks in your workflow. They define how information is passed from one block to another, enabling you to create sophisticated, multi-step processes.
<Callout type="info">
Properly configured connections are essential for creating effective workflows. They determine how
data moves through your system and how blocks interact with each other.
</Callout>
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="connections.mp4" />
</div>
## Connection Types
Sim supports different types of connections that enable various workflow patterns:
<Cards>
<Card title="Connection Basics" href="/connections/basics">
Learn how connections work and how to create them in your workflows
</Card>
<Card title="Connection Tags" href="/connections/tags">
Understand how to use connection tags to reference data between blocks
</Card>
<Card title="Data Structure" href="/connections/data-structure">
Explore the output data structures of different block types
</Card>
<Card title="Accessing Data" href="/connections/accessing-data">
Learn techniques for accessing and manipulating connected data
</Card>
<Card title="Best Practices" href="/connections/best-practices">
Follow recommended patterns for effective connection management
</Card>
</Cards>
<FAQ items={[
{ question: "How does data actually flow between connected blocks?", answer: "The execution engine builds a directed acyclic graph (DAG) from your connections and processes blocks in dependency order. When a block finishes, its output is stored in the execution context. Downstream blocks reference that output using connection tags like <BlockName.response>, which the variable resolver replaces with the actual data at execution time." },
{ question: "Can a block receive input from multiple upstream blocks?", answer: "Yes. A block waits until all of its incoming connections have been fulfilled before it executes. The engine tracks incoming edges for each node and only marks a block as ready when every upstream dependency has completed. You can reference outputs from any connected block using their respective connection tags." },
{ question: "What happens if an upstream block fails?", answer: "If a block errors, the engine activates the error edge (if one exists) and deactivates the normal output edge. Downstream blocks on the success path will not execute. You can connect an error handle to a separate block to build fallback or recovery logic." },
{ question: "Do connections support conditional branching?", answer: "Yes. Router and Condition blocks produce a selected route or option that determines which outgoing edge is activated. Only the blocks on the chosen path will execute. Edges on unselected paths are deactivated and their entire downstream subgraph is skipped." },
{ question: "Can blocks in parallel branches share data with each other?", answer: "Blocks within the same parallel branch cannot directly reference blocks in a sibling branch because branches execute independently. However, once all branches complete and the parallel block exits, downstream blocks can access the aggregated results from all branches." },
{ question: "How are connection tags formatted?", answer: "Connection tags use angle-bracket syntax: <BlockName.property>. For nested data you can chain dot notation, such as <BlockName.response.items[0].name>. The resolver walks the object path at execution time and substitutes the resolved value into your input field." },
]} />
@@ -1,3 +0,0 @@
{
"pages": ["index", "basics", "data-structure", "tags"]
}
@@ -1,121 +0,0 @@
---
title: Tags
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Video } from '@/components/ui/video'
import { FAQ } from '@/components/ui/faq'
Connection tags are visual representations of the data available from connected blocks, providing an easy way to reference data between blocks and outputs from previous blocks in your workflow.
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="connections.mp4" />
</div>
### What Are Connection Tags?
Connection tags are interactive elements that appear when blocks are connected. They represent the data that can flow from one block to another and allow you to:
- Visualize available data from source blocks
- Reference specific data fields in destination blocks
- Create dynamic data flows between blocks
<Callout type="info">
Connection tags make it easy to see what data is available from previous blocks and use it in your
current block without having to remember complex data structures.
</Callout>
## Using Connection Tags
There are two primary ways to use connection tags in your workflows:
<div className="my-6 grid grid-cols-1 gap-4 md:grid-cols-2">
<div className="rounded-lg border border-gray-200 p-4 dark:border-gray-800">
<h3 className="mb-2 text-lg font-medium">Drag and Drop</h3>
<div className="text-sm text-gray-600 dark:text-gray-400">
Click on a connection tag and drag it into input fields of destination blocks. A dropdown will
appear showing available values.
</div>
<ol className="mt-2 list-decimal pl-5 text-sm text-gray-600 dark:text-gray-400">
<li>Hover over a connection tag to see available data</li>
<li>Click and drag the tag to an input field</li>
<li>Select the specific data field from the dropdown</li>
<li>The reference is inserted automatically</li>
</ol>
</div>
<div className="rounded-lg border border-gray-200 p-4 dark:border-gray-800">
<h3 className="mb-2 text-lg font-medium">Angle Bracket Syntax</h3>
<div className="text-sm text-gray-600 dark:text-gray-400">
Type <code>&lt;&gt;</code> in input fields to see a dropdown of available connection values
from previous blocks.
</div>
<ol className="mt-2 list-decimal pl-5 text-sm text-gray-600 dark:text-gray-400">
<li>Click in any input field where you want to use connected data</li>
<li>
Type <code>&lt;&gt;</code> to trigger the connection dropdown
</li>
<li>Browse and select the data you want to reference</li>
<li>Continue typing or select from the dropdown to complete the reference</li>
</ol>
</div>
</div>
## Tag Syntax
Connection tags use a simple syntax to reference data:
```
<blockName.path.to.data>
```
Where:
- `blockName` is the name of the source block
- `path.to.data` is the path to the specific data field
For example:
- `<agent1.content>` - References the content field from a block with ID "agent1"
- `<api2.data.users[0].name>` - References the name of the first user in the users array from the data field of a block with ID "api2"
## Dynamic Tag References
Connection tags are evaluated at runtime, which means:
1. They always reference the most current data
2. They can be used in expressions and combined with static text
3. They can be nested within other data structures
### Examples
```javascript
// Reference in text
"The user's name is <userBlock.name>"
// Reference in JSON
{
"userName": "<userBlock.name>",
"orderTotal": <apiBlock.data.total>
}
// Reference in code
const greeting = "Hello, <userBlock.name>!";
const total = <apiBlock.data.total> * 1.1; // Add 10% tax
```
<Callout type="warning">
When using connection tags in numeric contexts, make sure the referenced data is actually a number
to avoid type conversion issues.
</Callout>
<FAQ items={[
{ question: "How are tag references resolved at runtime?", answer: "The executor uses a chain of resolvers. Each reference like <blockName.path> is matched against resolvers in order: loop references, parallel references, workflow variables, environment variables, and finally block output references. The first resolver that recognizes the reference handles it." },
{ question: "Does the block name in a tag reference need to match exactly?", answer: "Block names are normalized by converting to lowercase and removing spaces before matching. So <My Agent.content> and <myagent.content> resolve to the same block. However, the field path after the block name (e.g., content, data.users) is case-sensitive." },
{ question: "Can I reference environment variables in tag syntax?", answer: "Yes, but environment variables use double-brace syntax instead of angle brackets: {{MY_VAR}}. These are resolved by a dedicated environment variable resolver during execution." },
{ question: "What happens if I reference a block that did not execute on the current path?", answer: "If the referenced block exists in the workflow but did not produce output (for example, it was on an unselected condition branch), the reference resolves to an empty value. In most blocks this becomes an empty string; in Function blocks it becomes null." },
{ question: "Can I access array elements inside a tag reference?", answer: "Yes. Use bracket notation for array indices within the dot path, for example <api1.data.users[0].name>. The resolver supports multiple levels of array indexing like items[0][1] as well." },
{ question: "How are tag values formatted inside Function blocks versus other blocks?", answer: "In Function blocks, resolved values are formatted as code literals (strings are quoted, objects are JSON, null stays as null) so they can be used directly in JavaScript or Python code. In other block types, objects are JSON-stringified and primitives are converted to plain strings." },
{ question: "Can I mix static text with tag references?", answer: "Yes. You can embed tag references anywhere in a text string, such as \"Hello, <agent1.content>! Your order total is <api1.data.total>.\" The resolver replaces each tag in place while leaving the surrounding text intact." },
]} />
@@ -1,59 +0,0 @@
---
title: Copilot
description: Your per-workflow AI assistant for building and editing workflows.
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Image } from '@/components/ui/image'
import { Video } from '@/components/ui/video'
import { FAQ } from '@/components/ui/faq'
Copilot is the AI assistant built into every workflow editor. It is scoped to the workflow you have open — it reads the current structure, makes changes directly, and saves checkpoints so you can revert if needed.
For workspace-wide tasks (managing multiple workflows, running research, working with tables, scheduling jobs), use [Mothership](/mothership).
<Callout type="info">
Copilot is a Sim-managed service. For self-hosted deployments, go to [sim.ai](https://sim.ai) → Settings → Copilot, generate a Copilot API key, then set `COPILOT_API_KEY` in your self-hosted environment.
</Callout>
<Video src="copilot/copilot.mp4" width={700} height={450} />
## What Copilot Can Do
Copilot can read and modify the workflow you are currently editing:
- Add, configure, and connect blocks
- Edit existing block configurations
- Delete blocks and connections
- Debug failures by reading execution logs
- Answer questions about the workflow or how Sim works
## Chat History
Click **History** (clock icon) in the Copilot header to see past conversations for this workflow. Click any chat to resume it, or click **+** to start a new one.
## File Attachments
Click the attachment icon in the input to upload files alongside your message. Copilot can read images, PDFs, and text-based files as context.
## Checkpoints
When Copilot modifies a workflow, it saves a checkpoint of the previous state.
To revert: hover over a Copilot message and click the checkpoints icon, then click **Revert** on the state you want to restore. Reverting cannot be undone.
## Thinking
For complex requests, Copilot may show its reasoning in an expandable thinking block before responding. The block shows how long the thinking took and collapses after the response is complete.
## Usage
Copilot usage is billed per token and counts toward your plan's credit usage. If you reach your limit, enable on-demand billing from Settings → Subscription.
<FAQ items={[
{ question: "How is Copilot different from Mothership?", answer: "Copilot is scoped to the workflow you have open — it reads and edits that workflow's blocks and connections. Mothership has access to your entire workspace and can build workflows, manage tables, run research, schedule jobs, and take actions across integrations." },
{ question: "Can Copilot access other workflows or workspace data?", answer: "Copilot is scoped to the current workflow. For tasks that span multiple workflows or require workspace-level context, use Mothership." },
{ question: "Can I revert changes Copilot made?", answer: "Yes. Copilot saves a checkpoint before each change. Hover over the message and click the checkpoints icon to see saved states, then click Revert to restore one. Reverting cannot be undone." },
{ question: "How does Copilot billing work?", answer: "Copilot usage is billed per token and counts toward your plan's credit usage. If you reach your limit, enable on-demand billing from Settings → Subscription." },
{ question: "How do I set up Copilot for a self-hosted deployment?", answer: "Go to sim.ai → Settings → Copilot and generate a Copilot API key. Set the COPILOT_API_KEY environment variable in your self-hosted environment. Copilot runs on Sim's infrastructure regardless of where you host the application." },
]} />
@@ -1,5 +0,0 @@
{
"title": "Secrets",
"pages": ["index"],
"defaultOpen": false
}
-333
View File
@@ -1,333 +0,0 @@
---
title: External API
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Video } from '@/components/ui/video'
Sim provides a comprehensive external API for querying workflow run logs. To react to workflow runs in real time, use the Sim trigger block to run a workflow on workspace events like execution errors, successes, and deployments.
## Authentication
All API requests require an API key passed in the `x-api-key` header:
```bash
curl -H "x-api-key: YOUR_API_KEY" \
https://sim.ai/api/v1/logs?workspaceId=YOUR_WORKSPACE_ID
```
You can generate API keys from the Sim platform and navigate to **Settings**, then go to **Sim Keys** and click **Create**.
## Logs API
All API responses include information about your workflow run limits and usage:
```json
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 150, // Sustained rate limit per minute
"maxBurst": 300, // Maximum burst capacity
"remaining": 298, // Current tokens available (up to maxBurst)
"resetAt": "..." // When tokens next refill
},
"async": {
"requestsPerMinute": 1000, // Sustained rate limit per minute
"maxBurst": 2000, // Maximum burst capacity
"remaining": 1998, // Current tokens available
"resetAt": "..." // When tokens next refill
}
},
"usage": {
"currentPeriodCost": 1.234, // Current billing period usage in USD
"limit": 10, // Usage limit in USD
"plan": "pro", // Current subscription plan
"isExceeded": false // Whether limit is exceeded
}
}
```
**Note:** Rate limits use a token bucket algorithm. `remaining` can exceed `requestsPerMinute` up to `maxBurst` when you haven't used your full allowance recently, allowing for burst traffic. The rate limits in the response body are for workflow runs. The rate limits for calling this API endpoint are in the response headers (`X-RateLimit-*`).
### Query Logs
Query workflow run logs with extensive filtering options.
<Tabs items={['Request', 'Response']}>
<Tab value="Request">
```http
GET /api/v1/logs
```
**Required Parameters:**
- `workspaceId` - Your workspace ID
**Optional Filters:**
- `workflowIds` - Comma-separated workflow IDs
- `folderIds` - Comma-separated folder IDs
- `triggers` - Comma-separated trigger types: `api`, `webhook`, `schedule`, `manual`, `chat`
- `level` - Filter by level: `info`, `error`
- `startDate` - ISO timestamp for date range start
- `endDate` - ISO timestamp for date range end
- `executionId` - Exact run ID match
- `minDurationMs` - Minimum run duration in milliseconds
- `maxDurationMs` - Maximum run duration in milliseconds
- `minCost` - Minimum run cost
- `maxCost` - Maximum run cost
- `model` - Filter by AI model used
**Pagination:**
- `limit` - Results per page (default: 100)
- `cursor` - Cursor for next page
- `order` - Sort order: `desc`, `asc` (default: desc)
**Detail Level:**
- `details` - Response detail level: `basic`, `full` (default: basic)
- `includeTraceSpans` - Include trace spans (default: false)
- `includeFinalOutput` - Include final output (default: false)
</Tab>
<Tab value="Response">
```json
{
"data": [
{
"id": "log_abc123",
"workflowId": "wf_xyz789",
"executionId": "exec_def456",
"level": "info",
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"cost": {
"total": 0.00234
},
"files": null
}
],
"nextCursor": "eyJzIjoiMjAyNS0wMS0wMVQxMjozNDo1Ni43ODlaIiwiaWQiOiJsb2dfYWJjMTIzIn0",
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 150,
"maxBurst": 300,
"remaining": 298,
"resetAt": "2025-01-01T12:35:56.789Z"
},
"async": {
"requestsPerMinute": 1000,
"maxBurst": 2000,
"remaining": 1998,
"resetAt": "2025-01-01T12:35:56.789Z"
}
},
"usage": {
"currentPeriodCost": 1.234,
"limit": 10,
"plan": "pro",
"isExceeded": false
}
}
}
```
</Tab>
</Tabs>
### Get Log Details
Retrieve detailed information about a specific log entry.
<Tabs items={['Request', 'Response']}>
<Tab value="Request">
```http
GET /api/v1/logs/{id}
```
</Tab>
<Tab value="Response">
```json
{
"data": {
"id": "log_abc123",
"workflowId": "wf_xyz789",
"executionId": "exec_def456",
"level": "info",
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"workflow": {
"id": "wf_xyz789",
"name": "My Workflow",
"description": "Process customer data"
},
"executionData": {
"traceSpans": [...],
"finalOutput": {...}
},
"cost": {
"total": 0.00234,
"tokens": {
"prompt": 123,
"completion": 456,
"total": 579
},
"models": {
"gpt-4o": {
"input": 0.001,
"output": 0.00134,
"total": 0.00234,
"tokens": {
"prompt": 123,
"completion": 456,
"total": 579
}
}
}
},
"limits": {
"workflowExecutionRateLimit": {
"sync": {
"requestsPerMinute": 150,
"maxBurst": 300,
"remaining": 298,
"resetAt": "2025-01-01T12:35:56.789Z"
},
"async": {
"requestsPerMinute": 1000,
"maxBurst": 2000,
"remaining": 1998,
"resetAt": "2025-01-01T12:35:56.789Z"
}
},
"usage": {
"currentPeriodCost": 1.234,
"limit": 10,
"plan": "pro",
"isExceeded": false
}
}
}
}
```
</Tab>
</Tabs>
### Get Run Details
Retrieve run details including the workflow state snapshot.
<Tabs items={['Request', 'Response']}>
<Tab value="Request">
```http
GET /api/v1/logs/executions/{executionId}
```
</Tab>
<Tab value="Response">
```json
{
"executionId": "exec_def456",
"workflowId": "wf_xyz789",
"workflowState": {
"blocks": {...},
"edges": [...],
"loops": {...},
"parallels": {...}
},
"executionMetadata": {
"trigger": "api",
"startedAt": "2025-01-01T12:34:56.789Z",
"endedAt": "2025-01-01T12:34:57.123Z",
"totalDurationMs": 334,
"cost": {...}
}
}
```
</Tab>
</Tabs>
## Best Practices
1. **Polling Strategy**: When polling for logs, use cursor-based pagination with `order=asc` and `startDate` to fetch new logs efficiently.
2. **Privacy**: By default, `finalOutput` and `traceSpans` are excluded from responses. Only enable these if you need the data and understand the privacy implications.
3. **Rate Limiting**: Implement exponential backoff when you receive 429 responses. Check the `Retry-After` header for the recommended wait time.
## Rate Limiting
The API uses a **token bucket algorithm** for rate limiting, providing fair usage while allowing burst traffic:
| Plan | Requests/Minute | Burst Capacity |
|------|-----------------|----------------|
| Free | 30 | 60 |
| Pro | 100 | 200 |
| Team | 200 | 400 |
| Enterprise | 500 | 1000 |
**How it works:**
- Tokens refill at `requestsPerMinute` rate
- You can accumulate up to `maxBurst` tokens when idle
- Each request consumes 1 token
- Burst capacity allows handling traffic spikes
Rate limit information is included in response headers:
- `X-RateLimit-Limit`: Requests per minute (refill rate)
- `X-RateLimit-Remaining`: Current tokens available
- `X-RateLimit-Reset`: ISO timestamp when tokens next refill
## Example: Polling for New Logs
```javascript
let cursor = null;
const workspaceId = 'YOUR_WORKSPACE_ID';
const startDate = new Date().toISOString();
async function pollLogs() {
const params = new URLSearchParams({
workspaceId,
startDate,
order: 'asc',
limit: '100'
});
if (cursor) {
params.append('cursor', cursor);
}
const response = await fetch(
`https://sim.ai/api/v1/logs?${params}`,
{
headers: {
'x-api-key': 'YOUR_API_KEY'
}
}
);
if (response.ok) {
const data = await response.json();
// Process new logs
for (const log of data.data) {
console.log(`New execution: ${log.executionId}`);
}
// Update cursor for next poll
if (data.nextCursor) {
cursor = data.nextCursor;
}
}
}
// Poll every 30 seconds
setInterval(pollLogs, 30000);
```
import { FAQ } from '@/components/ui/faq'
<FAQ items={[
{ question: "How do I trigger an async run via the API?", answer: "Set the X-Execution-Mode header to 'async' on your POST request to /api/workflows/{id}/execute. The API returns a 202 response with a jobId, executionId, and a statusUrl you can poll to check when the job completes. Async mode does not support draft state, workflow overrides, or selective output options." },
{ question: "What authentication methods does the API support?", answer: "The API supports two authentication methods: API keys passed in the x-api-key header, and session-based authentication for logged-in users. API keys can be generated from Settings > Sim Keys in the platform. Workflows with public API access enabled can also be called without authentication." },
{ question: "What rate limits apply to the Logs API?", answer: "Rate limits use a token bucket algorithm. Free plans get 30 requests/minute with 60 burst capacity, Pro gets 100/200, Team gets 200/400, and Enterprise gets 500/1000. These are separate from workflow run rate limits, which are shown in the response body." },
{ question: "How do I get notified when a workflow run completes or fails?", answer: "Add the Sim trigger block to a workflow and deploy it. It runs on workspace events like execution successes, errors, deployments, and alert conditions such as latency spikes or cost thresholds — and you can compose any blocks downstream (Slack, email, webhooks, custom logic) to deliver the alert." },
]} />
@@ -1,109 +0,0 @@
---
title: Basics
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { Image } from '@/components/ui/image'
Understanding how workflows run in Sim is key to building efficient and reliable automations. The execution engine automatically handles dependencies, concurrency, and data flow to ensure your workflows run smoothly and predictably.
## How Workflows Execute
Sim's execution engine processes workflows intelligently by analyzing dependencies and running blocks in the most efficient order possible.
### Concurrent Execution by Default
Multiple blocks run concurrently when they don't depend on each other. This dramatically improves performance without requiring manual configuration.
<Image
src="/static/execution/concurrency.png"
alt="Multiple blocks running concurrently after the Start block"
width={800}
height={500}
/>
In this example, both the Customer Support and Deep Researcher agent blocks execute simultaneously after the Start block, maximizing efficiency.
### Automatic Output Combination
When blocks have multiple dependencies, the execution engine automatically waits for all dependencies to complete, then provides their combined outputs to the next block. No manual combining required.
<Image
src="/static/execution/combination.png"
alt="Function block automatically receiving outputs from multiple previous blocks"
width={800}
height={500}
/>
The Function block receives outputs from both agent blocks as soon as they complete, allowing you to process the combined results.
### Smart Routing
Workflows can branch in multiple directions using routing blocks. The execution engine supports both deterministic routing (with Condition blocks) and AI-powered routing (with Router blocks).
<Image
src="/static/execution/routing.png"
alt="Workflow showing both conditional and router-based branching"
width={800}
height={500}
/>
This workflow demonstrates how a run can follow different paths based on conditions or AI decisions, with each path running independently.
## Block Types
Sim provides different types of blocks that serve specific purposes in your workflows:
<Cards>
<Card title="Triggers" href="/triggers">
**Starter blocks** initiate workflows and **Webhook blocks** respond to external events. Every workflow needs a trigger to begin a run.
</Card>
<Card title="Processing Blocks" href="/blocks">
**Agent blocks** interact with AI models, **Function blocks** run custom code, and **API blocks** connect to external services. These blocks transform and process your data.
</Card>
<Card title="Control Flow" href="/blocks">
**Router blocks** use AI to choose paths, **Condition blocks** branch based on logic, and **Loop/Parallel blocks** handle iterations and concurrency.
</Card>
<Card title="Output & Response" href="/blocks">
**Response blocks** format final outputs for APIs and chat interfaces, returning structured results from your workflows.
</Card>
</Cards>
All blocks run automatically based on their dependencies - you don't need to manually manage run order or timing.
## Run Monitoring
When workflows run, Sim provides real-time visibility into the process:
- **Live Block States**: See which blocks are currently running, completed, or failed
- **Run Logs**: Detailed logs appear in real-time showing inputs, outputs, and any errors
- **Performance Metrics**: Track run time and costs for each block
- **Path Visualization**: Understand which paths were taken through your workflow
<Callout type="info">
All run details are captured and available for review even after workflows complete, helping with debugging and optimization.
</Callout>
## Key Principles
Understanding these core principles will help you build better workflows:
1. **Dependency-Based Execution**: Blocks only run when all their dependencies have completed
2. **Automatic Parallelization**: Independent blocks run concurrently without configuration
3. **Smart Data Flow**: Outputs flow automatically to connected blocks
4. **Error Handling**: Failed blocks stop their run path but don't affect independent paths
5. **Response Blocks as Exit Points**: When a Response block runs, the entire workflow stops and the API response is sent immediately. Multiple Response blocks can exist on different branches — the first one to run wins
6. **State Persistence**: All block outputs and run details are preserved for debugging
7. **Cycle Protection**: Workflows that call other workflows (via Workflow blocks, MCP tools, or API blocks) are tracked with a call chain. If the chain exceeds 25 hops, the run is stopped to prevent infinite loops
## Next Steps
Now that you understand execution basics, explore:
- **[Block Types](/blocks)** - Learn about specific block capabilities
- **[Logging](/execution/logging)** - Monitor workflow runs and debug issues
- **[Cost Calculation](/execution/costs)** - Understand and optimize workflow costs
- **[Triggers](/triggers)** - Set up different ways to run your workflows
@@ -1,170 +0,0 @@
---
title: Overview
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
Sim's execution engine brings your workflows to life by processing blocks in the correct order, managing data flow, and handling errors gracefully, so you can understand exactly how workflows run in Sim.
<Callout type="info">
Every workflow run follows a deterministic path based on your block connections and logic, ensuring predictable and reliable results.
</Callout>
## Documentation Overview
<Cards>
<Card title="Execution Basics" href="/execution/basics">
Learn about the fundamental execution flow, block types, and how data flows through your
workflow
</Card>
<Card title="Logging" href="/execution/logging">
Monitor workflow runs with comprehensive logging and real-time visibility
</Card>
<Card title="Cost Calculation" href="/execution/costs">
Understand how workflow run costs are calculated and optimized
</Card>
<Card title="External API" href="/execution/api">
Access run logs and set up webhooks programmatically via REST API
</Card>
<Card title="API Deployment" href="/execution/api-deployment">
Deploy your workflow as a REST API endpoint with sync, streaming, and async modes
</Card>
<Card title="Chat Deployment" href="/execution/chat">
Deploy your workflow as a conversational chat interface with streaming, file uploads, and voice
</Card>
</Cards>
## Key Concepts
### Topological Execution
Blocks run in dependency order, similar to how a spreadsheet recalculates cells. The execution engine automatically determines which blocks can run based on completed dependencies.
### Path Tracking
The engine actively tracks run paths through your workflow. Router and Condition blocks dynamically update these paths, ensuring only relevant blocks run.
### Layer-Based Processing
Instead of executing blocks one-by-one, the engine identifies layers of blocks that can run in parallel, optimizing performance for complex workflows.
### Run Context
Each workflow maintains a rich context during a run containing:
- Block outputs and states
- Active run paths
- Loop and parallel iteration tracking
- Environment variables
- Routing decisions
## Deployment Snapshots
API, Chat, Schedule, and Webhook runs use the workflow’s active deployment snapshot. Manual runs from the editor use the current draft canvas state, letting you test changes before deploying. Publish a new deployment whenever you change the canvas so every trigger uses the updated version.
<div className="flex justify-center my-6">
<Image
src="/static/execution/deployment-versions.png"
alt="Deployment versions table"
width={500}
height={280}
className="rounded-xl border border-border shadow-sm"
/>
</div>
### Version History
The **General** tab in the Deploy modal shows a version history table for every deployment. Each row shows the version name, who deployed it, and when.
<div className="flex justify-center">
<Image
src="/static/execution/deployment-versions-table.png"
alt="Version history table with multiple deployment versions"
width={600}
height={650}
className="my-6"
/>
</div>
From the version table you can:
- **Rename** a version to give it a meaningful label (e.g., "v2 — added error handling")
- **Add a description** with notes about what changed in that deployment
- **Promote to live** to roll back to an older version — this makes the selected version the active deployment without changing your draft canvas
- **Load into editor** to restore a previous version's workflow into the canvas for editing and redeploying
- **Preview a version** by selecting a row to view that version's workflow in the canvas preview, then toggle between **Live** and the selected version
<div className="flex justify-center">
<Image
src="/static/execution/deployment-version-preview.png"
alt="Previewing a selected deployment version"
width={600}
height={650}
className="my-6"
/>
</div>
<Callout type="info">
Promoting an old version takes effect immediately — all API, Chat, Schedule, and Webhook executions will use the promoted version. Your draft canvas is not affected.
</Callout>
## Programmatic Access
Run workflows from your applications using our official SDKs:
```bash
# TypeScript/JavaScript
npm install simstudio-ts-sdk
# Python
pip install simstudio-sdk
```
```typescript
// TypeScript Example
import { SimStudioClient } from 'simstudio-ts-sdk';
const client = new SimStudioClient({
apiKey: 'your-api-key'
});
const result = await client.executeWorkflow('workflow-id', {
input: { message: 'Hello' }
});
```
## Best Practices
### Design for Reliability
- Handle errors gracefully with appropriate fallback paths
- Use environment variables for sensitive data
- Add logging to Function blocks for debugging
### Optimize Performance
- Minimize external API calls where possible
- Use parallel execution for independent operations
- Cache results with Memory blocks when appropriate
### Monitor Runs
- Review logs regularly to understand performance patterns
- Track costs for AI model usage
- Use workflow snapshots to debug issues
## What's Next?
Start with [Execution Basics](/execution/basics) to understand how workflows run, then explore [Logging](/execution/logging) to monitor your runs and [Cost Calculation](/execution/costs) to optimize your spending.
<FAQ items={[
{ question: "What are the run timeout limits?", answer: "Synchronous runs (API, chat) have a default timeout of 5 minutes on the Free plan and 50 minutes on Pro, Team, and Enterprise plans. Asynchronous runs (schedules, webhooks) allow up to 90 minutes across all plans. These limits are configurable by the platform administrator." },
{ question: "How does parallel execution work?", answer: "The engine identifies layers of blocks with no dependencies on each other and runs them concurrently. Within loops and parallel blocks, the engine supports up to 20 parallel branches by default and up to 1,000 loop iterations. Nested subflows (loops inside parallels, or vice versa) are supported up to 10 levels deep." },
{ question: "Can I cancel a running workflow?", answer: "Yes. The engine supports cancellation through an abort signal mechanism. When you cancel a run, the engine checks for cancellation between blocks (at roughly 500ms intervals when using Redis-backed cancellation). Any in-progress blocks complete, and the run returns with a cancelled status." },
{ question: "What is a deployment snapshot?", answer: "A deployment snapshot is a frozen copy of your workflow at the time you click Deploy. Trigger-based runs (API, chat, schedule, webhook) use the active snapshot, not your draft canvas. Manual runs from the editor use the current draft canvas state, so you can test changes before deploying. You can view, compare, and roll back snapshots from the Deploy modal." },
{ question: "How are run costs calculated?", answer: "Costs are tracked per block based on the AI model used. Each block log records input tokens, output tokens, and the computed cost using the model's pricing. The total workflow cost is the sum of all block-level costs for that run. You can review costs in the run logs." },
{ question: "What happens when a block fails during a run?", answer: "When a block throws an error, the engine captures the error message in the block log, finalizes any incomplete logs with timing data, and halts the run with a failure status. If the failing block has an error output handle connected to another block, that error path is followed instead of halting entirely." },
{ question: "Can I re-run part of a workflow without starting from scratch?", answer: "Yes. The run-from-block feature lets you select a specific block and re-run from that point. The engine computes which upstream blocks need to be re-run (the dirty set) and preserves cached outputs from blocks that have not changed, so only the affected portion of the workflow re-runs." },
]} />
@@ -1,159 +0,0 @@
---
title: Logging
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import { Image } from '@/components/ui/image'
Sim provides comprehensive logging for all workflow runs, giving you complete visibility into how your workflows run, what data flows through them, and where issues might occur.
## Logging System
Sim offers two complementary logging interfaces to match different workflows and use cases:
### Real-Time Console
During manual or chat workflow runs, logs appear in real-time in the Console panel on the right side of the workflow editor:
<div className="flex justify-center">
<Image
src="/static/logs/console.png"
alt="Real-time Console Panel"
width={400}
height={300}
className="my-6"
/>
</div>
The console shows:
- Block progress with active block highlighting
- Real-time outputs as blocks complete
- Timing for each block
- Success/error status indicators
### Logs Page
All workflow runs—whether triggered manually, via API, Chat, Schedule, or Webhook—are logged to the dedicated Logs page:
<div className="flex justify-center">
<Image
src="/static/logs/logs.png"
alt="Logs Page"
width={600}
height={400}
className="my-6"
/>
</div>
The Logs page provides:
- Comprehensive filtering by time range, status, trigger type, folder, and workflow
- Search functionality across all logs
- Live mode for real-time updates
- 7-day log retention (upgradeable for longer retention)
## Log Details Sidebar
Clicking on any log entry opens a detailed sidebar view:
<div className="flex justify-center">
<Image
src="/static/logs/logs-sidebar.png"
alt="Logs Sidebar Details"
width={600}
height={400}
className="my-6"
/>
</div>
### Block Input/Output
View the complete data flow for each block with tabs to switch between:
<Tabs items={['Output', 'Input']}>
<Tab>
**Output Tab** shows the block's result:
- Structured data with JSON formatting
- Markdown rendering for AI-generated content
- Copy button for easy data extraction
</Tab>
<Tab>
**Input Tab** displays what was passed to the block:
- Resolved variable values
- Referenced outputs from other blocks
- Environment variables used
- API keys are automatically redacted for security
</Tab>
</Tabs>
### Run Timeline
For workflow-level logs, view detailed run metrics:
- Start and end timestamps
- Total workflow duration
- Individual block run times
- Performance bottleneck identification
## Workflow Snapshots
For any logged run, click "View Snapshot" to see the exact workflow state at the time of the run:
<div className="flex justify-center">
<Image
src="/static/logs/logs-frozen-canvas.png"
alt="Workflow Snapshot"
width={600}
height={400}
className="my-6"
/>
</div>
The snapshot provides:
- Frozen canvas showing the workflow structure
- Block states and connections as they were during the run
- Click any block to see its inputs and outputs
- Useful for debugging workflows that have since been modified
<Callout type="info">
Workflow snapshots are only available for runs after the enhanced logging system was introduced. Older migrated logs show a "Logged State Not Found" message.
</Callout>
## Log Retention
- **Free Plan**: 7 days of log retention (logs are archived to cloud storage and then deleted)
- **Pro / Team / Enterprise Plans**: Logs are retained indefinitely (no automatic cleanup)
## Best Practices
### For Development
- Use the real-time console for immediate feedback during testing
- Check block inputs and outputs to verify data flow
- Use workflow snapshots to compare working vs. broken versions
### For Production
- Monitor the Logs page regularly for errors or performance issues
- Set up filters to focus on specific workflows or time periods
- Use live mode during critical deployments to watch runs in real-time
### For Debugging
- Always check the run timeline to identify slow blocks
- Compare inputs between working and failing runs
- Use workflow snapshots to see the exact state when issues occurred
## Next Steps
- Learn about [Cost Calculation](/execution/costs) to understand workflow pricing
- Explore the [External API](/execution/api) for programmatic log access
- Add the Sim trigger block to a workflow to react to execution errors, successes, deployments, and alert conditions in real time
import { FAQ } from '@/components/ui/faq'
<FAQ items={[
{ question: "How long are run logs retained?", answer: "Free plans retain logs for 7 days — after that, logs are archived to cloud storage and deleted from the database. Pro, Team, and Enterprise plans retain logs indefinitely with no automatic cleanup." },
{ question: "What data is captured in each run log?", answer: "Each log entry includes the run ID, workflow ID, trigger type, start and end timestamps, total duration in milliseconds, cost breakdown (total cost, token counts, and per-model breakdowns), run data with trace spans, final output, and any associated files. The log details sidebar lets you inspect block-level inputs and outputs." },
{ question: "Are API keys visible in the logs?", answer: "No. API keys and credentials are automatically redacted in the log input tab for security. You can safely inspect block inputs without exposing sensitive values." },
{ question: "What is a workflow snapshot?", answer: "A workflow snapshot is a frozen copy of the workflow's structure (blocks, connections, and configuration) captured at the time of a run. It lets you see the exact state of the workflow when a particular run happened, which is useful for debugging workflows that have been modified since." },
{ question: "Can I access logs programmatically?", answer: "Yes. The External API provides endpoints to query logs with filtering by workflow, time range, trigger type, duration, cost, and model. To react to runs in real time, use the Sim trigger block to run a workflow on execution events." },
{ question: "What does Live mode do on the Logs page?", answer: "Live mode automatically refreshes the Logs page in real-time so new log entries appear as they are recorded, without requiring manual page refreshes. This is useful during deployments or when monitoring active workflows." },
]} />
@@ -1,3 +0,0 @@
{
"pages": ["index", "basics", "files", "api", "api-deployment", "chat", "logging", "costs"]
}
@@ -0,0 +1,68 @@
---
title: Generating files
description: How a workflow produces a document, report, or media file and saves it to the workspace Files store.
pageType: concept
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
A generated file is an artifact a workflow run creates: a report, a CSV, a rendered audio clip. It starts as a value a block produces and becomes a workspace file when a [File](/integrations/file) block writes it to the [Files](/files) store. Once saved, it has a name, a size, and a URL, and any later run can read it back.
Like a build pipeline that produces artifacts and stores them in an artifact repository, a workflow produces file artifacts that land in your workspace Files store, indexed by ID and shared across every workflow.
{/* VISUAL: flow diagram: [Agent/Function block produces content] → [File block, Write] → [Files panel, file visible] */}
## What produces file content
Most generated files start as the output of an earlier block. Two patterns are common.
A block returns **text content** you want to keep. An [Agent](/workflows/blocks/agent) writes an analysis or summary; a [Function](/workflows/blocks/function) builds a CSV or formats a report. That text is part of the block's output, read by reference as `<agent.content>` or `<function.result>`. To make it a file, you pass that value into a File block set to Write.
A block returns a **file object** directly. ElevenLabs text-to-speech returns an `audioFile`; an image generator returns a generated image; the File block's own Read operation returns parsed files. These are already [UserFile](/files/passing-files) objects (an object with `id`, `name`, `url`, `size`, and `type`), so they appear in the output panel as files with no Write step. You can hand one to a downstream block, or write its content to the Files store to persist it.
<Callout type="info">
A block's output is remembered under the block's name for the rest of the run, and a later block reads it by key, like `<elevenlabs.audioFile>` or `<agent.content>`. Producing content and saving it are two separate steps: the producing block holds the value, the File block persists it.
</Callout>
## Saving content with the File block
The [File](/integrations/file) block's **Write** operation creates a new workspace file. It takes two required fields: a `fileName` (like `report.md`) and the `content` string to store. It returns the saved file's details.
{/* VISUAL: File Write anatomy: inputs (fileName, content, contentType?) → outputs (id, name, size, url) */}
To save an Agent's analysis, connect Agent into a File block, set the operation to Write, and reference the Agent's output: `fileName` = `research_summary.md`, `content` = `<agent.content>`. When the workflow runs, the File block writes the file and produces:
- `id`: the canonical file ID, used to fetch the file later.
- `name`: the final file name after any deduplication (see below).
- `size`: the byte count.
- `url`: an absolute URL to download or preview the file.
The content type is detected from the file extension: `.csv` becomes `text/csv`, `.pdf` becomes `application/pdf`, `.md` becomes `text/markdown`. To set it yourself, fill in `contentType` in advanced mode.
If a file with the same name already exists in the workspace, Write does not overwrite it. It appends a numeric suffix instead: a second `data.csv` is saved as `data (1).csv`, a third as `data (2).csv`. To add to an existing file rather than create a new one, use the **Append** operation, which writes content to the end of a named file.
## Where the file goes
A saved file lands in the workspace [Files](/files) store, the same place uploads live. It shows up in the Files panel in the sidebar with its name, size, type icon, and modified date, grouped by category: document, image, audio, video, or code. From there you can preview it, rename it, move it into a folder, or download it by URL.
{/* VISUAL: Files panel UI: generated files listed with name, size, type icon, owner, modified date, folder */}
Files are scoped to the workspace, not to one workflow. A file written by one workflow is visible to every other workflow in the same workspace. Any later run can read it back with the File block's Read or Get operation, or by selecting it in a file picker.
During a run, the file also appears in the output panel as the File block's output, shown as a structured object with its `id`, `name`, `url`, and `size`. The output panel shows you one run as it happens, while the Files store is where the file stays afterward.
{/* VISUAL: output panel tree: file object (id, name, url, size, type) during a run, beside the same file in the Files store */}
## Returning a generated file from a deployment
When a workflow is deployed as an [API](/workflows/deployment/api), a generated file can be part of the response. Reference the file in a [Response](/workflows/blocks/response) block, or include its ID in the object you return. The caller uses the `url` or `id` to fetch the file from the workspace store. The file itself stays in the Files store, and the response carries a pointer to it, not the bytes.
## Next
<Cards>
<Card title="Files overview" href="/files" description="The artifact layer of your workspace." />
<Card title="Using files in workflows" href="/files/passing-files" description="Provide files to a workflow and receive files back." />
<Card title="File block" href="/files" description="Read, Get, Write, and Append operations in full." />
<Card title="API deployment" href="/workflows/deployment/api" description="Expose a workflow as an API and return file references." />
</Cards>
+25
View File
@@ -0,0 +1,25 @@
---
title: Overview
description: Files are the documents, images, spreadsheets, and PDFs your workflows read and produce.
pageType: concept
---
import { Card, Cards } from 'fumadocs-ui/components/card'
A **file** is a document, image, spreadsheet, or PDF in your workspace. Files are how documents and media move into and out of your agents. Your team uploads them, you create them in the editor, or a workflow produces them, and they all live in one store shared across the workspace.
Any [workflow](/workflows) can read a file or produce one. You might upload a contract for an agent to review, generate a report from a table, or hand a workflow the document it needs to answer a question.
## How files fit the workspace
- **[Workflows](/workflows)** read files, like a PDF to summarize, and produce them, like a rendered report. See [using files in workflows](/files/using-in-workflows).
- **[Knowledge bases](/knowledgebase)** are built from files you upload, turning their contents into searchable memory.
- **[Deployments](/workflows/deployment)** can take a file as input and return one as output.
Use a file when the document or media itself is what matters. Use a [table](/tables) when you need structured rows and fields, and a [knowledge base](/knowledgebase) when an agent needs to search across many documents.
<Cards>
<Card title="Using files in workflows" href="/files/using-in-workflows" description="Read a file into a workflow and produce one from a run." />
<Card title="Passing files" href="/files/passing-files" description="Provide files to a workflow and receive generated files back." />
<Card title="Knowledge bases" href="/knowledgebase" description="Make uploaded documents searchable for your agents." />
</Cards>
@@ -1,11 +1,13 @@
---
title: Passing Files
title: Passing files
description: How file objects move between blocks, into workflows via the API, and back out in responses.
pageType: guide
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
Sim makes it easy to work with files throughout your workflows. Blocks can receive files, process them, and pass them to other blocks seamlessly.
A file moves through a workflow as a standardized **file object**. Blocks receive it, act on it, and pass it on — this page covers the object's shape, how to reference it between blocks, and how files enter and leave through the API.
## File Objects
@@ -13,11 +15,12 @@ When blocks output files (like Gmail attachments, generated images, or parsed do
```json
{
"id": "f_8c2...",
"name": "report.pdf",
"url": "https://...",
"base64": "JVBERi0xLjQK...",
"size": 245678,
"type": "application/pdf",
"size": 245678
"base64": "JVBERi0xLjQK..."
}
```
@@ -25,7 +28,7 @@ You can access any of these properties when referencing files from previous bloc
## The File Block
The **File block** is the universal entry point for files in your workflows. It accepts files from any source and outputs standardized file objects that work with all integrations.
The **File block** brings a file into a workflow. It accepts files from any source and outputs standardized file objects every block understands.
**Inputs:**
- **Uploaded files** - Drag and drop or select files directly
@@ -33,8 +36,9 @@ The **File block** is the universal entry point for files in your workflows. It
- **Files from other blocks** - Pass files from Gmail attachments, Slack downloads, etc.
**Outputs:**
- A list of `UserFile` objects with consistent structure (`name`, `url`, `base64`, `type`, `size`)
- `combinedContent` - Extracted text content from all files (for documents)
- A list of `UserFile` objects with consistent structure (`id`, `name`, `url`, `size`, `type`, `base64`)
- `contents` - Extracted text per file (the Get Content operation)
- `combinedContent` - All fetched files' text merged into one string (the Fetch operation)
**Example usage:**
@@ -45,8 +49,8 @@ The **File block** is the universal entry point for files in your workflows. It
// Get the first file
<file.files[0]>
// Get combined text content from parsed documents
<file.combinedContent>
// Get the first file's extracted text (Get Content operation)
<file.contents[0]>
```
The File block automatically:
@@ -139,7 +143,7 @@ Use `url` for direct downloads or `base64` for inline processing.
**File inputs:**
- **File** - Parse documents, images, and text files
- **Vision** - Analyze images with AI models
- **Agent** - Read images with a vision-capable model, or documents as text
- **Mistral Parser** - Extract text from PDFs
**File outputs:**
@@ -163,7 +167,7 @@ Use `url` for direct downloads or `base64` for inline processing.
1. **Use file objects directly** - Pass the full file object rather than extracting individual properties. Blocks handle the conversion automatically.
2. **Check file types** - Ensure the file type matches what the receiving block expects. The Vision block needs images, the File block handles documents.
2. **Check file types** - Ensure the file type matches what the receiving block expects. An Agent using a vision-capable model can read images, while the File block handles documents.
3. **Consider file size** - Large files increase run time. For very large files, consider using storage blocks (S3, Supabase) for intermediate storage.
@@ -173,7 +177,6 @@ import { FAQ } from '@/components/ui/faq'
{ question: "What is the maximum file size for uploads?", answer: "The maximum file size for files processed during a workflow run is 20 MB. Files exceeding this limit will be rejected with an error indicating the actual file size. For larger files, use storage blocks like S3 or Supabase for intermediate storage." },
{ question: "What file input formats are supported via the API?", answer: "When triggering a workflow via API, you can send files as base64-encoded data (using a data URI with the format 'data:{mime};base64,{data}') or as a URL pointing to a publicly accessible file. In both cases, include the file name and MIME type in the request." },
{ question: "How are files passed between blocks internally?", answer: "Files are represented as standardized UserFile objects with name, url, base64, type, and size properties. Most blocks accept the full file object and extract what they need automatically, so you typically pass the entire object rather than individual properties." },
{ question: "Which blocks can output files?", answer: "Gmail outputs email attachments, Slack outputs downloaded files, TTS generates audio files, Video Generator and Image Generator produce media files. Storage blocks like S3, Supabase, Google Drive, and Dropbox can also retrieve files for use in downstream blocks." },
{ question: "Do I need to extract base64 or URL from file objects manually?", answer: "No. Most blocks accept the full file object and handle the format conversion automatically. Simply pass the entire file reference (e.g., <gmail.attachments[0]>) and the receiving block will extract the data it needs." },
{ question: "How do file fields work in the Start block's input format?", answer: "When you define a field with type 'file[]' in the Start block's input format, the engine automatically processes incoming file data (base64 or URL) and uploads it to storage, converting it into UserFile objects before the workflow runs." },
]} />
@@ -0,0 +1,110 @@
---
title: Using files in workflows
description: Read a file's contents in a workflow, pass a file to a block that needs one, or save a new file from a run.
pageType: concept
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { WorkflowPreview, FILE_SUMMARY_WORKFLOW } from '@/components/workflow-preview'
A file is a document, image, spreadsheet, or PDF in your workspace. A workflow can read a file to act on its contents, pass a file to a block or tool that needs one (attach a PDF to an email, send an image to a vision model), or produce a new file and save it. The [File](/integrations/file) block is how a file enters or leaves a workflow; the work in between is done by whatever block the task calls for.
What you do with a file depends on the task, so this page covers the File block's operations and how a file moves between blocks rather than one fixed recipe. The example we'll use throughout reads `report.pdf`, asks an agent to summarize it, and saves the summary as `summary.md`, which exercises reading, processing, and writing in one workflow.
<WorkflowPreview workflow={FILE_SUMMARY_WORKFLOW} height={200} />
## The File block
The **File** block is one block with five operations, chosen from a dropdown. Each operation is a different way a file, or its contents, enters or leaves the workflow.
| Operation | What it does | Outputs |
| --- | --- | --- |
| **Read** | Take an existing workspace file, by file picker or by file ID. | `files` |
| **Get Content** | Extract a workspace file's text, by file picker or by file ID. | `contents` |
| **Fetch** | Download and parse a file from an external URL. | `files`, `combinedContent` |
| **Write** | Create a new workspace file from a name and text content. | `id`, `name`, `size`, `url` |
| **Append** | Add text to the end of an existing workspace file. | `id`, `name`, `size`, `url` |
Read hands the next block the **file itself**; Get Content hands it the **text inside**. Fetch brings in both for an external URL. Write and Append put a file out. A workflow uses only the operations its task needs, often just Read. The two sections below cover Read and Write; the other operations are variants noted alongside.
{/* VISUAL: File block config UI. Operation dropdown open showing Read / Get Content / Fetch / Write / Append */}
## Reading a file in
In our example, the first File block is set to **Read**, with `report.pdf` chosen from the file picker. When it runs, it produces `files`: a list of **file objects**, one per file read. The first is `<file1.files[0]>`.
When the next step needs the file's *text* rather than the file itself, use **Get Content** instead: it extracts the text and outputs `contents`, an array with one string per file, read as `<file1.contents[0]>`.
A **file object** is the standard shape Sim uses for every file. It carries the file's details:
```jsonc
{
"id": "wf_V1StGXR8z5jdHi6B…", // workspace file ID
"name": "report.pdf",
"url": "https://…", // where to access it
"size": 248120, // bytes
"type": "application/pdf"
}
```
You rarely type a reference by hand. Wherever a block parameter accepts a file or a value, the builder lists the available outputs and you pick the one you want. Read mode also takes a file ID directly in advanced mode, which is how you read a file produced earlier in the same run.
<Callout type="info">
**Fetch** brings in files that live outside the workspace. Point it at a URL, and add request headers (such as `Authorization: Bearer …`) when the download needs authentication. It outputs `files` like Read does, plus `combinedContent`: the fetched files' text merged into one string.
</Callout>
{/* VISUAL: File block in Read mode, file picker open with report.pdf selected; callout on the files[] output */}
## Processing the file
Once a file is read, a processing block reads that output by name. Two blocks do this, and they consume the file differently.
### Agent block
An [Agent](/workflows/blocks/agent) block has a **Files** input. Reference the read file there, `<file1.files[0]>`, and write the instruction in the prompt: "Summarize this document." The agent receives the file object, not just its text, so a **vision-capable model** can analyze images and scanned pages directly. Other models work from the file's text content.
In our example the Agent reads `<file1.files[0]>`, summarizes it, and keeps the summary under its own name as `<agent1.content>`.
{/* VISUAL: Agent block config. Files input bound to <file1.files[0]>, prompt "Summarize this document" */}
### Function block
A [Function](/workflows/blocks/function) block runs code, and it usually works with file **text**. Read the file with **Get Content** and pass the extracted text, `<file1.contents[0]>`, and the code can parse, filter, or reshape it and return the result as its own output. A Function can also take the file object itself and read it in code with the `sim.files` helpers, like `await sim.files.readText(file)` — see [the Function block](/workflows/blocks/function) for those. Use a Function when the file is structured text (a CSV or JSON dump) and you want exact, deterministic processing instead of a model's interpretation.
<Callout type="info">
The two blocks differ in what they take. An **Agent** takes the file object on its Files input, while a **Function** typically takes the file's text from Get Content's `contents`. Both store their result under their own name for the next block to read.
</Callout>
## Writing a file out
Writing a file is optional, and many workflows skip it. The agent's summary could be returned in the response, posted to Slack, or emailed as-is without ever becoming a workspace file. Write a file when you specifically need a new one to keep, download, or hand to a later run.
The last block in our example is a File block set to **Write**. Give it a file name and the content to save:
- `fileName`: `summary.md`
- `content`: `<agent1.content>`
Write creates a new workspace file and returns its `id`, `name`, `size`, and `url`. If a file with that name already exists, Write keeps both by adding a numeric suffix to the new one. The saved file lands in your workspace [files](/files), ready for the next run, a download, or another workflow.
{/* VISUAL: run log showing the File Write step with the returned file id, name, and url */}
<Callout type="info">
**Append** adds to a file instead of replacing it. Target an existing workspace file by name and give it the content to add to the end. Use it to accumulate across runs, such as appending each run's observation to one `notes.md`.
</Callout>
## Composing the steps
Each block references the previous one by name, so you compose only the steps a task needs. A contract read by an Agent that returns a verdict stops at processing, and an uploaded image described by a vision model never touches Write, while a fetched CSV cleaned by a Function and saved as a new file uses all three. Reading a file in is common, and writing one out is only for when the result is itself a file.
For the file-object schema in full, base64 access, and how files move across API and chat triggers, see [Passing files](/files/passing-files).
## Next
<Cards>
<Card title="Passing files" href="/files/passing-files" description="The file object in full: properties, base64, and references across blocks." />
<Card title="Generating & storing files" href="/files/generating" description="Produce files from a run and save them to your workspace." />
<Card title="File block reference" href="/integrations/file" description="Every operation, input, and output in detail." />
<Card title="Agent block" href="/workflows/blocks/agent" description="Give an agent files to read, summarize, or analyze." />
<Card title="Using tables in workflows" href="/tables/using-in-workflows" description="Read and write structured rows the same way you read and write files." />
</Cards>
@@ -1,203 +1,106 @@
---
title: Getting Started
description: Build your first agent in 10 minutes — a people researcher with web search tools and structured output.
pageType: guide
---
import { Callout } from 'fumadocs-ui/components/callout'
import { Card, Cards } from 'fumadocs-ui/components/card'
import { File, Files, Folder } from 'fumadocs-ui/components/files'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
import {
AgentIcon,
ApiIcon,
ChartBarIcon,
CodeIcon,
ConditionalIcon,
ConnectIcon,
ExaAIIcon,
FirecrawlIcon,
GmailIcon,
NotionIcon,
PerplexityIcon,
SlackIcon,
} from '@/components/icons'
import { Video } from '@/components/ui/video'
import { Image } from '@/components/ui/image'
import { FAQ } from '@/components/ui/faq'
Build your first AI workflow in 10 minutes. In this tutorial, you'll create a people research agent that uses advanced LLM-powered search tools to extract and structure information about individuals.
## What You'll Build
A people research agent that:
1. Accepts user input through a chat interface
2. Searches the web using AI-powered tools (Exa and Linkup)
3. Extracts and structures information about individuals
4. Returns formatted JSON data with location, profession, and education
Build a people research agent in 10 minutes. It takes a name through a chat interface, searches the web with AI search tools, and returns structured JSON with the person's location, profession, and education.
<Image
src="/static/getting-started/started-1.png"
alt="Getting Started Example"
alt="The finished people research workflow"
width={800}
height={500}
/>
## Step-by-Step Tutorial
## Tutorial
<Steps>
<Step title="Create a workflow and add an AI agent">
Click **New Workflow** in the dashboard and name it "Getting Started".
Every new workflow includes a **Start block** by default—this is the entry point that receives user input. Since we'll trigger this workflow via chat, no configuration is needed for the Start block.
Drag an **Agent Block** onto the canvas from the left panel and configure it:
- **Model**: Select "OpenAI GPT-4o"
- **System Prompt**: "You are a people research agent. When given a person's name, use your available search tools to find comprehensive information about them including their location, profession, educational background, and other relevant details."
- **User Prompt**: Drag the connection from the Start block's output into this field to connect `<start.input>` to the user prompt
<Step title="Create a workflow and add an Agent">
In the sidebar, click the **+** next to **Workflows** and name the new workflow "Getting Started". The **Start block** is already on the canvas — it's the entry point that receives your chat input.
Drag an **Agent block** onto the canvas and configure its **Messages**:
- **System**: "You are a people research agent. When given a person's name, use your search tools to find their location, profession, educational background, and other relevant details."
- **User**: insert `<start.input>` so the agent reads whatever the chat receives.
Leave the **Model** on the default (`claude-sonnet-4-6`), or pick any other.
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="getting-started/started-2.mp4" width={700} height={450} />
</div>
</Step>
<Step title="Add search tools to the agent">
Enhance your agent with web search capabilities. Click on the Agent block to select it.
In the **Tools** section:
- Click **Add Tool**
- Select **Exa** and **Linkup** from the available tools
- Provide your API keys for both tools to enable web search and data access
<Step title="Add search tools">
In the Agent block's **Tools** section, add **Exa** and **Linkup**. The agent decides when to call them.
<div className="mx-auto w-3/5 overflow-hidden rounded-lg">
<Video src="getting-started/started-3.mp4" width={700} height={450} />
</div>
</Step>
<Step title="Test your workflow">
Test your workflow using the **Chat panel** on the right side of the screen.
In the chat panel:
- Click the dropdown and select `agent1.content` to view the agent's output
- Enter a test message: "John is a software engineer from San Francisco who studied Computer Science at Stanford University."
- Click **Send** to execute the workflow
The agent will analyze the person and return structured information.
<Step title="Test it">
Open the **Chat panel**, select `agent1.content` as the output to display, and send: *"John is a software engineer from San Francisco who studied Computer Science at Stanford University."*
The agent searches, then returns what it found.
{/* Why this callout exists: the largest funnel drop in platform data (2026-06) is created-a-workflow -> first-successful-run, 92% -> 49% — this is the stall point it targets. */}
<Callout type="info">
If the run doesn't go green, open the output dropdown and read what each block returned — the failure is almost always visible there. The two most common causes are a missing API key on a tool and a reference pointing at a value that isn't there. For the full method, see [tracing a failure backward](/logs-debugging).
</Callout>
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="getting-started/started-4.mp4" width={700} height={450} />
</div>
</Step>
<Step title="Configure structured output">
Configure your agent to return structured JSON data. Click on the Agent block to select it.
In the **Response Format** section:
- Click the **magic wand icon** (✨) next to the schema field
- Enter the prompt: "create a schema named person, that contains location, profession, and education"
- The AI will automatically generate the JSON schema
<Step title="Add structured output">
In the Agent block's **Response Format**, click the magic wand (✨) and prompt: *"create a schema named person, that contains location, profession, and education"*. The schema is generated for you.
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="getting-started/started-5.mp4" width={700} height={450} />
</div>
</Step>
<Step title="Test with structured output">
Return to the **Chat panel** to test the structured response format.
With the response format configured, new output options are now available:
- Click the dropdown and select the structured output option (the schema you just created)
- Enter a test message: "Sarah is a marketing manager from New York who has an MBA from Harvard Business School."
- Click **Send** to execute the workflow
The agent will now return structured JSON output with the person's information organized into location, profession, and education fields.
<Step title="Test the structured output">
Back in the **Chat panel**, select the new structured output option and send: *"Sarah is a marketing manager from New York who has an MBA from Harvard Business School."*
The agent now returns JSON with `location`, `profession`, and `education` fields — ready for any downstream block to read by name.
<div className="mx-auto w-full overflow-hidden rounded-lg">
<Video src="getting-started/started-6.mp4" width={700} height={450} />
</div>
</Step>
</Steps>
## What You've Built
You've successfully created an AI workflow that:
- ✅ Accepts user input through a chat interface
- ✅ Processes unstructured text using AI
- ✅ Integrates external search tools (Exa and Linkup)
- ✅ Returns structured JSON data with AI-generated schemas
- ✅ Demonstrates real-time testing and iteration
- ✅ Showcases the power of visual, no-code development
## Key Concepts You Learned
### Block Types Used
<Files>
<File
name="Start Block"
icon={<ConnectIcon className="h-4 w-4" />}
annotation="Entry point for user input (auto-included)"
/>
<File
name="Agent Block"
icon={<AgentIcon className="h-4 w-4" />}
annotation="AI model for text processing and analysis"
/>
</Files>
### Core Workflow Concepts
**Data Flow**
Connect blocks by dragging connections to pass data between workflow steps
**Chat Interface**
Test workflows in real-time with the chat panel and select different output options
**Tool Integration**
Extend agent capabilities by integrating external services like Exa and Linkup
**Variable References**
Access block outputs using the `<blockName.output>` syntax
**Structured Output**
Define JSON schemas to ensure consistent, formatted responses from AI
**AI-Generated Schemas**
Use the magic wand (✨) to generate schemas from natural language prompts
**Iterative Development**
Build, test, and refine workflows quickly with immediate feedback
## Next Steps
## Next
<Cards>
<Card title="Explore Blocks" href="/blocks">
Discover API, Function, Condition, and other blocks
<Card title="Workflows" href="/workflows">
The full model: blocks, connections, triggers, and how runs execute.
</Card>
<Card title="Browse Integrations" href="/tools">
Connect 1,000+ services including Gmail, Slack, Notion, and more
<Card title="Agent block" href="/workflows/blocks/agent">
Everything the block you just used can do — tools, memory, skills, structured output.
</Card>
<Card title="Add Custom Logic" href="/blocks/function">
Write custom functions for advanced data processing
<Card title="Integrations" href="/integrations">
1,000+ services your agents can act on, from Gmail to HubSpot.
</Card>
<Card title="Deploy Your Agent" href="/execution">
Make your agent accessible via REST API or webhooks
<Card title="Deploy it" href="/workflows/deployment">
Ship the workflow as an API, a chat, or an MCP server.
</Card>
</Cards>
## Resources
**Need detailed explanations?** Visit the [Blocks documentation](/blocks) for comprehensive guides on each component.
**Looking for integrations?** Explore the [Tools documentation](/tools) to see all 1,000+ available integrations.
**Ready to go live?** Learn about [Execution and Deployment](/execution) to make your workflows production-ready.
<FAQ items={[
{ question: "How long does the getting started tutorial take?", answer: "About 10 minutes. The tutorial walks you through creating a people research agent with web search tools and structured output. You will have a fully working workflow by the end." },
{ question: "Do I need API keys to follow this tutorial?", answer: "You need API keys for the search tools (Exa and Linkup) used in this tutorial. For the AI model, you can either use Sim's hosted keys (included with your plan credits) or bring your own OpenAI API key. If you prefer not to set up search tool keys, you can still build a basic agent workflow without them." },
{ question: "Do I need coding experience to complete this tutorial?", answer: "No. The entire tutorial uses the visual drag-and-drop interface. You will connect blocks, configure settings, and test through the chat panel without writing any code." },
{ question: "Can I use a different AI model instead of GPT-4o?", answer: "Yes. The Agent block supports models from OpenAI, Anthropic, Google, Groq, Cerebras, DeepSeek, Mistral, xAI, and more. You can select any available model from the dropdown. If you self-host, you can also use local models through Ollama." },
{ question: "Can I import workflows from other tools?", answer: "Sim does not currently support importing workflows from other automation platforms. However, you can use the Copilot feature to describe what you want in natural language and have it build the workflow for you, which is often faster than manual recreation." },
{ question: "What if my workflow does not produce the expected output?", answer: "Use the Chat panel to test iteratively and inspect outputs from each block. You can click the dropdown to view different block outputs and pinpoint where the issue is. The execution logs (accessible from the Logs tab) show detailed information about each step including token usage, costs, and any errors." },
{ question: "Where do I go after completing this tutorial?", answer: "Explore the Blocks documentation to learn about Condition, Router, Function, and API blocks. Browse the Tools section to discover 1,000+ integrations you can add to your agents. When you are ready to deploy, check the Execution docs for REST API, webhook, and scheduled trigger options." },
{ question: "How long does the getting started tutorial take?", answer: "About 10 minutes. You will have a fully working workflow by the end." },
{ question: "Do I need API keys to follow this tutorial?", answer: "No. On hosted Sim, the model and the search tools run on Sim's hosted keys, included with your plan credits. You can bring your own provider keys if you prefer." },
{ question: "Do I need coding experience to complete this tutorial?", answer: "No. The whole tutorial uses the visual builder and the chat panel — no code." },
{ question: "Can I use a different model?", answer: "Yes. The Agent block supports models from OpenAI, Anthropic, Google, Groq, Cerebras, DeepSeek, xAI, and more, plus local models through Ollama if you self-host." },
{ question: "Can I import workflows from other tools?", answer: "Not directly. But you can describe what you want to Mothership in natural language and have it build the workflow for you, which is usually faster than manual recreation." },
{ question: "What if my workflow does not produce the expected output?", answer: "Test iteratively in the Chat panel and inspect each block's output from the dropdown to pinpoint where things go wrong. The Logs page records every run, block by block, with inputs, outputs, timing, and cost." },
]} />
+8 -8
View File
@@ -17,10 +17,10 @@ Welcome to Sim, the open-source AI workspace where teams build, deploy, and mana
<Card title="Getting Started" href="/getting-started">
Build your first agent in 10 minutes
</Card>
<Card title="Blocks" href="/blocks">
<Card title="Blocks" href="/workflows#blocks">
Learn about the building blocks
</Card>
<Card title="Tools & Integrations" href="/tools">
<Card title="Tools & Integrations" href="/integrations">
Explore 1,000+ integrations
</Card>
</Cards>
@@ -28,16 +28,16 @@ Welcome to Sim, the open-source AI workspace where teams build, deploy, and mana
## Core Concepts
<Cards>
<Card title="Connections" href="/connections">
<Card title="Connections" href="/workflows/connections">
Understand how data flows between blocks
</Card>
<Card title="Variables" href="/variables">
<Card title="Variables" href="/workflows/variables">
Work with workflow and environment variables
</Card>
<Card title="Execution" href="/execution">
<Card title="Execution" href="/workflows">
Monitor agent runs and manage costs
</Card>
<Card title="Triggers" href="/triggers">
<Card title="Triggers" href="/workflows#triggers">
Start agents via API, webhooks, or schedules
</Card>
</Cards>
@@ -45,10 +45,10 @@ Welcome to Sim, the open-source AI workspace where teams build, deploy, and mana
## Advanced Features
<Cards>
<Card title="Team Management" href="/permissions/roles-and-permissions">
<Card title="Team Management" href="/platform/permissions">
Set up workspace roles and permissions
</Card>
<Card title="MCP Integration" href="/mcp">
<Card title="MCP Integration" href="/agents/mcp">
Connect external services with Model Context Protocol
</Card>
<Card title="SDKs" href="/api-reference">
@@ -41,7 +41,7 @@ Integrate AgentMail into your workflow. Create and manage email inboxes, send an
## Tools
## Actions
### `agentmail_create_draft`
@@ -41,7 +41,7 @@ Give your workflow a phone. Provision SMS- and voice-enabled numbers, send messa
## Tools
## Actions
### `agentphone_create_call`
@@ -38,7 +38,7 @@ Integrate with Agiloft contract lifecycle management to create, read, update, de
## Tools
## Actions
### `agiloft_attach_file`
@@ -31,7 +31,7 @@ Integrate Ahrefs SEO tools into your workflow. Analyze domain ratings, backlinks
## Tools
## Actions
### `ahrefs_domain_rating`
@@ -30,7 +30,7 @@ Integrates Airtable into the workflow. Can list bases, list tables (with schema)
## Tools
## Actions
### `airtable_list_bases`
@@ -221,3 +221,52 @@ Get the schema of all tables, fields, and views in an Airtable base
| `metadata` | json | Operation metadata including total tables count |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Airtable Webhook
Trigger workflow from Airtable record changes like create, update, and delete events (requires Airtable credentials)
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | This trigger requires airtable credentials to access your account. |
| `baseId` | string | Yes | The ID of the Airtable Base this webhook will monitor. |
| `tableId` | string | Yes | The ID of the table within the Base that the webhook will monitor. |
| `includeCellValues` | boolean | No | Enable to receive the complete record data in the payload, not just changes. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `payloads` | array | The payloads of the Airtable changes |
| ↳ `timestamp` | string | Timestamp of the change |
| ↳ `baseTransactionNumber` | number | Transaction number |
| `latestPayload` | object | The most recent payload from Airtable |
| ↳ `timestamp` | string | ISO 8601 timestamp of the change |
| ↳ `baseTransactionNumber` | number | Transaction number |
| ↳ `payloadFormat` | string | Payload format version \(e.g., v0\) |
| ↳ `actionMetadata` | object | Metadata about who made the change |
| ↳ `source` | string | Source of the change \(e.g., client, publicApi\) |
| ↳ `sourceMetadata` | object | Source metadata including user info |
| ↳ `user` | object | User who made the change |
| ↳ `id` | string | User ID |
| ↳ `email` | string | User email |
| ↳ `name` | string | User name |
| ↳ `permissionLevel` | string | User permission level |
| ↳ `changedTablesById` | object | Tables that were changed \(keyed by table ID\) |
| ↳ `changedRecordsById` | object | Changed records keyed by record ID |
| ↳ `current` | object | Current state of the record |
| ↳ `cellValuesByFieldId` | object | Cell values keyed by field ID |
| ↳ `createdRecordsById` | object | Created records by ID |
| ↳ `destroyedRecordIds` | array | Array of destroyed record IDs |
| `airtableChanges` | array | Changes made to the Airtable table |
| ↳ `tableId` | string | Table ID |
| ↳ `recordId` | string | Record ID |
| ↳ `changeType` | string | Type of change \(created, changed, destroyed\) |
| ↳ `cellValuesByFieldId` | object | Cell values by field ID |
@@ -32,7 +32,7 @@ Search across your synced data sources using Airweave. Supports semantic search
## Tools
## Actions
### `airweave_search`
@@ -32,7 +32,7 @@ Integrate Algolia into your workflow. Search indices, manage records (add, updat
## Tools
## Actions
### `algolia_search`
@@ -35,7 +35,7 @@ Integrate Amplitude into your workflow to track events, identify users and group
## Tools
## Actions
### `amplitude_send_event`
@@ -34,7 +34,7 @@ Integrate Apify into your workflow. Run any Apify actor or saved task with custo
## Tools
## Actions
### `apify_run_actor_sync`
@@ -37,7 +37,7 @@ Integrates Apollo.io into the workflow. Search for people and companies, enrich
## Tools
## Actions
### `apollo_people_search`
@@ -34,7 +34,7 @@ Integrate AWS AppConfig into workflows. Manage applications, environments, and c
## Tools
## Actions
### `appconfig_get_configuration`
@@ -31,7 +31,7 @@ Integrates ArXiv into the workflow. Can search for papers, get paper details, an
## Tools
## Actions
### `arxiv_search`
@@ -31,7 +31,7 @@ Integrate Asana into the workflow. Can read, write, and update tasks.
## Tools
## Actions
### `asana_get_task`
@@ -34,7 +34,7 @@ Integrate Ashby into the workflow. Manage candidates (list, get, create, update,
## Tools
## Actions
### `ashby_add_candidate_tag`
@@ -1078,3 +1078,185 @@ Updates an existing candidate record in Ashby. Only provided fields are changed.
| `syncToken` | string | Sync token for incremental updates |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Ashby Application Submitted
Trigger workflow when a new application is submitted
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
| `application` | object | application output from the tool |
| ↳ `id` | string | Application UUID |
| ↳ `createdAt` | string | Application creation timestamp \(ISO 8601\) |
| ↳ `updatedAt` | string | Application last update timestamp \(ISO 8601\) |
| ↳ `status` | string | Application status \(Active, Hired, Archived, Lead\) |
| ↳ `candidate` | object | candidate output from the tool |
| ↳ `id` | string | Candidate UUID |
| ↳ `name` | string | Candidate name |
| ↳ `currentInterviewStage` | object | currentInterviewStage output from the tool |
| ↳ `id` | string | Current interview stage UUID |
| ↳ `title` | string | Current interview stage title |
| ↳ `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
---
### Ashby Candidate Deleted
Trigger workflow when a candidate is deleted
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
| `candidate` | object | candidate output from the tool |
| ↳ `id` | string | Deleted candidate UUID |
---
### Ashby Candidate Hired
Trigger workflow when a candidate is hired
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
| `application` | object | application output from the tool |
| ↳ `id` | string | Application UUID |
| ↳ `createdAt` | string | Application creation timestamp \(ISO 8601\) |
| ↳ `updatedAt` | string | Application last update timestamp \(ISO 8601\) |
| ↳ `status` | string | Application status \(Hired\) |
| ↳ `candidate` | object | candidate output from the tool |
| ↳ `id` | string | Candidate UUID |
| ↳ `name` | string | Candidate name |
| ↳ `currentInterviewStage` | object | currentInterviewStage output from the tool |
| ↳ `id` | string | Current interview stage UUID |
| ↳ `title` | string | Current interview stage title |
| ↳ `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
| `offer` | object | offer output from the tool |
| ↳ `id` | string | Accepted offer UUID |
| ↳ `applicationId` | string | Associated application UUID |
| ↳ `acceptanceStatus` | string | Offer acceptance status |
| ↳ `offerStatus` | string | Offer process status |
| ↳ `decidedAt` | string | Offer decision timestamp \(ISO 8601\) |
| ↳ `latestVersion` | object | latestVersion output from the tool |
| ↳ `id` | string | Latest offer version UUID |
---
### Ashby Candidate Stage Change
Trigger workflow when a candidate changes interview stages
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
| `application` | object | application output from the tool |
| ↳ `id` | string | Application UUID |
| ↳ `createdAt` | string | Application creation timestamp \(ISO 8601\) |
| ↳ `updatedAt` | string | Application last update timestamp \(ISO 8601\) |
| ↳ `status` | string | Application status \(Active, Hired, Archived, Lead\) |
| ↳ `candidate` | object | candidate output from the tool |
| ↳ `id` | string | Candidate UUID |
| ↳ `name` | string | Candidate name |
| ↳ `currentInterviewStage` | object | currentInterviewStage output from the tool |
| ↳ `id` | string | Current interview stage UUID |
| ↳ `title` | string | Current interview stage title |
| ↳ `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
---
### Ashby Job Created
Trigger workflow when a new job is created
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
| `job` | object | job output from the tool |
| ↳ `id` | string | Job UUID |
| ↳ `title` | string | Job title |
| ↳ `confidential` | boolean | Whether the job is confidential |
| ↳ `status` | string | Job status \(Open, Closed, Draft, Archived\) |
| ↳ `employmentType` | string | Employment type \(Full-time, Part-time, etc.\) |
---
### Ashby Offer Created
Trigger workflow when a new offer is created
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | API Key |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `action` | string | The webhook event type \(e.g., applicationSubmit, candidateHire\) |
| `offer` | object | offer output from the tool |
| ↳ `id` | string | Offer UUID |
| ↳ `applicationId` | string | Associated application UUID |
| ↳ `acceptanceStatus` | string | Offer acceptance status \(Accepted, Declined, Pending, Created, Cancelled, WaitingOnResponse\) |
| ↳ `offerStatus` | string | Offer process status \(WaitingOnApprovalStart, WaitingOnOfferApproval, WaitingOnCandidateResponse, CandidateAccepted, CandidateRejected, OfferCancelled\) |
| ↳ `decidedAt` | string | Offer decision timestamp \(ISO 8601\). Typically null at creation; populated after candidate responds. |
| ↳ `latestVersion` | object | latestVersion output from the tool |
| ↳ `id` | string | Latest offer version UUID |
@@ -42,7 +42,7 @@ Integrate AWS Athena into workflows. Execute SQL queries against data in S3, che
## Tools
## Actions
### `athena_start_query`
@@ -31,7 +31,7 @@ Connect to Attio to manage CRM records (people, companies, custom objects), note
## Tools
## Actions
### `attio_list_records`
@@ -1073,3 +1073,505 @@ Delete a webhook from Attio
| `deleted` | boolean | Whether the webhook was deleted |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Attio Comment Created
Trigger workflow when a new comment is created in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `threadId` | string | The thread ID |
| `commentId` | string | The comment ID |
| `objectId` | string | The object type ID |
| `recordId` | string | The record ID |
| `listId` | string | The list ID \(if comment is on a list entry\) |
| `entryId` | string | The list entry ID \(if comment is on a list entry\) |
---
### Attio Comment Deleted
Trigger workflow when a comment is deleted in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `threadId` | string | The thread ID |
| `commentId` | string | The comment ID |
| `objectId` | string | The object type ID |
| `recordId` | string | The record ID |
| `listId` | string | The list ID \(if comment is on a list entry\) |
| `entryId` | string | The list entry ID \(if comment is on a list entry\) |
---
### Attio Comment Resolved
Trigger workflow when a comment thread is resolved in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `threadId` | string | The thread ID |
| `commentId` | string | The comment ID |
| `objectId` | string | The object type ID |
| `recordId` | string | The record ID |
| `listId` | string | The list ID \(if comment is on a list entry\) |
| `entryId` | string | The list entry ID \(if comment is on a list entry\) |
---
### Attio Comment Unresolved
Trigger workflow when a comment thread is unresolved in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `threadId` | string | The thread ID |
| `commentId` | string | The comment ID |
| `objectId` | string | The object type ID |
| `recordId` | string | The record ID |
| `listId` | string | The list ID \(if comment is on a list entry\) |
| `entryId` | string | The list entry ID \(if comment is on a list entry\) |
---
### Attio List Created
Trigger workflow when a list is created in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `listId` | string | The list ID |
---
### Attio List Deleted
Trigger workflow when a list is deleted in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `listId` | string | The list ID |
---
### Attio List Entry Created
Trigger workflow when a new list entry is created in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `listId` | string | The list ID |
| `entryId` | string | The list entry ID |
---
### Attio List Entry Deleted
Trigger workflow when a list entry is deleted in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `listId` | string | The list ID |
| `entryId` | string | The list entry ID |
---
### Attio List Entry Updated
Trigger workflow when a list entry is updated in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `listId` | string | The list ID |
| `entryId` | string | The list entry ID |
| `attributeId` | string | The ID of the attribute that was updated on the list entry |
---
### Attio List Updated
Trigger workflow when a list is updated in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `listId` | string | The list ID |
---
### Attio Note Created
Trigger workflow when a new note is created in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `noteId` | string | The note ID |
| `parentObjectId` | string | The parent object type ID |
| `parentRecordId` | string | The parent record ID |
---
### Attio Note Deleted
Trigger workflow when a note is deleted in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `noteId` | string | The note ID |
| `parentObjectId` | string | The parent object type ID |
| `parentRecordId` | string | The parent record ID |
---
### Attio Note Updated
Trigger workflow when a note is updated in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `noteId` | string | The note ID |
| `parentObjectId` | string | The parent object type ID |
| `parentRecordId` | string | The parent record ID |
---
### Attio Record Created
Trigger workflow when a new record is created in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `objectId` | string | The object type ID \(e.g. people, companies\) |
| `recordId` | string | The record ID |
---
### Attio Record Deleted
Trigger workflow when a record is deleted in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `objectId` | string | The object type ID \(e.g. people, companies\) |
| `recordId` | string | The record ID |
---
### Attio Record Merged
Trigger workflow when two records are merged in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `objectId` | string | The object type ID of the surviving record |
| `recordId` | string | The surviving record ID after merge |
| `duplicateObjectId` | string | The object type ID of the merged-away record |
| `duplicateRecordId` | string | The record ID that was merged away |
---
### Attio Record Updated
Trigger workflow when a record is updated in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `objectId` | string | The object type ID \(e.g. people, companies\) |
| `recordId` | string | The record ID |
| `attributeId` | string | The ID of the attribute that was updated on the record |
---
### Attio Task Created
Trigger workflow when a new task is created in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `taskId` | string | The task ID |
---
### Attio Task Deleted
Trigger workflow when a task is deleted in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `taskId` | string | The task ID |
---
### Attio Task Updated
Trigger workflow when a task is updated in Attio
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `taskId` | string | The task ID |
---
### Attio Webhook (All Events)
Trigger workflow on any Attio webhook event
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `id` | json | The event ID object containing resource identifiers |
| `parentObjectId` | string | The parent object type ID \(if applicable\) |
| `parentRecordId` | string | The parent record ID \(if applicable\) |
---
### Attio Workspace Member Created
Trigger workflow when a new member is added to the Attio workspace
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `triggerCredentials` | string | Yes | Attio Account |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | The type of event \(e.g. record.created, note.created\) |
| `workspaceId` | string | The workspace ID |
| `workspaceMemberId` | string | The workspace member ID |
@@ -33,7 +33,7 @@ Integrate Azure DevOps into your workflow. List and inspect pipelines and builds
## Tools
## Actions
### `azure_devops_list_pipelines`
@@ -554,3 +554,75 @@ List comments for an Azure DevOps work item.
| ↳ `url` | string | API URL for the comment |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Azure DevOps Build Failed
Trigger workflow when an Azure DevOps build fails, is canceled, or partially succeeds
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `buildId` | number | Build ID |
| `buildNumber` | string | Build number string \(e.g. 20240101.1\) |
| `result` | string | Build result: failed \| canceled \| partiallySucceeded |
| `pipelineId` | number | Pipeline definition ID |
| `pipelineName` | string | Pipeline definition name |
| `projectName` | string | Azure DevOps project name |
| `branch` | string | Source branch name \(refs/heads/ prefix stripped\) |
| `commitSha` | string | Source commit SHA |
| `triggeredBy` | string | Display name of the person who triggered the build |
| `triggeredByEmail` | string | Email/unique name of the person who triggered the build, or null if not set |
| `startTime` | string | Build start time \(ISO 8601\) |
| `finishTime` | string | Build finish time \(ISO 8601\) |
| `buildUrl` | string | API URL for the build resource |
---
### Azure DevOps Webhook (All Service Hook Events)
Trigger on whichever service hook event types you configure in Azure DevOps. Sim does not filter deliveries for this trigger.
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `eventType` | string | Service hook event type \(e.g. build.complete, workitem.created\) |
| `notificationId` | number | Notification ID |
| `subscriptionId` | string | Service hook subscription ID |
| `publisherId` | string | Publisher ID \(e.g. tfs\) |
| `createdDate` | string | Event creation time \(ISO 8601\) |
| `resource` | json | Event resource payload |
| `resourceContainers` | json | Resource container references \(project, collection, etc.\) |
| `message` | json | Short message object |
| `detailedMessage` | json | Detailed message object |
---
### Azure DevOps Work Item Created
Trigger workflow when a work item is created in Azure DevOps
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `workItemId` | number | Work item ID |
| `workItemType` | string | Work item type for Basic process \(e.g. Issue, Task, Epic\) |
| `title` | string | Work item title |
| `state` | string | Work item state for Basic process \(e.g. To Do, Doing, Done\) |
| `createdBy` | string | Display name of the creator, or null if not set |
| `assignedTo` | string | Assignee display name, or null if unassigned |
| `priority` | number | Priority \(1–4\), or 0 if not set |
| `areaPath` | string | Area path |
| `iterationPath` | string | Iteration path |
| `description` | string | Work item description \(HTML\), or null if not set |
| `projectName` | string | Azure DevOps project name |
| `workItemUrl` | string | API URL for the work item resource |
@@ -40,7 +40,7 @@ Integrate Box into your workflow to manage files, folders, and e-signatures. Upl
## Tools
## Actions
### `box_upload_file`
@@ -16,7 +16,7 @@ Integrate Brandfetch into your workflow. Retrieve brand logos, colors, fonts, an
## Tools
## Actions
### `brandfetch_get_brand`
@@ -16,7 +16,7 @@ Integrate Bright Data into the workflow. Scrape any URL with Web Unlocker, searc
## Tools
## Actions
### `brightdata_scrape_url`
@@ -31,7 +31,7 @@ Integrate Browser Use into the workflow. Can navigate the web and perform action
## Tools
## Actions
### `browser_use_run_task`
@@ -32,7 +32,7 @@ Integrate Cal.com into your workflow. Create and manage bookings, event types, s
## Tools
## Actions
### `calcom_create_booking`
@@ -776,3 +776,285 @@ Get available booking slots for a Cal.com event type within a time range
| `data` | json | Available time slots grouped by date \(YYYY-MM-DD keys\). Each date maps to an array of slot objects with start time, optional end time, and seated event info. |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### CalCom Booking Cancelled
Trigger workflow when a booking is cancelled in Cal.com
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Booking title |
| ↳ `description` | string | Booking description |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Booking start time \(ISO 8601\) |
| ↳ `endTime` | string | Booking end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `status` | string | Booking status |
| ↳ `location` | string | Meeting location or URL |
| ↳ `cancellationReason` | string | Reason for cancellation |
| ↳ `responses` | json | Booking form responses |
| ↳ `metadata` | json | Custom metadata attached to the booking |
---
### CalCom Booking Created
Trigger workflow when a new booking is created in Cal.com
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Booking title |
| ↳ `description` | string | Booking description |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Booking start time \(ISO 8601\) |
| ↳ `endTime` | string | Booking end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `status` | string | Booking status |
| ↳ `location` | string | Meeting location or URL |
| ↳ `responses` | json | Booking form responses \(dynamic - fields depend on your event type configuration\) |
| ↳ `metadata` | json | Custom metadata attached to the booking \(dynamic - user-defined key-value pairs\) |
| ↳ `videoCallData` | json | Video call details \(structure varies by provider\) |
---
### CalCom Booking Paid
Trigger workflow when payment is completed for a paid booking
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type \(BOOKING_PAID\) |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Booking title |
| ↳ `description` | string | Booking description |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Booking start time \(ISO 8601\) |
| ↳ `endTime` | string | Booking end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `status` | string | Booking status |
| ↳ `location` | string | Meeting location or URL |
| ↳ `payment` | object | Payment details |
| ↳ `id` | string | Payment ID |
| ↳ `amount` | number | Payment amount |
| ↳ `currency` | string | Payment currency |
| ↳ `success` | boolean | Whether payment succeeded |
| ↳ `metadata` | json | Custom metadata attached to the booking |
---
### CalCom Booking Rejected
Trigger workflow when a booking request is rejected by the host
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type \(BOOKING_REJECTED\) |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Booking title |
| ↳ `description` | string | Booking description |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Requested start time \(ISO 8601\) |
| ↳ `endTime` | string | Requested end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `status` | string | Booking status \(rejected\) |
| ↳ `rejectionReason` | string | Reason for rejection provided by host |
| ↳ `metadata` | json | Custom metadata attached to the booking |
---
### CalCom Booking Requested
Trigger workflow when a booking request is submitted (pending confirmation)
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type \(BOOKING_REQUESTED\) |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Booking title |
| ↳ `description` | string | Booking description |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Requested start time \(ISO 8601\) |
| ↳ `endTime` | string | Requested end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `status` | string | Booking status \(pending\) |
| ↳ `location` | string | Meeting location or URL |
| ↳ `responses` | json | Booking form responses |
| ↳ `metadata` | json | Custom metadata attached to the booking |
---
### CalCom Booking Rescheduled
Trigger workflow when a booking is rescheduled in Cal.com
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Booking title |
| ↳ `description` | string | Booking description |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | New booking start time \(ISO 8601\) |
| ↳ `endTime` | string | New booking end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `status` | string | Booking status |
| ↳ `location` | string | Meeting location or URL |
| ↳ `rescheduleId` | number | Previous booking ID |
| ↳ `rescheduleUid` | string | Previous booking UID |
| ↳ `rescheduleStartTime` | string | Original start time \(ISO 8601\) |
| ↳ `rescheduleEndTime` | string | Original end time \(ISO 8601\) |
| ↳ `responses` | json | Booking form responses |
| ↳ `metadata` | json | Custom metadata attached to the booking |
---
### CalCom Meeting Ended
Trigger workflow when a Cal.com meeting ends
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type \(MEETING_ENDED\) |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Meeting title |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Meeting start time \(ISO 8601\) |
| ↳ `endTime` | string | Meeting end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `duration` | number | Actual meeting duration in minutes |
| ↳ `videoCallData` | json | Video call details |
---
### CalCom Recording Ready
Trigger workflow when a meeting recording is ready for download
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type \(RECORDING_READY\) |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | object | payload output from the tool |
| ↳ `title` | string | Meeting title |
| ↳ `eventTypeId` | number | Event type ID |
| ↳ `startTime` | string | Meeting start time \(ISO 8601\) |
| ↳ `endTime` | string | Meeting end time \(ISO 8601\) |
| ↳ `uid` | string | Unique booking identifier |
| ↳ `bookingId` | number | Numeric booking ID |
| ↳ `recordingUrl` | string | URL to download the recording |
| ↳ `transcription` | string | Meeting transcription text \(if available\) |
---
### CalCom Webhook (All Events)
Trigger workflow on any Cal.com webhook event (configure event types in Cal.com)
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Used to verify webhook requests via X-Cal-Signature-256 header. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `triggerEvent` | string | The webhook event type \(e.g., BOOKING_CREATED, MEETING_ENDED\) |
| `createdAt` | string | When the webhook event was created \(ISO 8601\) |
| `payload` | json | Complete webhook payload \(structure varies by event type\) |
@@ -30,7 +30,7 @@ Integrate Calendly into your workflow. Manage event types, scheduled events, inv
## Tools
## Actions
### `calendly_get_current_user`
@@ -292,3 +292,173 @@ Cancel a scheduled event
| ↳ `created_at` | string | ISO timestamp when event was canceled |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Calendly Invitee Canceled
Trigger workflow when someone cancels a scheduled event on Calendly
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Personal Access Token |
| `organization` | string | Yes | Organization URI for the webhook subscription. Get this from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | string | Event type \(invitee.created or invitee.canceled\) |
| `created_at` | string | Webhook event creation timestamp |
| `created_by` | string | URI of the Calendly user who created this webhook |
| `payload` | object | payload output from the tool |
| ↳ `uri` | string | Invitee URI |
| ↳ `email` | string | Invitee email address |
| ↳ `name` | string | Invitee full name |
| ↳ `first_name` | string | Invitee first name |
| ↳ `last_name` | string | Invitee last name |
| ↳ `status` | string | Invitee status \(active or canceled\) |
| ↳ `timezone` | string | Invitee timezone |
| ↳ `event` | string | Scheduled event URI |
| ↳ `text_reminder_number` | string | Phone number for text reminders |
| ↳ `rescheduled` | boolean | Whether this invitee rescheduled |
| ↳ `old_invitee` | string | URI of the old invitee \(if rescheduled\) |
| ↳ `new_invitee` | string | URI of the new invitee \(if rescheduled\) |
| ↳ `cancel_url` | string | URL to cancel the event |
| ↳ `reschedule_url` | string | URL to reschedule the event |
| ↳ `created_at` | string | Invitee creation timestamp |
| ↳ `updated_at` | string | Invitee last update timestamp |
| ↳ `canceled` | boolean | Whether the event was canceled |
| ↳ `cancellation` | object | Cancellation details |
| ↳ `canceled_by` | string | Who canceled the event |
| ↳ `reason` | string | Cancellation reason |
| ↳ `payment` | object | Payment details |
| ↳ `id` | string | Payment ID |
| ↳ `provider` | string | Payment provider |
| ↳ `amount` | number | Payment amount |
| ↳ `currency` | string | Payment currency |
| ↳ `terms` | string | Payment terms |
| ↳ `successful` | boolean | Whether payment was successful |
| ↳ `no_show` | object | No-show details |
| ↳ `created_at` | string | No-show marked timestamp |
| ↳ `reconfirmation` | object | Reconfirmation details |
| ↳ `created_at` | string | Reconfirmation timestamp |
| ↳ `confirmed_at` | string | Confirmation timestamp |
---
### Calendly Invitee Created
Trigger workflow when someone schedules a new event on Calendly
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Personal Access Token |
| `organization` | string | Yes | Organization URI for the webhook subscription. Get this from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | string | Event type \(invitee.created or invitee.canceled\) |
| `created_at` | string | Webhook event creation timestamp |
| `created_by` | string | URI of the Calendly user who created this webhook |
| `payload` | object | payload output from the tool |
| ↳ `uri` | string | Invitee URI |
| ↳ `email` | string | Invitee email address |
| ↳ `name` | string | Invitee full name |
| ↳ `first_name` | string | Invitee first name |
| ↳ `last_name` | string | Invitee last name |
| ↳ `status` | string | Invitee status \(active or canceled\) |
| ↳ `timezone` | string | Invitee timezone |
| ↳ `event` | string | Scheduled event URI |
| ↳ `text_reminder_number` | string | Phone number for text reminders |
| ↳ `rescheduled` | boolean | Whether this invitee rescheduled |
| ↳ `old_invitee` | string | URI of the old invitee \(if rescheduled\) |
| ↳ `new_invitee` | string | URI of the new invitee \(if rescheduled\) |
| ↳ `cancel_url` | string | URL to cancel the event |
| ↳ `reschedule_url` | string | URL to reschedule the event |
| ↳ `created_at` | string | Invitee creation timestamp |
| ↳ `updated_at` | string | Invitee last update timestamp |
| ↳ `canceled` | boolean | Whether the event was canceled |
| ↳ `cancellation` | object | Cancellation details |
| ↳ `canceled_by` | string | Who canceled the event |
| ↳ `reason` | string | Cancellation reason |
| ↳ `payment` | object | Payment details |
| ↳ `id` | string | Payment ID |
| ↳ `provider` | string | Payment provider |
| ↳ `amount` | number | Payment amount |
| ↳ `currency` | string | Payment currency |
| ↳ `terms` | string | Payment terms |
| ↳ `successful` | boolean | Whether payment was successful |
| ↳ `no_show` | object | No-show details |
| ↳ `created_at` | string | No-show marked timestamp |
| ↳ `reconfirmation` | object | Reconfirmation details |
| ↳ `created_at` | string | Reconfirmation timestamp |
| ↳ `confirmed_at` | string | Confirmation timestamp |
---
### Calendly Routing Form Submitted
Trigger workflow when someone submits a Calendly routing form
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Personal Access Token |
| `organization` | string | Yes | Organization URI for the webhook subscription. Get this from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | string | Event type \(routing_form_submission.created\) |
| `created_at` | string | Webhook event creation timestamp |
| `created_by` | string | URI of the Calendly user who created this webhook |
| `payload` | object | payload output from the tool |
| ↳ `uri` | string | Routing form submission URI |
| ↳ `routing_form` | string | Routing form URI |
| ↳ `submitter` | object | Submitter details |
| ↳ `uri` | string | Submitter URI |
| ↳ `email` | string | Submitter email address |
| ↳ `name` | string | Submitter full name |
| ↳ `submitter_type` | string | Type of submitter |
| ↳ `result` | object | Routing result details |
| ↳ `type` | string | Result type \(event_type, custom_message, or external_url\) |
| ↳ `value` | string | Result value \(event type URI, message, or URL\) |
| ↳ `created_at` | string | Submission creation timestamp |
| ↳ `updated_at` | string | Submission last update timestamp |
---
### Calendly Webhook
Trigger workflow from any Calendly webhook event
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Personal Access Token |
| `organization` | string | Yes | Organization URI for the webhook subscription. Get this from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | string | Event type \(invitee.created, invitee.canceled, or routing_form_submission.created\) |
| `created_at` | string | Webhook event creation timestamp |
| `created_by` | string | URI of the Calendly user who created this webhook |
| `payload` | object | Complete event payload \(structure varies by event type\) |
@@ -1,6 +1,6 @@
---
title: Circleback
description: Available Circleback triggers for automating workflows
description: Circleback triggers for automating workflows
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -10,10 +10,50 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
color="linear-gradient(180deg, #E0F7FA 0%, #FFFFFF 100%)"
/>
Circleback provides 3 triggers for automating workflows based on events.
{/* MANUAL-CONTENT-START:intro */}
[Circleback](https://circleback.ai/) is an AI-powered platform that automates meeting notes, action items, transcripts, and recordings for your team. When a meeting is completed, Circleback processes the conversation and provides detailed notes and action items, along with a transcript and a recording (when available). This helps teams efficiently capture insights, distribute action items, and ensure nothing is missed—all seamlessly integrated into your workflows.
With the Sim Circleback integration, you can:
- **Receive detailed meeting notes and action items**: Automatically collect well-formatted meeting summaries and track actionable tasks discussed during your calls.
- **Access complete meeting recordings and transcripts**: Get the full conversation and the associated recording, making it easy to review key moments or share with colleagues.
- **Capture attendee information and meeting context**: Attendee lists, meeting metadata, and tags help keep your data organized and actionable.
- **Deliver insights directly into your workflows**: Trigger automations or send Circleback data to other systems the moment a meeting is done, using Sim’s powerful webhook triggers.
**How it works in Sim:**
Circleback uses webhook triggers: whenever a meeting is processed, data is pushed automatically to your agent or automation. You can build further automations based on:
- Meeting completed (all processed data available)
- New notes (notes ready even before full meeting is processed)
- Raw webhook integration for advanced use cases
**The following information is available in the Circleback meeting webhook payload:**
| Field | Type | Description |
|----------------|---------|----------------------------------------------------|
| `id` | number | Circleback meeting ID |
| `name` | string | Meeting title |
| `url` | string | Virtual meeting URL (Zoom, Meet, Teams, etc.) |
| `createdAt` | string | Meeting creation timestamp |
| `duration` | number | Duration in seconds |
| `recordingUrl` | string | Recording URL (valid 24 hours) |
| `tags` | json | Array of tags |
| `icalUid` | string | Calendar event ID |
| `attendees` | json | Array of attendee objects |
| `notes` | string | Meeting notes in Markdown |
| `actionItems` | json | Array of action items |
| `transcript` | json | Array of transcript segments |
| `insights` | json | User-created insights |
| `meeting` | json | Full meeting payload |
Whether you want to distribute instant summaries, log action items, or build custom workflows triggered by new meeting data, Circleback and Sim make it seamless to handle everything related to your meetings—automatically.
{/* MANUAL-CONTENT-END */}
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Circleback Meeting Completed
Trigger workflow when a meeting is processed and ready in Circleback
@@ -31,7 +31,7 @@ Integrate Clay into the workflow. Can populate a table with data.
## Tools
## Actions
### `clay_populate`
@@ -32,7 +32,7 @@ Integrate Clerk authentication and user management into your workflow. Create, u
## Tools
## Actions
### `clerk_list_users`
@@ -37,7 +37,7 @@ Integrate ClickHouse into the workflow. Query and insert data, manage databases
## Tools
## Actions
### `clickhouse_query`
@@ -32,7 +32,7 @@ Integrate Cloudflare into the workflow. Manage zones (domains), DNS records, SSL
## Tools
## Actions
### `cloudflare_list_zones`
@@ -33,7 +33,7 @@ Integrate AWS CloudFormation into workflows. Describe stacks, list resources, de
## Tools
## Actions
### `cloudformation_describe_stacks`
@@ -34,7 +34,7 @@ Integrate AWS CloudWatch into workflows. Run Log Insights queries, list log grou
## Tools
## Actions
### `cloudwatch_query_logs`
@@ -16,7 +16,7 @@ Integrate AWS CodePipeline into workflows. Start, stop, and monitor pipeline exe
## Tools
## Actions
### `codepipeline_list_pipelines`
@@ -31,7 +31,7 @@ Integrate Confluence into the workflow. Can read, create, update, delete pages,
## Tools
## Actions
### `confluence_retrieve`
@@ -1433,3 +1433,684 @@ Get display name and profile info for a Confluence user by account ID.
| `publicName` | string | Public name of the user |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Confluence Attachment Created
Trigger workflow when an attachment is uploaded in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
| `confluenceEmail` | string | No | Your Atlassian account email. Required together with API token to download attachment files. |
| `confluenceApiToken` | string | No | API token from https://id.atlassian.com/manage-profile/security/api-tokens. Required to download attachment file content. |
| `includeFileContent` | boolean | No | Download and include actual file content from attachments. Requires email, API token, and domain. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `attachment` | object | attachment output from the tool |
| ↳ `mediaType` | string | MIME type of the attachment |
| ↳ `fileSize` | number | File size in bytes |
| ↳ `parent` | object | parent output from the tool |
| ↳ `id` | number | Container page/blog ID |
| ↳ `title` | string | Container page/blog title |
| ↳ `contentType` | string | Container content type |
| `files` | file[] | Attachment file content downloaded from Confluence \(if includeFileContent is enabled with credentials\) |
---
### Confluence Attachment Removed
Trigger workflow when an attachment is removed in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
| `confluenceEmail` | string | No | Your Atlassian account email. Required together with API token to download attachment files. |
| `confluenceApiToken` | string | No | API token from https://id.atlassian.com/manage-profile/security/api-tokens. Required to download attachment file content. |
| `includeFileContent` | boolean | No | Download and include actual file content from attachments. Requires email, API token, and domain. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `attachment` | object | attachment output from the tool |
| ↳ `mediaType` | string | MIME type of the attachment |
| ↳ `fileSize` | number | File size in bytes |
| ↳ `parent` | object | parent output from the tool |
| ↳ `id` | number | Container page/blog ID |
| ↳ `title` | string | Container page/blog title |
| ↳ `contentType` | string | Container content type |
| `files` | file[] | Attachment file content downloaded from Confluence \(if includeFileContent is enabled with credentials\) |
---
### Confluence Attachment Updated
Trigger workflow when an attachment is updated in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
| `confluenceEmail` | string | No | Your Atlassian account email. Required together with API token to download attachment files. |
| `confluenceApiToken` | string | No | API token from https://id.atlassian.com/manage-profile/security/api-tokens. Required to download attachment file content. |
| `includeFileContent` | boolean | No | Download and include actual file content from attachments. Requires email, API token, and domain. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `attachment` | object | attachment output from the tool |
| ↳ `mediaType` | string | MIME type of the attachment |
| ↳ `fileSize` | number | File size in bytes |
| ↳ `parent` | object | parent output from the tool |
| ↳ `id` | number | Container page/blog ID |
| ↳ `title` | string | Container page/blog title |
| ↳ `contentType` | string | Container content type |
| `files` | file[] | Attachment file content downloaded from Confluence \(if includeFileContent is enabled with credentials\) |
---
### Confluence Blog Post Created
Trigger workflow when a blog post is created in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Blog Post Removed
Trigger workflow when a blog post is removed in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Blog Post Restored
Trigger workflow when a blog post is restored from trash in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Blog Post Updated
Trigger workflow when a blog post is updated in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Comment Created
Trigger workflow when a comment is created in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `comment` | object | comment output from the tool |
| ↳ `parent` | object | parent output from the tool |
| ↳ `id` | number | Parent page/blog ID |
| ↳ `title` | string | Parent page/blog title |
| ↳ `contentType` | string | Parent content type \(page or blogpost\) |
| ↳ `spaceKey` | string | Space key of the parent |
| ↳ `self` | string | URL link to the parent content |
---
### Confluence Comment Removed
Trigger workflow when a comment is removed in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `comment` | object | comment output from the tool |
| ↳ `parent` | object | parent output from the tool |
| ↳ `id` | number | Parent page/blog ID |
| ↳ `title` | string | Parent page/blog title |
| ↳ `contentType` | string | Parent content type \(page or blogpost\) |
| ↳ `spaceKey` | string | Space key of the parent |
| ↳ `self` | string | URL link to the parent content |
---
### Confluence Comment Updated
Trigger workflow when a comment is updated in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `comment` | object | comment output from the tool |
| ↳ `parent` | object | parent output from the tool |
| ↳ `id` | number | Parent page/blog ID |
| ↳ `title` | string | Parent page/blog title |
| ↳ `contentType` | string | Parent content type \(page or blogpost\) |
| ↳ `spaceKey` | string | Space key of the parent |
| ↳ `self` | string | URL link to the parent content |
---
### Confluence Label Added
Trigger workflow when a label is added to content in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `label` | object | label output from the tool |
| ↳ `name` | string | Label name |
| ↳ `id` | string | Label ID |
| ↳ `prefix` | string | Label prefix \(global, my, team\) |
| `content` | object | content output from the tool |
| ↳ `id` | number | Content ID the label was added to or removed from |
| ↳ `title` | string | Content title |
| ↳ `contentType` | string | Content type \(page, blogpost\) |
---
### Confluence Label Removed
Trigger workflow when a label is removed from content in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `label` | object | label output from the tool |
| ↳ `name` | string | Label name |
| ↳ `id` | string | Label ID |
| ↳ `prefix` | string | Label prefix \(global, my, team\) |
| `content` | object | content output from the tool |
| ↳ `id` | number | Content ID the label was added to or removed from |
| ↳ `title` | string | Content title |
| ↳ `contentType` | string | Content type \(page, blogpost\) |
---
### Confluence Page Created
Trigger workflow when a new page is created in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Page Moved
Trigger workflow when a page is moved in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Page Permissions Updated
Trigger workflow when page permissions are changed in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `id` | number | Content ID |
| `title` | string | Content title |
| `contentType` | string | Content type \(page, blogpost, comment, attachment\) |
| `version` | number | Version number |
| `spaceKey` | string | Space key the content belongs to |
| `creatorAccountId` | string | Account ID of the creator |
| `lastModifierAccountId` | string | Account ID of the last modifier |
| `self` | string | URL link to the content |
| `creationDate` | number | Creation timestamp \(Unix epoch milliseconds\) |
| `modificationDate` | number | Last modification timestamp \(Unix epoch milliseconds\) |
| `page` | object | page output from the tool |
| ↳ `permissions` | json | Updated permissions object for the page |
---
### Confluence Page Removed
Trigger workflow when a page is removed or trashed in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Page Restored
Trigger workflow when a page is restored from trash in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Page Updated
Trigger workflow when a page is updated in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
---
### Confluence Space Created
Trigger workflow when a new space is created in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `space` | object | space output from the tool |
| ↳ `key` | string | Space key |
| ↳ `name` | string | Space name |
| ↳ `self` | string | URL link to the space |
---
### Confluence Space Removed
Trigger workflow when a space is removed in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `space` | object | space output from the tool |
| ↳ `key` | string | Space key |
| ↳ `name` | string | Space name |
| ↳ `self` | string | URL link to the space |
---
### Confluence Space Updated
Trigger workflow when a space is updated in Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `space` | object | space output from the tool |
| ↳ `key` | string | Space key |
| ↳ `name` | string | Space name |
| ↳ `self` | string | URL link to the space |
---
### Confluence User Created
Trigger workflow when a new user is added to Confluence
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `user` | object | user output from the tool |
| ↳ `accountId` | string | Account ID of the new user |
| ↳ `accountType` | string | Account type \(e.g., atlassian, app\) |
| ↳ `displayName` | string | Display name of the user |
| ↳ `emailAddress` | string | Email address of the user \(may not be available due to GDPR/privacy settings\) |
| ↳ `publicName` | string | Public name of the user |
| ↳ `self` | string | URL link to the user profile |
---
### Confluence Webhook (All Events)
Trigger workflow on any Confluence webhook event
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `webhookSecret` | string | No | Optional secret to validate webhook deliveries from Confluence using HMAC signature |
| `confluenceDomain` | string | No | Your Confluence Cloud domain |
| `confluenceEmail` | string | No | Your Atlassian account email. Required together with API token to download attachment files. |
| `confluenceApiToken` | string | No | API token from https://id.atlassian.com/manage-profile/security/api-tokens. Required to download attachment file content. |
| `includeFileContent` | boolean | No | Download and include actual file content from attachments. Requires email, API token, and domain. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `timestamp` | number | Timestamp of the webhook event \(Unix epoch milliseconds\) |
| `userAccountId` | string | Account ID of the user who triggered the event |
| `accountType` | string | Account type \(e.g., customer\) |
| `page` | json | Page object \(present in page events\) |
| `comment` | json | Comment object \(present in comment events\) |
| `blog` | json | Blog post object \(present in blog events\) |
| `attachment` | json | Attachment object \(present in attachment events\) |
| `space` | json | Space object \(present in space events\) |
| `label` | json | Label object \(present in label events\) |
| `content` | json | Content object \(present in label events\) |
| `user` | json | User object \(present in user events\) |
| `files` | file[] | Attachment file content \(present in attachment events when includeFileContent is enabled\) |
@@ -16,7 +16,7 @@ Integrate CrowdStrike Identity Protection into workflows to search sensors, fetc
## Tools
## Actions
### `crowdstrike_get_sensor_aggregates`
@@ -32,7 +32,7 @@ Interact with Cursor Cloud Agents API to launch AI agents that can work on your
## Tools
## Actions
### `cursor_list_agents`
@@ -30,7 +30,7 @@ Connect to a Dagster instance to launch job runs, monitor run status, list avail
## Tools
## Actions
### `dagster_launch_run`
@@ -31,7 +31,7 @@ Connect to Databricks to execute SQL queries against SQL warehouses, trigger and
## Tools
## Actions
### `databricks_execute_sql`
@@ -31,7 +31,7 @@ Integrate Datadog monitoring into workflows. Submit metrics, manage monitors, qu
## Tools
## Actions
### `datadog_submit_metrics`
@@ -39,7 +39,7 @@ Integrate Devin into your workflow. Create sessions to assign coding tasks, send
## Tools
## Actions
### `devin_create_session`
@@ -42,7 +42,7 @@ Comprehensive Discord integration: messages, threads, channels, roles, members,
## Tools
## Actions
### `discord_send_message`
@@ -33,7 +33,7 @@ Create and send envelopes for e-signature, use templates, check signing status,
## Tools
## Actions
### `docusign_send_envelope`
@@ -32,7 +32,7 @@ Integrate Dropbox into your workflow for file management, sharing, and collabora
## Tools
## Actions
### `dropbox_upload`
@@ -30,7 +30,7 @@ Integrate with your self-hosted DSPy programs for LLM-powered predictions. Suppo
## Tools
## Actions
### `dspy_predict`
@@ -33,7 +33,7 @@ Create, manage, and track short links with Dub. Supports custom domains, UTM par
## Tools
## Actions
### `dub_create_link`
@@ -31,7 +31,7 @@ Search the web using DuckDuckGo Instant Answers API. Returns instant answers, ab
## Tools
## Actions
### `duckduckgo_search`
@@ -41,7 +41,7 @@ Integrate Amazon DynamoDB into workflows. Supports Get, Put, Query, Scan, Update
## Tools
## Actions
### `dynamodb_get`
@@ -32,7 +32,7 @@ Integrate Elasticsearch into workflows for powerful search, indexing, and data m
## Tools
## Actions
### `elasticsearch_search`
@@ -31,7 +31,7 @@ Integrate ElevenLabs into the workflow. Can convert text to speech.
## Tools
## Actions
### `elevenlabs_tts`
@@ -1,19 +1,443 @@
---
title: Emailbison
description: Available Emailbison triggers for automating workflows
title: Email Bison
description: Manage Email Bison leads, campaigns, replies, and tags
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
<BlockInfoCard
type="emailbison"
color="#FB7A22"
/>
Emailbison provides 17 triggers for automating workflows based on events.
## Usage Instructions
Integrate Email Bison into workflows. Create and update leads, manage campaigns, attach leads to campaigns, list replies, and organize leads with tags.
## Actions
### `emailbison_list_leads`
Retrieves leads from Email Bison with optional search and tag filters.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `search` | string | No | Search term for filtering leads |
| `campaignStatus` | string | No | Lead campaign status filter: in_sequence, sequence_finished, sequence_stopped, never_contacted, or replied |
| `tagIds` | array | No | Tag IDs to include |
| `excludedTagIds` | array | No | Tag IDs to exclude |
| `withoutTags` | boolean | No | Only return leads without tags |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_get_lead`
Retrieves a lead by Email Bison lead ID or email address.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `leadId` | string | Yes | Lead ID or email address |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_create_lead`
Creates a single lead in Email Bison.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `firstName` | string | Yes | Lead first name |
| `lastName` | string | Yes | Lead last name |
| `email` | string | Yes | Lead email address |
| `title` | string | No | Lead job title |
| `company` | string | No | Lead company |
| `notes` | string | No | Additional notes about the lead |
| `customVariables` | array | No | Custom variables to store on the lead |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_update_lead`
Updates an existing Email Bison lead. Fields omitted from a PUT update may be cleared by Email Bison.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `leadId` | string | Yes | Lead ID or email address |
| `firstName` | string | Yes | Lead first name |
| `lastName` | string | Yes | Lead last name |
| `email` | string | Yes | Lead email address |
| `title` | string | No | Lead job title |
| `company` | string | No | Lead company |
| `notes` | string | No | Additional notes about the lead |
| `customVariables` | array | No | Custom variables to store on the lead |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_list_campaigns`
Retrieves Email Bison campaigns.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_create_campaign`
Creates a new Email Bison campaign.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | Yes | Campaign name |
| `campaignType` | string | No | Campaign type: outbound or reply_followup |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_update_campaign`
Updates Email Bison campaign settings.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `campaignId` | number | Yes | Campaign ID |
| `name` | string | No | Campaign name |
| `maxEmailsPerDay` | number | No | Maximum emails per day |
| `maxNewLeadsPerDay` | number | No | Maximum new leads per day |
| `plainText` | boolean | No | Send plain text emails |
| `openTracking` | boolean | No | Enable open tracking |
| `reputationBuilding` | boolean | No | Enable reputation building |
| `canUnsubscribe` | boolean | No | Enable unsubscribe link |
| `includeAutoRepliesInStats` | boolean | No | Include auto replies in campaign stats |
| `sequencePrioritization` | string | No | Sequence prioritization: followups or new_leads |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_update_campaign_status`
Pauses, resumes, or archives an Email Bison campaign.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `campaignId` | number | Yes | Campaign ID |
| `action` | string | Yes | Status action: pause, resume, or archive |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_attach_leads_to_campaign`
Adds existing Email Bison leads to a campaign.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `campaignId` | number | Yes | Campaign ID |
| `leadIds` | array | Yes | Lead IDs to add to the campaign |
| `allowParallelSending` | boolean | No | Force add leads already in sequence in other campaigns |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_list_replies`
Retrieves Email Bison replies with optional status, folder, campaign, sender, lead, and tag filters.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `search` | string | No | Search term for replies |
| `status` | string | No | Reply status: interested, automated_reply, or not_automated_reply |
| `folder` | string | No | Reply folder: inbox, sent, spam, bounced, or all |
| `read` | boolean | No | Filter by read state |
| `campaignId` | number | No | Campaign ID |
| `senderEmailId` | number | No | Sender email ID |
| `leadId` | number | No | Lead ID |
| `tagIds` | array | No | Tag IDs to filter replies by |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_list_tags`
Retrieves all Email Bison tags for the authenticated workspace.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_create_tag`
Creates a new Email Bison tag.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | Yes | Tag name |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
### `emailbison_attach_tags_to_leads`
Attaches Email Bison tags to one or more leads.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `tagIds` | array | Yes | Tag IDs to attach |
| `leadIds` | array | Yes | Lead IDs to tag |
| `skipWebhooks` | boolean | No | Skip Email Bison webhooks for this action |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leads` | array | List of leads |
| `campaigns` | array | List of campaigns |
| `replies` | array | List of replies |
| `tags` | array | List of tags |
| `count` | number | Number of returned records |
| `id` | number | Record ID |
| `uuid` | string | Record UUID |
| `name` | string | Campaign or tag name |
| `first_name` | string | Lead first name |
| `last_name` | string | Lead last name |
| `email` | string | Lead email address |
| `status` | string | Record status |
| `success` | boolean | Whether the action succeeded |
| `message` | string | Action message |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### Email Bison Contact First Emailed
Trigger when a contact receives their first campaign email in Email Bison
@@ -32,7 +32,7 @@ Access real-time B2B data intelligence with Enrich.so. Enrich profiles from emai
## Tools
## Actions
### `enrich_check_credits`

Some files were not shown because too many files have changed in this diff Show More