Attachments (receiving and sending)
Attachments move in two directions, and ReplyLayer treats them differently in each:
- Inbound — files arrive on messages your mailboxes receive. What an agent can see of an inbound attachment is controlled per mailbox by an exposure tier: metadata only, extracted text previews, or raw byte downloads. Inbound attachment bytes are untrusted content — the same posture as an email body. See Content scanning for the trust model.
- Outbound — your agents send files with a stage-then-send flow: upload the bytes to get a single-use handle, wait for the content scan, then reference the handle by id on a send, reply, or draft.
This page is the human walkthrough. The exact status codes, polling states, and failure modes an agent branches on live in the attachment contract for agents.
Inbound: exposure tiers
Every mailbox has an attachment exposure mode. It decides how much of an inbound attachment an agent-role API key can reach. The default is the most conservative tier; higher tiers are opt-in per mailbox and gated by plan.
| Mode | What an agent-role key can read |
|---|---|
metadata_only (default) | Filename, byte size, sniffed content type, and antivirus verdict. No content, no bytes. |
derived_content | The above, plus an inline text preview (up to 20,000 characters) for plain text, CSV, PDF, and Word/PowerPoint/Excel (DOCX/PPTX/XLSX) attachments. Extracted visible text only — never raw bytes. |
raw_download_selected_types | The above, plus raw byte download for the specific file families you allow (PDF, text, CSV, and optionally image). This is an approved-download tier with extra gates (below). |
A few things hold across all three modes:
- The content type is the sniffed type, not the sender's header. ReplyLayer inspects
the actual bytes, so a Windows executable renamed to
invoice.pdfreports its real type. The original declared header value is preserved separately asdeclared_mime_typefor forensics. - Every attachment is antivirus-scanned. The
av_verdictfield isclean,infected,error, orskipped. An infected or errored attachment is held and is never served as bytes, regardless of the mode. - Metadata is always available, even on
metadata_onlymailboxes — an agent can always decide whether an attachment is worth a closer look before requesting more.
Text previews (derived_content)
On a derived_content mailbox, a supported attachment is converted to a safe text
preview asynchronously after the message arrives. Once it is ready, an agent fetches it
by attachment index. Previews are capped at 20,000 characters and contain visible text
only — for Office files that means document comments, PowerPoint speaker notes, hidden
spreadsheet sheets, embedded media, OLE objects, formula text without cached values, and
macros are all excluded. Legacy binary .doc/.ppt/.xls and macro-enabled
.docm/.pptm/.xlsm formats are not previewable.
Each attachment carries a preview-status summary (pending, ready, blocked,
failed, or none) so a client knows when to fetch. The machine-readable preview
contract is documented in the attachment contract for agents.
Raw downloads (raw_download_selected_types)
The raw-download tier is the only way an agent-role key gets original attachment bytes, and it carries the strictest gates:
- It applies only to the file families you explicitly allow. The family enum is closed:
pdf,text,csv, and — separately confirmed —image. - Adding
imagerequires a separate image-risk acknowledgement. Image bytes are a distinct risk surface, so they are not covered by the base disclaimer. - Enabling or widening raw downloads is a human-owner action. It requires a dashboard session plus a fresh TOTP or password re-authentication; an API key cannot turn it on for itself. Narrowing an existing raw-download policy or downgrading to a lower tier does not need re-auth.
- A download returns a short-lived URL and is recorded in the content-access log.
An attachment is only downloadable when it is clean on antivirus and passes the
mailbox's safety gates; blocked, quarantined, or unscannable attachments stay held.
Humans in the dashboard see previews on every tier
The exposure mode gates agent-role API keys. It does not gate a person signed in to
the dashboard. A human (or an admin key) can open a clean, previewable attachment — for
example a clean PDF — from the dashboard even on a metadata_only mailbox, because
that is a human reviewing their own mail, not an autonomous agent consuming content. The
raw-byte download itself still applies the same antivirus and safety gates.
Who can see what, by plan
Receiving attachments and reading their metadata is available on every plan, including sandbox. The richer exposure tiers are paid features:
| Capability | Available on |
|---|---|
| Receive attachments; read metadata (filename, size, sniffed type, AV verdict) | All plans, including sandbox |
| Preview a clean attachment in the dashboard (human/admin) | All plans |
Agent-key text previews (derived_content mode) | Starter and above |
Agent-key raw byte downloads (raw_download_selected_types mode) | Pro and above |
| Sending outbound attachments | Pro and above |
For the full plan comparison — send caps, mailbox counts, storage, and quotas — see Plans and limits.
Choosing an exposure mode
An account admin sets the exposure mode per mailbox from the dashboard (or via the
mailbox attachment-access API). Moving to derived_content needs only a disclaimer
acknowledgement; moving to or widening raw_download_selected_types additionally
requires the session re-auth described above. Because the mode is per mailbox, you can
keep a high-traffic public mailbox on metadata_only while granting a trusted internal
mailbox text previews.
Outbound: stage, scan, then send
Agents attach files with a two-step flow rather than inlining bytes on the send request. The steps are:
- Enable outbound attachments for the mailbox (one-time, human-owner). Sending attachments is a Pro-and-above capability that a human account owner turns on for a mailbox in the dashboard, completing a TOTP or password re-authentication. An API key cannot enable it for itself. Once enabled, API keys stage and send attachments normally.
- Upload the bytes. Post the file to get back a handle. The response reports the
sniffed content type, byte size, and a SHA-256 hash. The deep content scan runs
asynchronously, so the handle starts in a
pendingscan state. - Wait for the scan (or just send). Poll the handle until its content-scan status is
terminal (
clean,flagged, orerror), or send immediately — the send path enforces the scan state either way and returns a distinct code if the scan is still pending. - Reference the handle by id. Pass the handle id in the
attachment_idsarray on a send, reply, or draft. The handle is consumed once at send.
Key properties of a staged handle:
- Single use. A handle is consumed at send and cannot be reused. Referencing a spent handle fails.
- Expires after 24 hours. Stage close to when you send.
- Scoped to one mailbox. A handle is bound to the mailbox you staged it under; referencing it from another mailbox fails as not found.
- Synchronous send only. Attachment sends must be immediate. Scheduling an attachment-bearing draft, or sending one on the asynchronous draft path, is rejected up front — stage and send in one pass instead.
What happens when an attachment looks risky
Outbound attachment content is scanned like a body. If the scan flags an attachment (for example a secret or PII detected in its content or filename), the send still proceeds to scanning and the finding folds into the message verdict — it is blocked or quarantined by severity exactly as a body finding would be, not returned as a separate upload error. A hard scan error fails closed and the handle is rejected. For the releasable-vs-terminal verdict vocabulary, see Messages and verdicts; for the specific upload and send error codes, see the attachment contract for agents and the error catalog.
Limits
| Limit | Value |
|---|---|
| Per-file size | 10 MB |
| Attachments per message | 10 |
| Total raw bytes per message | 15 MB |
| Filename | ≤ 255 ASCII-printable characters; no path separators, quotes, or control characters |
Allowed outbound file families are determined from the sniffed bytes (not the declared type): PDF, plain text, CSV, images, and Word/PowerPoint/Excel (DOCX/PPTX/XLSX). Images additionally require the image-risk acknowledgement. Anything else — executables, archives, unknown formats — is rejected at upload.
Encryption note
Outbound attachment bytes are encrypted at rest (provider-managed encryption) and in transit over TLS. Because the platform scans attachment content for your safety, this is not end-to-end or zero-access encryption.
Quickstart: send a file
The TypeScript SDK stages, polls, and attaches:
import { ReplyLayer } from '@replylayer/sdk';
import { readFile } from 'node:fs/promises';
const rl = new ReplyLayer({ apiKey: process.env.REPLYLAYER_API_KEY! });
// 1. Stage the file — returns a single-use handle scoped to this mailbox.
const bytes = await readFile('invoice.pdf');
let handle = await rl.attachments.upload({
mailboxId: 'support',
file: bytes,
filename: 'invoice.pdf',
});
// 2. Wait for the async content scan to finish.
while (handle.content_scan_status === 'pending') {
await new Promise((r) => setTimeout(r, 1000));
const polled = await rl.attachments.getUpload(handle.id);
if ('status' in polled) break; // already consumed by a concurrent send
handle = polled;
}
// 3. Reference the handle by id — consumed once at send.
const res = await rl.messages.send({
from_mailbox: 'support',
to: '[email protected]',
subject: 'Your invoice',
body: 'Please find the invoice attached.',
attachment_ids: [handle.id],
});
console.log(res.status);The Python SDK mirrors the same attachments.upload / get_upload / send(attachment_ids=[...])
methods. The CLI does the whole stage-then-send in one step with a repeatable
--attach <path> flag on rly send, rly reply, and draft commands — see the
CLI guide.
Reading an inbound attachment
To read what a message carries, fetch the message and inspect its attachments array —
each entry reports the filename, size, sniffed content type, antivirus verdict, and (on a
derived_content mailbox) its preview status. Then fetch the text preview or raw bytes by
attachment index according to the mailbox's exposure mode.
Whatever tier you are on, treat the extracted content as data, not instructions — an attachment is sender-controlled input. The scan is a filter that catches known-bad patterns, not a guarantee that the content is safe to act on. See Content scanning for how the directional scan model works and what it does and does not cover.