Documentation Index Fetch the complete documentation index at: https://mintlify.com/vercel/chat/llms.txt
Use this file to discover all available pages before exploring further.
File Attachments
The Chat SDK supports sending files and images, and reading attachments from incoming messages.
Sending Files
Attach files when posting messages:
import { readFile } from "fs/promises" ;
const fileData = await readFile ( "./report.pdf" );
await thread . post ({
raw: "Here's the report you requested" ,
files: [
{
filename: "report.pdf" ,
data: fileData ,
mimeType: "application/pdf" ,
},
],
});
FileUpload Interface
interface FileUpload {
filename : string ; // File name with extension
data : Buffer | Blob | ArrayBuffer ; // Binary data
mimeType ?: string ; // MIME type (inferred if not provided)
}
Sending Images
Images are uploaded the same way:
const imageData = await readFile ( "./chart.png" );
await thread . post ({
markdown: "Here's the sales chart" ,
files: [
{
filename: "sales-chart.png" ,
data: imageData ,
mimeType: "image/png" ,
},
],
});
Multiple Files
Send multiple files in one message:
await thread . post ({
raw: "Quarterly reports" ,
files: [
{
filename: "q1-report.pdf" ,
data: await readFile ( "./q1.pdf" ),
},
{
filename: "q2-report.pdf" ,
data: await readFile ( "./q2.pdf" ),
},
{
filename: "summary.xlsx" ,
data: await readFile ( "./summary.xlsx" ),
},
],
});
Files with Cards
Attach files to card messages:
import { Card , Text , Fields , Field } from "chat" ;
await thread . post ({
card : (
< Card title = "Monthly Report" >
< Fields >
< Field label = "Period" value = "March 2024" />
< Field label = "Status" value = "Completed" />
</ Fields >
< Text > See attached PDF for full details. </ Text >
</ Card >
),
files: [
{
filename: "march-report.pdf" ,
data: reportData ,
},
],
});
Reading Attachments
Access attachments from incoming messages:
interface Message {
attachments ?: Attachment [];
}
interface Attachment {
type : "image" | "file" | "video" | "audio" ;
name ?: string ; // Filename
url ?: string ; // URL to the file
mimeType ?: string ; // MIME type
size ?: number ; // File size in bytes
width ?: number ; // Image/video width
height ?: number ; // Image/video height
data ?: Buffer | Blob ; // Binary data (if already fetched)
// Fetch the file data
fetchData ?: () => Promise < Buffer >;
}
Example: Processing Image Attachments
chat . onNewMessage ( /analyze/ , async ( thread , message ) => {
const imageAttachments = message . attachments ?. filter (
( a ) => a . type === "image"
);
if ( ! imageAttachments || imageAttachments . length === 0 ) {
await thread . post ( "Please attach an image to analyze." );
return ;
}
for ( const image of imageAttachments ) {
// Download the image
const imageData = image . fetchData
? await image . fetchData ()
: image . data ;
if ( imageData ) {
// Process the image
const analysis = await analyzeImage ( imageData );
await thread . post (
`Image: ${ image . name } \n Analysis: ${ analysis } `
);
}
}
});
Fetching Private Files
Some platforms (like Slack) require authentication to download files:
chat . onSubscribedMessage ( async ( thread , message ) => {
if ( message . attachments ) {
for ( const attachment of message . attachments ) {
if ( attachment . fetchData ) {
// Adapter handles authentication automatically
const data = await attachment . fetchData ();
// Process the file
await processFile ( attachment . name , data );
}
}
}
});
Access file information without downloading:
chat . onNewMessage ( / . * / , async ( thread , message ) => {
if ( message . attachments ) {
for ( const file of message . attachments ) {
console . log ( `File: ${ file . name } ` );
console . log ( `Type: ${ file . type } ` );
console . log ( `MIME: ${ file . mimeType } ` );
console . log ( `Size: ${ file . size } bytes` );
console . log ( `URL: ${ file . url } ` );
}
}
});
MIME Types
Common MIME types:
const mimeTypes = {
// Images
png: "image/png" ,
jpg: "image/jpeg" ,
jpeg: "image/jpeg" ,
gif: "image/gif" ,
webp: "image/webp" ,
svg: "image/svg+xml" ,
// Documents
pdf: "application/pdf" ,
doc: "application/msword" ,
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ,
xls: "application/vnd.ms-excel" ,
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ,
ppt: "application/vnd.ms-powerpoint" ,
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation" ,
// Text
txt: "text/plain" ,
csv: "text/csv" ,
json: "application/json" ,
xml: "text/xml" ,
// Archives
zip: "application/zip" ,
tar: "application/x-tar" ,
gz: "application/gzip" ,
};
Example: File Upload Bot
Collect files and generate a summary:
import { Chat , Card , Text , Table , emoji } from "chat" ;
const chat = new Chat ({ /* ... */ });
chat . onSlashCommand ( "/upload-files" , async ( event ) => {
await event . channel . post (
"Please upload the files you want to process (attach them to your next message)."
);
await event . channel . subscribe ();
});
chat . onSubscribedMessage ( async ( thread , message ) => {
if ( ! message . attachments || message . attachments . length === 0 ) {
return ;
}
const fileInfo = message . attachments . map (( file ) => [
file . name || "unnamed" ,
file . type ,
` ${ Math . round (( file . size || 0 ) / 1024 ) } KB` ,
]);
await thread . post (
< Card title = { ` ${ emoji . package } Files Received` } >
< Text > Processing {message.attachments. length } file ( s ) ...</ Text >
< Table
headers = { [ "Filename" , "Type" , "Size" ]}
rows={fileInfo}
/>
</Card>
);
// Process each file
for (const file of message.attachments) {
if (file.fetchData) {
const data = await file.fetchData();
await processUploadedFile(file.name, data);
}
}
await thread.post(`${emoji.check} All files processed successfully!`);
await thread.unsubscribe();
});
Slack
✅ Upload files via API
✅ Download files with authentication
✅ Image, video, audio, and document attachments
Microsoft Teams
✅ File attachments in cards
✅ Download via public URLs
⚠️ Upload requires additional setup
Google Chat
✅ Image attachments in cards
✅ Download via public URLs
⚠️ Limited file upload support
File Size Limits
Each platform has different file size limits:
Slack : 1 GB (free tier: varies)
Microsoft Teams : 250 MB
Google Chat : 200 MB
Always handle file size validation:
const MAX_FILE_SIZE = 10 * 1024 * 1024 ; // 10 MB
await thread . post ({
raw: "Here's the file" ,
files: files . filter (( f ) => {
if ( f . data . byteLength > MAX_FILE_SIZE ) {
console . warn ( `File ${ f . filename } exceeds size limit` );
return false ;
}
return true ;
}),
});
Complete Example: Document Processor
import { Chat , Card , Text , Actions , Button , emoji } from "chat" ;
import { readFile } from "fs/promises" ;
const chat = new Chat ({ /* ... */ });
// Upload document
chat . onAction ( "upload_doc" , async ( event ) => {
const docData = await readFile ( "./document.pdf" );
await event . thread . post ({
card : (
< Card title = "Document Ready" >
< Text > Your processed document is attached . </ Text >
</ Card >
),
files: [
{
filename: "processed-document.pdf" ,
data: docData ,
mimeType: "application/pdf" ,
},
],
});
});
// Process uploaded files
chat . onNewMention ( async ( thread , message ) => {
const attachments = message . attachments || [];
const pdfFiles = attachments . filter (
( a ) => a . mimeType === "application/pdf"
);
if ( pdfFiles . length === 0 ) {
await thread . post (
` ${ emoji . warning } Please attach a PDF file.`
);
return ;
}
await thread . post (
` ${ emoji . hourglass } Processing ${ pdfFiles . length } PDF(s)...`
);
for ( const pdf of pdfFiles ) {
if ( pdf . fetchData ) {
const data = await pdf . fetchData ();
const result = await processPDF ( data );
await thread . post (
< Card title = { ` ${ emoji . check } ${ pdf . name } Processed` } >
< Text > Pages : {result. pageCount } </ Text >
< Text > Text extracted : {result. textLength } characters </ Text >
< Actions >
< Button id = "download_text" value = {pdf. name } >
Download Text
</ Button >
</ Actions >
</ Card >
);
}
}
});
Next Steps
Cards Display file metadata in rich cards
Error Handling Handle file upload and download errors