Facio's Message & Upload Tools: How AI Agents Deliver Files and Conversations Across Any Channel
An AI agent that produces a perfect report but cannot deliver it to a human is a journal, not a worker. The report is written, the analysis is complete, the assets are generated — and then everything stops at the filesystem. The human has to know the file exists, navigate to the workspace, open the right folder, and read the output. Every step is friction. Every friction point is a chance for the deliverable to be missed.
Facio's message and upload_file tools close the delivery loop. The agent creates the output, attaches the files, and sends a complete message — text, images, videos, documents — to the human's channel of choice. Placet.io, Telegram, Discord, or a custom integration. Here's how the delivery architecture works.
The Two Delivery Primitives
Facio splits delivery into two complementary tools:
upload_file for the file itself. The tool takes local file paths or HTTP(S) URLs and uploads them to Placet, returning attachment IDs that can be referenced in subsequent messages. Use this when you need to reference the same file from multiple messages, or when the file is too large to inline-deliver.
message for the conversation. The tool takes text content, optional file attachments, and a target channel — then delivers the message with the files attached. The agent calls this once at the end of a workflow to deliver results.
The split mirrors how messaging platforms actually work: a file is uploaded to a server, then referenced by ID in a message. Facio's two-tool design matches that model, with the agent calling the right tool for the right job.
# Standalone upload — get an attachment ID for reuse
attachment_id = upload_file(files=["output/hero-image.png"])
# Returns: "att_abc123..."
# Send a message with the file attached
message(
content="Here's the hero image for review.",
media=["output/hero-image.png"]
)
# Returns: "Message sent to placet:channel-id"
The agent doesn't have to call both tools. For a one-shot delivery with a single file, message with the media parameter handles everything — the file is uploaded automatically, the message is composed, the delivery is confirmed.
Multi-Channel Delivery
Facio's message tool doesn't lock agents to a single platform. The same message call can target any configured channel:
message(content="...", media=["..."], channel="placet")
message(content="...", media=["..."], channel="telegram")
message(content="...", media=["..."], channel="discord")
The agent picks the channel based on the audience. A product launch announcement might go to Telegram. An internal status update goes to Placet. A community question goes to Discord. The content is the same; the delivery path differs.
This is the same channel-agnostic principle that makes the HITL pipeline work: the human sees approval requests in their normal messaging interface, not in a separate agent dashboard. The same applies to outbound messages — the agent delivers to wherever the human already is.
The Media Parameter: Native File Attachments
The media parameter accepts local file paths, and Facio handles the rest:
message(
content="Blog post published. Hero image attached for reference.",
media=[
"output/blog-posts/2026-06-11-hitl-tools/hero.png",
"output/blog-posts/2026-06-11-hitl-tools/hero-mobile.png"
]
)
Multiple files can be attached in a single message. The files are uploaded automatically — no manual CDN upload, no separate file hosting step. The platform handles:
- MIME type detection — image, video, document, audio
- Upload to platform storage — with the file ID returned for the message
- Inline display — images and videos render in the chat, not as separate file links
- Download links — for documents the recipient can download later
This matters because file delivery is one of the most failure-prone parts of agent workflows. A generated PDF that doesn't render, a hero image that arrives as a broken link, a video that's the wrong format — all common with manual delivery. Facio's native media handling eliminates these failure modes.
When to Use message vs. upload_file
The decision rule:
| Scenario | Right tool |
|---|---|
| Send a one-time message with attached files | message(media=[...]) |
| Reference the same file from multiple messages | upload_file first, then message(attachment_ids=[...]) |
| Pre-stage files for a future batch of messages | upload_file once, reuse the attachment IDs |
| Send a status update with no files | message(content="...") |
| Upload a file for external use (e.g., a blog hero) | upload_file returns an ID usable outside Facio |
For most agent workflows, message alone is sufficient. The agent produces output, attaches the relevant files, and sends the result. upload_file is for the more sophisticated case where files need to be referenced multiple times or pre-staged for later use.
Dynamic Channel Management with manage_channel
Beyond the message tool's built-in channel targeting, Facio has a manage_channel tool for dynamic channel operations:
# Create a new channel for a specific project
manage_channel(action="create", name="Project Alpha Updates", tag="projects")
# List all channels
manage_channel(action="list")
# Rename a channel
manage_channel(action="rename", name="old-name", new_name="new-name")
# Archive a channel that's no longer needed
manage_channel(action="update", name="Project Alpha", description="Archived 2026-06-11")
The agent can create channels on-demand as projects emerge, route messages to the right audience, and archive channels when work is complete. The channel structure isn't a static config — it evolves with the work.
The reply Style Discipline
Facio's runtime includes a deliberate discipline around how agents use the message tool:
- Placet shows tool status automatically. The agent doesn't need to send status messages like "Starting research..." or "Processing..." — the UI handles that.
- Reply directly with text for conversations. The
messagetool is for delivering results, not for narrating progress. - Use
messagewithmediaonly for actual file delivery. Not for inline status updates.
This is why the post you're reading now ends with a brief German summary to the user, not a long status report. The message tool delivers the deliverable, the tool status UI handles the rest.
Production Patterns
Pattern 1: Multi-Asset Content Delivery
After generating a blog post with multiple media variants:
message(
content="Blog post published with hero image. Three format variants attached for platform-specific distribution.",
media=[
"output/blog-posts/2026-06-11-post/hero-1536x1024.png", # 16:9 for LinkedIn
"output/blog-posts/2026-06-11-post/hero-1024x1024.png", # 1:1 for Instagram
"output/blog-posts/2026-06-11-post/hero-1024x1536.png" # 2:3 for Stories
],
channel="placet"
)
One message, three file variants, ready for the human to download and distribute to each platform.
Pattern 2: Status Update with Audit Trail Attachment
A heartbeat task reports system health:
message(
content="Daily system health report. All checks passed. Detailed log attached.",
media=["tmp/health-report-2026-06-11.txt"],
channel="placet"
)
The message body is a brief summary (humans scan, they don't read), the file attachment is the detailed report (humans download it when they need depth).
Pattern 3: Video Generation Delivery
After generating a teaser video:
message(
content="Product launch teaser video (6 seconds, 1920x1080). Review before publishing.",
media=["output/videos/launch-teaser-v3.mp4"]
)
The video renders inline in Placet's video player. The human can watch without downloading, then approve or reject via the standard HITL flow.
File Size and Format Considerations
The message tool handles a wide range of file types natively:
| Type | Examples | Behavior |
|---|---|---|
| Images | PNG, JPEG, WebP | Inline display, can be saved by recipient |
| Videos | MP4, MOV, WebM | Inline player with play controls |
| Audio | MP3, WAV, OGG | Inline player |
| Documents | PDF, DOCX, XLSX, PPTX | Inline preview for supported formats, download link |
| Code | .py, .js, .ts, .md | Inline display with syntax highlighting where supported |
For very large files (hundreds of MB), the upload may take longer but still completes. For files that exceed the platform's inline preview limit, the recipient gets a download link.
What message Doesn't Do
- No message editing. Once sent, a message is final. For corrections, send a follow-up message.
- No scheduled sending. The
crontool handles scheduled message delivery — the agent composes the message, the cron job fires it at the scheduled time. - No message history access. The
messagetool sends, it doesn't read past conversations. For reading message history, the agent usesread_logsor platform-specific APIs. - No rich UI components (buttons, forms). For interactive messages, the agent uses the HITL tools (
ask_approval,ask_form,ask_selection) which deliver to Placet as structured cards. Themessagetool is for narrative delivery, not interactive workflows.
Bottom Line
An AI agent that can produce text, images, videos, and documents but cannot deliver them is only half an agent. The work is done but never reaches the human. The deployment never ships because the report never arrives. The launch never happens because the assets never get to the marketing team.
Facio's message and upload_file tools close that loop. The agent creates the output, attaches the files, and delivers the complete deliverable — text, images, videos, documents — to the human's channel of choice. Native file attachments, multi-channel routing, and the right defaults for production workflows.
Because an agent that can write the report but can't send the report is a tool, not a teammate.
See the delivery tools documentation for channel configuration, media handling, and multi-format attachment patterns.