Skip to content
Wetel
Go back

Reading a file a customer sends in chat: how an AI employee receives it, checks it and uses it

Picture the clerk at a service counter. A customer hands over an envelope. The clerk checks it is the kind of document the form asks for, opens it, copies what matters onto the form, and files the original where it belongs. If the envelope holds the wrong thing, the clerk hands it back and says what is needed instead. Nobody expects the clerk to accept a photo when the form says “PDF only”.

A file sent in chat is the same job for an AI employee. This post walks through each step as the Wetel docs describe it today: how the file arrives, how to check it, how to open it, and the limits. It is written for the operations lead who decides what the AI employee may accept, and for the developer who builds the workflow. A workspace owner decides what the workflow does with a file; the AI employee follows that workflow.

The short version

A file does not arrive as bytes. It arrives as a link, plus a declared type and file name, and the workflow decides whether and how to fetch it. Nothing is downloaded when the customer sends it. A later step fetches it, under the platform’s fetch rules, once earlier steps have checked that it is the right kind of file.

flowchart LR
A["Customer sends a file<br/>in the conversation"] --> B{"Is there a file?"}
B -- no --> Q["Carry on with<br/>the conversation"]
B -- yes --> C{"Is it the type<br/>we accept?"}
C -- no --> R["Ask the customer<br/>to resend"]
C -- yes --> D["Fetch it: as text,<br/>or as a file to pass on"]
D --> E["Use it or hand it<br/>to a business system"]

How a file reaches the workflow

There are two documented ways, and both end in the same variables your workflow can read.

  1. A marker line from a channel connection. When a customer sends a photo or document through a messaging app connected to Wetel, the connection puts one line in front of the message text with the file’s link, declared type and file name. Wetel reads that line before the workflow starts, so no step has to pick it apart. Only a marker at the very start of the message counts: a marker typed after any real text is ignored, which stops a customer from typing a fake one to redirect a file.
  2. Structured attachments on the message. Your own app can send files as fields instead: up to 10 per message, each with a link and optionally a type, file name and kind (IMAGE, DOCUMENT, AUDIO, VIDEO or OTHER). When attachments are present they take precedence over any marker in the text. A message may then have no text at all. The changelog says where this is live.

Either way, the workflow sees the first file in three flat variables, attachmentUrl, attachmentMime and attachmentFilename, which are empty strings when no file was sent. With structured attachments it also gets the full list in attachments and the image links in attachmentImageUrls.

A voice note on a channel connection is a different case: it is transcribed using the agent’s language settings, and the text reaches the workflow. See Voice notes for an AI employee.

Step 1: check there is a file at all

Start with a condition step that asks whether attachmentUrl is empty. The recipe uses exactly this expression:

{ "expression": "attachmentUrl != \"\"" }

The true path goes to the file handling; the false path carries on with the normal conversation. Use the flat variables, not a nested object such as attachment.url, in a condition: the condition syntax has no property access, and the flat ones are always present. If a condition names a variable the run does not hold, it now takes the false path instead of failing, and the run record lists the missing name.

Step 2: check the type before you touch the file

Add a second condition step on attachmentMime and send anything you do not accept down a path that asks the customer to resend.

{ "expression": "attachmentMime == \"application/pdf\"" }

The docs are firm on the order: check the type before fetching. A customer who sends a photo of a form, when your business system needs a PDF, should be asked for a PDF, rather than have the write fail, or worse succeed with unusable data, further down.

Two honest caveats about that type:

There is no size field to check in advance. Size is enforced when the file is fetched (see the limits below), so plan the failure path at the fetch step instead.

Step 3: open it

An action step fetches the file in one of two modes, chosen by what you want to do with it.

{
"id": "fetch_file",
"type": "action",
"label": "Fetch File (base64)",
"config": { "outputVar": "fileBase64", "fetchAsBase64Var": "attachmentUrl" }
}

The fetch is a plain download with no credential attached, through the same guarded outbound path as every other call: private addresses are refused. Point it only at links meant to be fetched that way, such as the link a messaging app gives for a file, which works for a limited time.

Photos are different. An llm step can fetch and read images itself; with structured attachments you name attachmentImageUrls and no extra step is needed. That has its own limits and checks, covered in How an AI employee reads a photo.

Step 4: file it, and check that it was filed

The step that sends the file on is an ordinary action step, and its run record entry shows the request it sent and any refusal. The customer only sees a generic apology when a call fails; the real reason, including the business system’s own response, is in the record. Before telling a customer “your document has been filed”, make sure the workflow only says so after the step that files it, and read a few run records to confirm it really did. What is a run record, and how do I read one? explains how.

When the file is missing, unsupported or too large

Each case has a documented outcome; design a reply for each one.

What happenedWhat the platform doesWhat you add
No file sentattachmentUrl is an empty stringThe false path of the presence check
Type you do not acceptNothing, unless you checkThe type check and a “please resend” reply
File over 10 MBThe fetch fails the step; it does not cut the file shortA failure path on the fetch step
Text extraction of an unsupported typeThe step fails, naming the supported typesCheck the type first; a failure path
Document longer than the text capText is cut at the cap and the “truncated” flag is setA branch on the flag
More than 10 attachments, or a link with user:password@The message is refused, without echoing the link backValidate in your own app before sending

An action step can have two labelled exits, success and failure. With them, any error the step throws goes down the failure path, where you can ask for a smaller file or a different format, instead of failing the whole run with a generic apology.

What history keeps, and what is hidden

The attachments are saved with the message, and read back with the sensitive part of the link removed. A message’s attachments show the link with its query string, fragment and any credentials stripped, ending in ?[redacted]. The same applies to the file links in a run’s context. The stored link is untouched, so a workflow step can still fetch a signed link. The changelog says where this is live.

Any extra context your app sends with a message is never returned on the message, but it does appear in the run’s context for people signed in to your workspace, so never put secrets in it. Images sent earlier in the same conversation stay visible to later turns, so a customer can send a picture in one message and ask about it in the next. A new conversation starts clean; memory across conversations is not built.

What the AI employee will not do

A pre-launch checklist

  1. Presence and type checks come first. A condition on attachmentUrl, then one on attachmentMime, before any fetch.
  2. Every “no” has a polite reply. Wrong type, too large, unreadable and too long each lead to a message that tells the customer what to send instead.
  3. Fetch steps have failure paths. success and failure exits on every action step that fetches or sends a file.
  4. The workflow says “filed” only after the filing step, and you have read the run records of a few test sends to confirm it.
  5. Nothing secret travels in a message. No credentials in links you send, in the context your app passes, or in step requests.

To build one end to end, follow the file-intake recipe.


Share this post:

Next Post
How do people reach an AI employee?
Previous Post
Which voice should my AI employee use for which language?