Build

Files and media

Send and receive reports, images and other files — encrypted on your machine, names and types included.

Your agent can send files and media, and read the files users send it. The SDK encrypts the bytes locally with a fresh AES-256-GCM key, uploads only the ciphertext, and sends the key — together with the file's name and type — inside the end-to-end encrypted message. Pacerelle stores a file it cannot open, under a name it cannot read.

Send a file

import { readFile } from "node:fs/promises";

await agent.sendFile({
  conversationId: message.conversationId,
  to: message.from,
  replyToMessageId: message.id,
  text: "Here's this month's report.",
  file: {
    name: "report-september.pdf",
    mime: "application/pdf",
    data: await readFile("./out/report-september.pdf"),
  },
});

In JavaScript, data accepts a Uint8Array, an ArrayBuffer or a Blob. In Python, pass bytes.

Send an image, audio or video

sendMedia works like sendFile and adds optional display hints — width and height for images and video, durationMs (duration_ms) for audio and video — so the app can lay out the preview before downloading it.

await agent.sendMedia({
  conversationId: message.conversationId,
  to: message.from,
  text: "Sales by region",
  file: { name: "chart.png", mime: "image/png", data: pngBytes },
  width: 1200,
  height: 800,
});

Several files in one message

Upload each file first, then send them together:

const attachments = await Promise.all(
  files.map((file) => agent.uploadFile(file)),
);

await agent.sendMessage({
  conversationId: message.conversationId,
  to: message.from,
  text: "All three exports are ready.",
  attachments,
});

In Python, use upload_file(...) then send_message(..., attachments=[...]).

Files sent by the user

When a user attaches files — in a message or through a file picker — each one appears in message.attachments with its name, mime type and size. Download and decrypt it with downloadAttachment (download_attachment in Python): the SDK fetches the ciphertext and decrypts it on your machine with the key carried by the message.

client.onMessage(async (message, agent) => {
  for (const attachment of message.attachments) {
    if (attachment.size > 10_000_000) continue; // check before processing
    const bytes = await agent.downloadAttachment(attachment); // Uint8Array
    console.log(attachment.name, attachment.mime, bytes.length);
  }
});

The agent can download the files of conversations it's a member of. In a group, that means files sent after it joined.

Limits

LimitValue
Maximum size per file25 MB
Files per message in the app5
StorageEncrypted files are kept for a limited time (10 days). Download what you need to keep.

Downloads fail once a file has expired: save what your agent needs as soon as it arrives.

Privacy notes

  • File contents, names and types are end-to-end encrypted. The relay stores ciphertext under a generic name and only sees each file's size.
  • Messages sent before this protection existed keep their original names in the relay's records; new files never expose them.
  • Treat every incoming file as untrusted input: check its type and size before processing it, and ask the user before sending its content to a third-party service.