Documentation

Integration Steps

For common fields, string substitutions, metadata filters, caching, and execution order, see the Agent Steps Overview.


Webhook Call Step

Makes an HTTP request to an external URL. Use this to integrate with external APIs, trigger workflows in other systems, post to messaging platforms, or send data to any HTTP endpoint.

Fields

FieldTypeRequiredDefaultDescription
urlstringYesThe endpoint URL (must start with http:// or https://). Supports substitutions.
methodenumNoPOSTHTTP method: POST or PUT
content_typeenumNoapplication/jsonRequest body content type: application/json, text/plain, text/html, application/xml
headersobjectNonullCustom HTTP headers as key-value pairs. Header values support substitutions.
payloadstringNonullThe request body. If null, the step's input is sent as the body. Supports substitutions.
payload_shapeenumNojson_with_urlsMulti-asset payload encoding when the step receives a manifest input: json_with_urls (download URLs in JSON), json_with_base64 (inline base64, 5 MB cap), or multipart_form (classic multipart/form-data). Ignored for plain text/JSON inputs.
attachment_url_modeenumNopresigned_s3Active only when payload_shape = json_with_urls. presigned_s3 emits 1-hour signed AWS S3 URLs the receiver can fetch without a Seclai key (no expiry control, no per-fetch audit). seclai_auth emits URLs that hit /api/v2/agent-runs/{run_id}/attachments/{attachment_id}; receivers must send an X-API-Key or Bearer token belonging to the same account — no expiry, revocable, every fetch logged. Pick seclai_auth for audited integrations and receivers that already hold a key; keep presigned_s3 for low-trust short-lived consumers.
attachmentslistNonullSelects which attachments are encoded into the request body — a list of {{…}} attachment reference expressions (e.g. {{attachments[*.pdf]}}, {{step.gen.attachments[0]}}). Null / empty list ⇒ every attachment on the step's input manifest.

Prompt-injection scanning: Webhook response bodies are automatically scanned for prompt injection attacks by the Prompt Scanner. If the response is flagged as unsafe, downstream steps that would consume the tainted data are blocked. The scan result appears as an Output Scan pseudo-step in the agent trace.

Rate limits: Outgoing webhook calls are rate-limited per destination host. Your plan sets a per-minute budget per host (see Outgoing webhook calls / minute on the plan comparison); a separate global ceiling protects each host across all accounts. When a limit is hit the step is automatically deferred and retried after a short backoff (the same handling as a 429 from the receiver), so bursts are throttled rather than failed. Build polling loops (webhook_call → wait → webhook_call) with a Wait step interval comfortably above your per-host budget.

Receiver-side signature verification

Every outgoing webhook delivery carries two headers so the receiver can prove the request came from Seclai (and was not modified in transit):

HeaderValue
X-Seclai-Signaturesha256=<hex> — HMAC-SHA256 over "{timestamp}.{body}" with your account's signing secret
X-Seclai-TimestampUnix milliseconds at the moment the delivery was signed. Reject deliveries older than ~5 minutes.

The signing secret is per-account, fetched / rotated via:

  • GET /authenticated/accounts/{account_id}/webhook-signing-secret — read the current secret (one-time, audited).
  • POST /authenticated/accounts/{account_id}/webhook-signing-secret/rotate — rotate and return the new secret. Old in-flight deliveries fail verification once rotated, so coordinate with receivers.

Python receiver example:

import hmac, hashlib, time

def verify_seclai_webhook(secret: str, headers: dict, body: bytes) -> bool:
    signature = headers.get("X-Seclai-Signature", "")
    timestamp_ms = headers.get("X-Seclai-Timestamp", "")
    if not signature.startswith("sha256=") or not timestamp_ms.isdigit():
        return False
    # Reject replays older than 5 minutes.
    if abs(time.time() * 1000 - int(timestamp_ms)) > 5 * 60 * 1000:
        return False
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"),
        f"{timestamp_ms}.".encode("utf-8") + body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Node receiver example:

const crypto = require("crypto");

function verifySeclaiWebhook(secret, headers, body) {
  const signature = headers["x-seclai-signature"] || "";
  const timestamp = headers["x-seclai-timestamp"] || "";
  if (!signature.startsWith("sha256=") || !/^\d+$/.test(timestamp))
    return false;
  if (Math.abs(Date.now() - parseInt(timestamp, 10)) > 5 * 60 * 1000)
    return false;
  const expected =
    "sha256=" +
    crypto
      .createHmac("sha256", secret)
      .update(`${timestamp}.`)
      .update(body)
      .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Signing is on by default — there's no per-step opt-out. Receivers that don't care about authenticity can simply ignore the headers.

Use Case Examples

Post to Slack:

URL: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
Method: POST
Content-Type: application/json
Payload:
{
  "text": "Agent {{agent.name}} completed: {{step.summary.output}}"
}
TriggerPrompt Callgenerate summaryTextSlack JSON payloadWebhook Callpost to Slack
Figure 1.Post to Slack — the agent generates a summary, formats a Slack JSON payload, and posts it via webhook.

Send to a custom API:

URL: https://api.example.com/ingest
Method: POST
Content-Type: application/json
Headers:
  Authorization: Bearer {{metadata.api_token}}
  X-Source: seclai-agent
Payload:
{
  "agent_id": "{{agent.id}}",
  "run_id": "{{agent.run_id}}",
  "result": {{step.extract_content.output}},
  "processed_at": "{{datetime UTC}}"
}

Trigger an external workflow:

URL: https://automation.example.com/webhooks/{{metadata.workflow_id}}
Method: POST
Content-Type: application/json
Payload:
{
  "event": "content_processed",
  "data": "{{step.final.output}}"
}
Triggercontent eventExtract Dataanalyze contentExtract Contentparse JSONWebhook Calltrigger workflow
Figure 2.Trigger external workflow — the agent analyzes content and triggers an external automation system via webhook.
TriggerPrompt Callgenerate summaryWebhook Callpost to SlackStreaming Result
Figure 3.Webhook Notify — after processing, the agent sends results to an external system via webhook.

MCP Client Call Step

Calls a tool on an external MCP (Model Context Protocol) server and returns the tool's result as the step output. Use this when an agent needs to invoke a capability hosted on an MCP server you've connected (for example, a third-party tool server or your own internal MCP service).

Before using this step you must add an MCP Client under MCP Clients in the left navigation — that's where the server URL and authentication live. The step references the saved client by id; it does not hold any connection details or secrets itself.

Fields

FieldTypeRequiredDescription
MCP ClientselectYesThe saved MCP client (external server) to connect to. Manage clients under MCP Clients.
Tool NamestringYesThe name of the tool to call on the external server. Use Refresh tools in the editor to fetch the server's tool list, or type a name. Supports string substitutions.
Argumentskey/value pairsNoThe arguments passed to the tool. Each value supports string substitutions (e.g. {{input}}, {{step.<id>.output}}), including attachment references (e.g. {{attachments[*.pdf]}}) — the referenced attachment's extracted text is inlined into that argument value (MCP arguments are JSON, so binaries are passed as text). There is no separate Attachments field on this step.

Output

If the tool returns structured content, the step output is that content as JSON. Otherwise the output is the tool's text result. If the external server is unreachable or rejects the call, the step fails; transient network errors are retried automatically.


Web Fetch Step

Fetches a single web page and returns its content. Use this to bring external page content into an agent run when the URL is known up front.

Fields

FieldTypeRequiredDefaultDescription
urlstringYesThe URL of the page to fetch. Must start with http:// or https://. Supports substitutions.
formatenumNomarkdownOutput format: markdown (best for LLM consumption), rawHtml (original HTML), or text (plain text).
media_typesstring[]No[]Media to download from the page and attach to the output. Any subset of images, video, audio, documents. Empty = text-only.

Caching: Results are cached for 5 minutes per URL + format — repeated fetches of the same URL with the same format within this window return instantly without an external request, but each step run is still billed at the standard web_fetch rate.

Prompt-injection scanning: Fetched content is automatically scanned for prompt injection attacks by the Prompt Scanner. If the content is flagged as unsafe, downstream steps that would consume the tainted data are blocked. The scan result appears as an Output Scan pseudo-step in the agent trace.

Governance: The resolved URL is screened against step-input governance policies before the fetch executes — blocking policies can prevent the request entirely. The fetched content itself is not governance-screened at this step, but when it flows into a terminal step (display_result, send_email, etc.) output governance policies evaluate it there. Additionally, the downstream prompt_call input scan provides injection-detection enforcement at the LLM boundary.

Step vs. tool: Use this step when the URL is known and you want predictable, auditable fetch behavior. For autonomous, model-driven page fetching, use the seclai_web_tools tool inside a Prompt Call step instead.

Attachments: By default Web Fetch returns text (markdown / HTML / plain text), which flows downstream as {{step.<id>.output}} and feeds a prompt_call as text. Set media_types to also download page media and attach it: images pulls <img> tags (inline data: URIs decoded, remote URLs downloaded, obvious ads filtered); video / audio pull embedded <video> / <audio> sources; documents follows <a href> links to downloadable files (PDF, Office, CSV, …). When a kind is selected and at least one asset is successfully downloaded, the output becomes a multi-asset manifest (text + attachments) that downstream prompt_call, send_email, and S3 steps can consume. If no assets are found, or every candidate is skipped (failed fetch / over a size cap), the step falls back to plain text output and produces no attachments — so don't assume {{step.<id>.attachments}} exists merely because media_types was set. Note that prompt_call only receives attachments its configured model can ingest — routing is gated by the model's supported_input_media (typically images, and video / PDF on multi-modal models); other kinds (audio, non-PDF documents like Office/CSV) are dropped for prompt_call even though they remain fully usable for send_email and S3 steps. Each asset is fetched through an SSRF guard and bounded by per-asset and total size caps; assets that fail to download or exceed a cap are skipped, leaving the text body intact. Reference individual assets with {{step.<id>.attachments[*]}} selectors — see the Attachments guide.

Use Case Examples

Fetch a known article and summarize it:

Web Fetch:
  url: {{metadata.article_url}}
  format: markdown
  → Prompt Call: Summarize the following article in 3 bullet points.
    {{input}}
    → Streaming Result

Search → fetch top result → derive insight:

Web Search:
  query: {{agent.input}}
  limit: 5
  → Extract Content (json_path: "$[0].url")
    → Web Fetch:
        url: {{input}}
        format: markdown
        → Extract Data: What are the three most important takeaways?
          → Display Result
TriggerWeb Searchfind pagesExtract Contenttop result URLWeb Fetchget pageExtract Datakey takeaways
Figure 4.Search → Extract → Fetch → Extract Data — the agent searches the web, extracts the top URL, fetches the page, and derives key takeaways.
TriggerWeb Fetchget articlePrompt CallsummarizeStreaming Result
Figure 5.Web Fetch + Summarize — the agent fetches a URL, summarizes the page content, and streams the result.

Web Search Step

Searches the web for a query and returns matching pages with titles, descriptions, and short content snippets. Use this to discover relevant pages for a topic before deciding whether to fetch any of them in full.

Fields

FieldTypeRequiredDefaultDescription
querystringYesThe search query. Supports substitutions.
limitintegerNo5Maximum number of search results to return (1–20).

Output shape: A JSON array of result objects, each with url, title, description, and content fields. Content snippets are truncated to ~1000 characters.

Prompt-injection scanning: Search results are automatically scanned for prompt injection attacks by the Prompt Scanner. If the content is flagged as unsafe, downstream steps that would consume the tainted data are blocked. The scan result appears as an Output Scan pseudo-step in the agent trace.

Step vs. tool: Use this step when you want a predictable, deterministic search with a fixed query template and downstream processing. For autonomous, model-driven web research, use the seclai_web_tools tool inside a Prompt Call step instead.

Use Case Examples

Search and synthesize from snippets only (no fetch):

Web Search:
  query: latest developments in {{agent.input}}
  limit: 8
  → Prompt Call: Summarize what these snippets say about {{agent.input}}.
    {{input}}
    → Streaming Result

Search → fetch first result → publish:

Web Search:
  query: "{{metadata.topic}} site:example.com"
  limit: 3
  → Extract Content (json_path: "$[0].url")
    → Web Fetch:
        url: {{input}}
        format: markdown
        → Publish Content
TriggerWeb Searchsite-scoped queryExtract Contentfirst URLWeb Fetchget pagePublish Contentstore in KB
Figure 6.Search → Fetch → Publish — the agent searches for a topic on a specific site, fetches the top result, and publishes it to a content store.
TriggerWeb SearchExtract Contentget top URLWeb FetchExtract Dataanalyze pageDisplay Result
Figure 7.Web research pipeline: search the web, extract the top URL, fetch the page, analyze it with Extract Data, and display the result.

Write AWS S3 Object Step

Saves content to an AWS S3 bucket. Use this to archive agent results, store generated reports, export data, or create file backups.

Fields

FieldTypeRequiredDefaultDescription
bucket_namestringYesThe S3 bucket name. Supports substitutions.
object_pathstringNonullOptional path prefix prepended to object_key (joined with a single /). May reference run-level substitutions (e.g. {{agent.run_id}}, {{datetime UTC}}) but MUST NOT contain per-attachment placeholders {{name}} / {{index}} / {{ext}} — those belong on object_key.
object_keystringYesThe key (filename) within the S3 bucket, appended after object_path if set. For multi-asset prompt outputs (image generation, etc.) include {{name}}, {{index}}, or {{ext}} so each attachment writes to a distinct key (e.g. {{index}}-{{name}}). Multi-asset input without a placeholder is rejected at runtime to prevent silent overwrites.
content_typeenumNotext/plainContent type for the stored object: text/plain, text/html, application/json, application/xml. Ignored for multi-asset prompt outputs — each attachment retains its native MIME.
contentstringNonullThe content to write. If null, the step's input is used. Supports substitutions.
write_manifest_textbooleanNofalseWhen the input is a multi-asset manifest, also write the manifest's text body to a separate object derived from object_key (the placeholder pattern is replaced with text.txt).
attachmentslistNonullSelects which attachments are written — a list of {{…}} attachment reference expressions (e.g. {{attachments[*.png]}}, {{step.gen.attachments[0]}}). Each match becomes its own S3 object using object_key as a template with the placeholders above (a multi-match without a placeholder is rejected at runtime). Null / empty list ⇒ every attachment on the step's input manifest.

Use Case Examples

Store a daily report:

Bucket: my-reports-bucket
Key: reports/{{date UTC}}/daily-summary.html
Content Type: text/html
Content: (uses step input — the HTML report from previous step)
TriggerscheduledRetrievaltoday's dataPrompt Callbuild HTML reportWrite S3daily-summary.html
Figure 8.Daily S3 report — the agent generates analysis, formats it as JSON, and writes it to a date-partitioned S3 path.

Archive agent results as JSON:

Bucket: agent-results
Key: agents/{{agent.id}}/runs/{{agent.run_id}}/output.json
Content Type: application/json
Content: {{step.extract_content.output}}

Organize by metadata:

Bucket: content-exports
Key: {{metadata.category}}/{{metadata.article_id}}/analysis.json
Content Type: application/json
Triggercontent eventExtract Dataanalyze contentExtract Contentparse JSONWrite S3{{category}}/analysis.json
Figure 9.Metadata-organized S3 — the agent classifies content by category, then writes it to S3 using metadata-derived paths.
TriggerPrompt Callgenerate reportWrite S3archive to bucketStreaming Result
Figure 10.Write S3 Archive — the agent processes content and archives the result to S3 for long-term storage.

Send Email Step

Sends an email notification. Use this to deliver reports, send alerts, or notify team members about agent results. By design it can only send to registered Seclai users in the account's organization — not arbitrary addresses. The one exception is reply_to_sender, which replies to the person who emailed an Email Received agent (voluntary opt-in). See Email → Who agents can email for the full policy and rationale.

Fields

FieldTypeRequiredDefaultDescription
recipient_user_idstringNoAccount owner / workspace default recipientOptional user ID to send the email to. If omitted, delivery uses the workspace default recipient (account owner for personal accounts). If provided for org accounts, it must be a valid member UUID.
reply_to_senderbooleanNofalseEmail Received agents only. When true, reply to the sender of the triggering email — the recipient is resolved at run time from the message's authenticated sender, so you never set an address yourself (recipient_user_id is ignored). Only sends when the inbound message passed SPF or DMARC; the step fails on any other trigger type or an unauthenticated sender. See Replying to the Sender.
reply_to_agent_inboxbooleanNofalseFor agents with an Email Received trigger. When true, sets the email's Reply-To to the agent's own inbox so the recipient's reply routes back to and re-triggers the agent. Use it on a recipient_user_id notification (not a reply) to enable a sustained back-and-forth; it has no effect if the agent has no email trigger. reply_to_sender already threads replies to the inbox, so don't combine them.
subjectstringYesThe email subject line. Supports substitutions.
html_bodystringNonullThe HTML email body. Supports substitutions. When omitted: if the input is HTML (text/html) it becomes the body; if the input is Markdown (text/markdown) it is rendered to HTML for the body; else if a text_body is set, that is rendered as the body; else if attachments are present (e.g. a for_each image loop) with no meaningful text, the body is a clean See attached.; otherwise the raw input is shown.
text_bodystringNonullThe plain text email body (fallback for email clients that don't support HTML). Supports substitutions.
attachmentslistNonullSelects which attachments are attached to the email as RFC-5322 attachments — a list of {{…}} attachment reference expressions (e.g. {{attachments[*.pdf]}}, {{step.report.attachments[0]}}). Null / empty list ⇒ every attachment on the step's input manifest. Total attachment size is capped at 25 MB (SES per-message limit); oversized runs fail loudly.

Branding: The sender display name, logo, accent color, and footer of agent-sent emails are configured once for the whole account on the Email Branding settings page (co-branding on Team and Pro plans; full white-label on Pro) — not per step. Outward agent emails also carry an unsubscribe footer so recipients can opt out of that agent, of all the account's agents, or of every Seclai email; opted-out recipients are skipped automatically.

Use Case Examples

Daily report delivery:

Subject: Daily Summary — {{date America/New_York}}
HTML Body:
<h1>Daily Summary</h1>
{{step.report.output}}
<p>Generated by {{agent.name}} at {{time America/New_York}}</p>

Alert notification:

Subject: Alert: {{metadata.alert_type}} detected
HTML Body:
<h2>Alert Details</h2>
<p><strong>Type:</strong> {{metadata.alert_type}}</p>
<p><strong>Source:</strong> {{metadata.source_url}}</p>
<p><strong>Details:</strong></p>
{{step.analysis.output}}
Text Body:
Alert: {{metadata.alert_type}} detected
Source: {{metadata.source_url}}
Details: {{step.analysis.output}}
Triggercontent eventExtract Datadetect alertGatealert detected?Send Emailalert notification
Figure 11.Alert email — the agent detects an anomaly and sends an alert notification via email.
TriggerPrompt Callgenerate reportSend Emaildeliver reportStreaming Result
Figure 12.Send Email Report — the agent generates a report and delivers it via email notification.

Inline images (CID embedding)

HTML email bodies can embed attachment images directly by referencing the attachment-placeholder grammar in an <img> src attribute:

<p>Today's chart:</p>
<img src="{{step.gen.attachments[0]}}" alt="chart" />
<img src="{{attachments[*.png]}}" alt="screenshots" />

Under the per-message budget (20 MB cumulative inline, 10 MB per image; SES caps the whole RFC-5322 envelope at 25 MB) each referenced image is CID-embedded — the <img src> is rewritten to a cid: reference and the image rides inside the message as a multipart/related part. The recipient's mail client renders it inline without an internet fetch.

When the cumulative budget would be exceeded, remaining <img src> placeholders are instead rewritten to an app.seclai.com link the recipient OAuth-authenticates against — they click → log in → see the image in their browser. Per-image overflow (>10 MB single image, >25 MB total) emits an EMAIL_ATTACHMENT_DROPPED_OVERSIZED event in the trace; the dropped attachment is silently omitted from the email.

Standalone attachments matches still go as RFC-5322 file attachments. If the same attachment is referenced inline AND matches the attachments references, it is CID-embedded only (no duplicate file attachment).

Automatic downscaling. To keep the message renderable, a large inline image (over ~250 KB) is downscaled to at most 1280 px and re-encoded as JPEG for the email copy only — the full-resolution original is untouched in storage (trace, file attachments, S3). Because JPEG has no transparency, a transparent PNG (logo, cut-out) is flattened onto a white background in the email. If you need pixel-exact or transparent images, send them as attachments (file attachments are not downscaled) rather than inline <img>.

Only raster images are inlined. image/png, image/jpeg, image/gif, and image/webp are CID-embedded; any other type (notably image/svg+xml) is routed to the OAuth-authenticated app.seclai.com link instead, so SVG is never embedded directly in the message.


Call Agent Step

Calls another agent as a sub-agent, running it synchronously and using its output as this step's output. Use this to compose complex workflows from smaller, reusable agents.

Fields

FieldTypeRequiredDefaultDescription
agent_idstringYesThe ID of the agent to call.
pass_inputbooleanNotrueWhen enabled, forwards this step's input as the called agent's input.
pass_metadatabooleanNotrueWhen enabled, forwards the parent agent run's metadata to the called agent.
content_version_idstringNonullAn optional content version ID to pass to the called agent for content-based triggers.
attachmentslistNonullSelects which attachments are propagated to the sub-agent's _seclai.input_attachments metadata — a list of {{…}} attachment reference expressions (e.g. {{attachments}}, {{step.gen.attachments[0]}}). The sub-agent's first prompt step receives the selected subset as if it were a dynamic_input upload. Null / empty list ⇒ every attachment on the step's input manifest.

Use Case Examples

Chain agents for multi-stage processing:

A summarization agent calls an analysis agent first, then processes its output:

Step 1: Call Agent (agent_id: "analysis-agent", pass_input: true, pass_metadata: true)
Step 2: Prompt Call — Summarize the analysis output
TriggerCall Agentanalysis agentPrompt Callsummarize outputStreaming Result
Figure 13.Agent chaining — the outer agent calls a specialized agent for analysis, then summarizes the result.

Fan-out to specialized agents:

Use parallel branches with call_agent steps to run multiple agents on the same input simultaneously:

Branch 1: Call Agent → "sentiment-agent"
Branch 2: Call Agent → "keyword-agent"
Merge: Combine results from both agents
TriggerCall AgentsentimentCall AgentkeywordsMergemerge resultsDisplay Result
Figure 14.Multi-agent fan-out: parallel call_agent steps invoke specialized agents, then a merge combines their results.

Important Considerations

  • Recursion protection: A maximum call depth is enforced to prevent infinite loops. If an agent calls itself (directly or indirectly), the run will fail once the depth limit is reached.
  • Synchronous execution: The called agent runs to completion before the parent continues. Long-running sub-agents will increase the overall run time.
  • Credit usage: Each sub-agent run consumes credits independently based on its own steps.

List Cloud Drive Folder Step

Lists the files in a folder on a connected cloud drive and emits a JSON array, one entry per file (name, path, file_id, size, is_folder, modified_at). Connect a drive under Integrations → Cloud Drives first (Dropbox is supported today). See the Cloud Drives guide for the full walkthrough and illustrated use cases.

Fields

FieldTypeRequiredDefaultDescription
cloud_drive_connection_iduuidYesThe connection to list files from.
folder_pathstringNo""The folder to list (blank lists the connection root). Supports substitutions.
recursiveboolNofalseList nested subfolders too. Pair with limit — a recursive listing is bounded by a system hard cap regardless.
limitintNonullCap on entries returned (1–1000). The result is hard-capped at 1000 either way.
path_patternstringNonullCase-insensitive glob (e.g. **/*.pdf) applied to the full path. Blank keeps everything.
content_typesstringNonullComma-separated MIME allowlist (application/pdf, image/*) matched against each file's guessed type.

Filenames are attacker-influenceable, so the listing is treated as untrusted — its output is prompt-injection scanned before any downstream prompt_call. Consume it with extract_content or for_each, not a prompt_call.

Use Case Example

Batch-process a folder: the listing is a JSON array, so a for_each consumes it directly — point its input at the list step's output and reference each file with {{step.<for_each_id>.item.<field>}}.

List Cloud Drive Folder (id: list): { connection: my-dropbox, folder_path: /reports }
  → For Each (id: each, input: {{step.list.output}}):   # iterates the JSON array, one file per item
      → Read Cloud Drive File: { file_path: {{step.each.item.path}} }
          → Prompt Call (id: analysis): "Analyze this report."
          → Write Cloud Drive File: { destination_path: /processed/{{step.each.item.name}}.txt, content: {{step.analysis.output}} }
TriggerList Cloud Drive Folder/reportsFor Eachper fileRead Cloud Drive Fileeach filePrompt CallanalyzeWrite Cloud Drive Filesave result
Figure 15.Batch processing — list a folder, then read each file, analyze it, and write the result back to a processed folder.

Read Cloud Drive File Step

Downloads a file by path and brings it into the workflow, mirroring Web Fetch. How the file arrives depends on its type:

  • Text files (.txt, .md, .csv, .json, HTML, …) are returned as text output (HTML keeps its content type for an extract_content step).
  • Images, audio, video, PDF, DOCX, XLSX are carried forward as a multimodal attachment for a downstream prompt_call on a vision/document model. Scannable text is also derived and travels alongside — documents/images via extraction (OCR), audio/video via transcription — so a text-only downstream model still gets the content and it is scanned.
  • Anything else (e.g. legacy .doc/.xls) has its text extracted and returned as text output.

Because derived text is always available, the file's content reaches downstream steps even when the model can't render the binary natively. See the Cloud Drives guide for connection setup, transcription, and credits.

Fields

FieldTypeRequiredDefaultDescription
cloud_drive_connection_iduuidYesThe connection to read the file from.
file_pathstringYesThe path of the file to read (e.g. /reports/summary.pdf). Supports substitutions.
max_file_mbintNonullPer-file size cap in MB (bounded by the 100 MB platform ceiling). A larger file is refused rather than truncated.

The read step ingests untrusted external content, so its output (including extracted text and transcripts) is prompt-injection scanned before a downstream prompt_call runs. Audio/video transcription is metered as TRANSCRIPTION credits by audio duration (the same rate as knowledge-base ingestion) and sends the file's audio to Seclai's transcription subprocessor — see Transcription.

Use Case Examples

Summarize a document: read a file and hand its attachment (plus extracted text) to a model.

Read Cloud Drive File: { connection: my-dropbox, file_path: /reports/{{metadata.file_name}} }
  → Prompt Call: "Summarize this document in five bullet points."
  → Display Result
TriggerRead Cloud Drive Fileget documentPrompt CallsummarizeStreaming Result
Figure 16.Summarize a drive document — read the file, hand the attachment (and its extracted text) to a prompt call, and stream the summary.

Transcribe a voice memo: pair a cloud file trigger on an audio folder with a read step so each recording is transcribed, summarized, and routed.

Read Cloud Drive File: { file_path: {{metadata.file_path}} }
  → Prompt Call: "Summarize this call and list action items."
  → Send Email: deliver the summary to the team
TriggerRead Cloud Drive Filetranscribe audioPrompt Callsummarize callSend Emailnotify teamStreaming Result
Figure 17.Transcribe a voice memo — the audio is transcribed on read, summarized by a prompt call, and emailed to the team.

Write Cloud Drive File Step

Writes content to a file on a connected drive — a delivery/sink step (like Write to S3) for the end of a branch. Missing parent folders are created as needed.

Fields

FieldTypeRequiredDefaultDescription
cloud_drive_connection_iduuidYesThe connection to write the file to.
destination_pathstringYesThe destination path (e.g. /reports/{{datetime UTC}}.txt). Missing parent folders are created. Supports substitutions.
contentstringNonullContent to write. Blank defaults to the step input (equivalent to {{input}}). Supports substitutions.
overwriteboolNotrueReplace an existing file at the destination. When false, a name clash auto-renames the new file.
max_file_mbintNonullSize cap in MB for the written content (bounded by the platform ceiling). A larger payload is refused.

Use Case Example

Save a generated report: produce content with a model, then write it to the drive.

Prompt Call: "Write a weekly status report from {{input}}."
  → Write Cloud Drive File: { connection: my-dropbox, destination_path: /reports/{{datetime UTC}}.md, content: {{step.report.output}} }
TriggerPrompt Callgenerate reportWrite Cloud Drive Filesave to drive
Figure 18.Save a generated report — a prompt call generates the content and the write step saves it to the connected drive.

Attachments references field

New to attachments? Start with the Attachments guide for the high-level model — where files come from, the two ways to reference them, and worked examples. This section is the complete field reference.

Every consumer step that can ingest files — Webhook Call, Write to S3, Send Email, Call Agent, Publish Content, Write Content Attachment, For Each, Display Result, and Streaming Result — exposes an optional attachments config field. It is a list of {{…}} reference expressions and is the mechanism for selecting which attachments the step consumes. Each expression resolves to zero or more files from a multi-asset manifest; the step unions the matches across all expressions in list order, then dedupes (first-seen wins).

The same {{…}} grammar already used inline in prompt templates, S3 keys, email HTML bodies, and webhook payload strings applies here:

ReferenceSelects
{{attachments}}All attachments on this step's input manifest.
{{attachments[0]}}This step's input attachment at the given zero-based index.
{{attachments[*.pdf]}}This step's input attachments whose filename matches the glob (* / ?).
{{agent.attachments[…]}}The agent's trigger-level uploads (requires a dynamic_input trigger). Same […] index / glob forms as above.
{{step.<id>.attachments[…]}}A specific upstream producer step's output files — a generate_image / generate_audio / generate_video, prompt_call, call_agent, or a for_each (which aggregates the media its body produced — reference the loop id from a post-loop step). Same […] index / glob forms as above.
{{step.<for_each_id>.item.attachment}}Inside a for_each running in iterate_attachments mode, the current iteration's file.

The filename glob in [*.pdf] / [*.png] forms uses fnmatch semantics (* matches any chars, ? matches one); literal strings reduce to an exact name match. The index in [0] forms is zero-based — a negative index is invalid and out-of-range indices match nothing.

Defaults: null / omitted / empty list ⇒ every attachment on the step's input manifest — except the result steps (display_result, streaming_result) and add_chat_turn, which default to text only and attach files only when at least one reference is set.

Zero matches resolve to a no-op (logged at INFO). Multi-match is normally allowed — the consumer iterates (S3 writes one object per match, webhook encodes a list, email attaches each file). The two single-attachment consumerswrite_content_attachment and publish_content — instead reject zero-or-more-than-one with a validation error; wrap them in a for_each with iterate_attachments=true to fan out.

Builder UI: the step editor's Attachments field has an Add Reference button that opens an expression row with a live match-count preview against the agent's most recent successful run. Drag-handles reorder references; the Delete button has a one-click undo banner.

Inline template references: the same {{attachments[…]}} / {{agent.attachments[…]}} / {{step.<id>.attachments[…]}} placeholder family also works inside prompt templates, S3 keys, email HTML bodies, and webhook payload strings. Used in the attachments field, an expression selects which attachments the consumer step ingests; used inline in a string, it injects one specific attachment reference into that string.


← Back to Agent Steps Overview