Overview
WAPI provides simple methods for working with media messages. You can send various media types and download incoming media files using the Context class methods.
Send Images Share photos with captions and options
Send Videos Send video files with GIF playback support
Send Audio Share voice messages and audio files
Send Stickers Send custom WebP stickers
Sending Images
The replyWithImage() method allows you to send images from URLs or Buffer objects:
From URL
From Buffer
With Options
bot . command ( "photo" , async ( ctx ) => {
const imageUrl = "https://example.com/photo.jpg" ;
await ctx . replyWithImage ( imageUrl , {
caption: "Here's your photo!"
});
});
import fs from "fs" ;
bot . command ( "localphoto" , async ( ctx ) => {
const imageBuffer = fs . readFileSync ( "./images/photo.jpg" );
await ctx . replyWithImage ( imageBuffer , {
caption: "Photo from local storage"
});
});
bot . command ( "secretphoto" , async ( ctx ) => {
const imageUrl = "https://example.com/photo.jpg" ;
await ctx . replyWithImage ( imageUrl , {
caption: "This photo will disappear!" ,
viewOnce: true , // View once message
mimetype: "image/jpeg"
});
});
The replyWithImage() method (from context.ts:41-68) accepts either a URL string or a Buffer. When using URLs, WhatsApp downloads the image directly. The default mimetype is image/jpeg.
Image Options
Available options for replyWithImage():
Option Type Description captionstringText caption for the image mimetypestringMIME type (default: image/jpeg) viewOncebooleanMake it a view-once message mentionsstring[]JIDs to mention in caption
Sending Videos
The replyWithVideo() method works similarly to images but supports video files:
Video File
GIF Playback
From Buffer
bot . command ( "video" , async ( ctx ) => {
const videoUrl = "https://example.com/video.mp4" ;
await ctx . replyWithVideo ( videoUrl , {
caption: "Check out this video!"
});
});
bot . command ( "gif" , async ( ctx ) => {
const gifUrl = "https://example.com/animation.mp4" ;
await ctx . replyWithVideo ( gifUrl , {
caption: "Funny GIF!" ,
gifPlayback: true // Display as GIF in WhatsApp
});
});
import fs from "fs" ;
bot . command ( "localvideo" , async ( ctx ) => {
const videoBuffer = fs . readFileSync ( "./videos/clip.mp4" );
await ctx . replyWithVideo ( videoBuffer , {
caption: "Local video file" ,
mimetype: "video/mp4"
});
});
Use the gifPlayback: true option to make videos play as GIFs in WhatsApp without sound and with automatic looping.
Video Options
Available options for replyWithVideo():
Option Type Description captionstringText caption for the video mimetypestringMIME type (default: video/mp4) viewOncebooleanMake it a view-once message gifPlaybackbooleanDisplay as GIF (no sound, loops) mentionsstring[]JIDs to mention in caption
Sending Audio
The replyWithAudio() method sends audio files and voice messages:
bot . command ( "audio" , async ( ctx ) => {
const audioUrl = "https://example.com/song.mp3" ;
await ctx . replyWithAudio ( audioUrl );
});
import fs from "fs" ;
bot . command ( "voice" , async ( ctx ) => {
const audioBuffer = fs . readFileSync ( "./audio/voice.mp3" );
await ctx . replyWithAudio ( audioBuffer , {
mimetype: "audio/mpeg"
});
});
The replyWithAudio() method (from context.ts:99-126) defaults to audio/mpeg mimetype. For voice messages, WhatsApp automatically detects the audio waveform.
Audio Options
Available options for replyWithAudio():
Option Type Description mimetypestringMIME type (default: audio/mpeg) viewOncebooleanMake it a view-once message mentionsstring[]JIDs to mention
Sending Stickers
The replyWithSticker() method sends WebP stickers:
import fs from "fs" ;
bot . command ( "sticker" , async ( ctx ) => {
// Stickers must be in WebP format as a Buffer
const stickerBuffer = fs . readFileSync ( "./stickers/cool.webp" );
await ctx . replyWithSticker ( stickerBuffer );
});
The replyWithSticker() method (from context.ts:128-146) only accepts Buffer objects, not URLs. Stickers must be in WebP format (image/webp mimetype).
Creating Stickers from Images
You can convert images to stickers using image processing libraries:
import sharp from "sharp" ;
bot . command ( "makesticker" , async ( ctx ) => {
// Check if message has an image
if ( ctx . type !== "imageMessage" ) {
await ctx . reply ( "Please send an image with this command." );
return ;
}
try {
// Download the image
const imageBuffer = await ctx . download ();
// Convert to WebP sticker format (512x512)
const stickerBuffer = await sharp ( imageBuffer )
. resize ( 512 , 512 , {
fit: "contain" ,
background: { r: 0 , g: 0 , b: 0 , alpha: 0 }
})
. webp ()
. toBuffer ();
// Send as sticker
await ctx . replyWithSticker ( stickerBuffer );
} catch ( error ) {
await ctx . reply ( "Failed to create sticker." );
}
});
The download() method retrieves media content from incoming messages:
bot . use ( async ( ctx , next ) => {
// Check if message contains media
const mediaTypes = [
"imageMessage" ,
"videoMessage" ,
"audioMessage" ,
"documentMessage" ,
"stickerMessage"
];
if ( mediaTypes . includes ( ctx . type )) {
console . log ( `Received ${ ctx . type } ` );
console . log ( `Size: ${ ctx . size } bytes` );
console . log ( `Mimetype: ${ ctx . mimetype } ` );
// Download the media
const buffer = await ctx . download ();
console . log ( `Downloaded ${ buffer . length } bytes` );
// Process the buffer (save to file, upload to storage, etc.)
}
await next ();
});
The download() method (from context.ts:174-176) uses Baileys’ downloadMediaMessage function to retrieve media content as a Buffer.
Practical Examples
Image Mirror Bot
Download an image and send it back: bot . command ( "mirror" , async ( ctx ) => {
if ( ctx . type !== "imageMessage" ) {
await ctx . reply ( "Please send an image with this command." );
return ;
}
// Download the image
const imageBuffer = await ctx . download ();
// Send it back
await ctx . replyWithImage ( imageBuffer , {
caption: "Here's your image back!"
});
});
Media Information Bot
Display details about received media: bot . command ( "mediainfo" , async ( ctx ) => {
const mediaTypes = [ "imageMessage" , "videoMessage" , "audioMessage" ];
if ( ! mediaTypes . includes ( ctx . type )) {
await ctx . reply ( "Please send media with this command." );
return ;
}
const info = [
`*Media Information*` ,
`Type: ${ ctx . type . replace ( "Message" , "" ) } ` ,
`Size: ${ ( ctx . size / 1024 ). toFixed ( 2 ) } KB` ,
`Mimetype: ${ ctx . mimetype } ` ,
`Hash: ${ ctx . hash } ` ,
ctx . text ? `Caption: ${ ctx . text } ` : "No caption"
]. join ( " \n " );
await ctx . reply ( info );
});
Auto-Save Media
Automatically save received media to disk: import fs from "fs" ;
import path from "path" ;
bot . use ( async ( ctx , next ) => {
const mediaTypes = [ "imageMessage" , "videoMessage" , "audioMessage" ];
if ( mediaTypes . includes ( ctx . type )) {
try {
const buffer = await ctx . download ();
// Create filename from hash and mimetype
const ext = ctx . mimetype . split ( "/" )[ 1 ];
const filename = ` ${ ctx . hash } . ${ ext } ` ;
const filepath = path . join ( "./downloads" , filename );
// Save to disk
fs . writeFileSync ( filepath , buffer );
console . log ( `Saved media to ${ filepath } ` );
} catch ( error ) {
console . error ( "Failed to save media:" , error );
}
}
await next ();
});
Gallery Command
Send multiple images in sequence: bot . command ( "gallery" , async ( ctx ) => {
const images = [
"https://example.com/photo1.jpg" ,
"https://example.com/photo2.jpg" ,
"https://example.com/photo3.jpg"
];
await ctx . reply ( "Sending gallery..." );
for ( let i = 0 ; i < images . length ; i ++ ) {
await ctx . replyWithImage ( images [ i ], {
caption: `Photo ${ i + 1 } of ${ images . length } `
});
}
});
You can create a utility to check media types:
function isMediaMessage ( type : string ) : boolean {
const mediaTypes = [
"imageMessage" ,
"videoMessage" ,
"audioMessage" ,
"documentMessage" ,
"stickerMessage"
];
return mediaTypes . includes ( type );
}
bot . use ( async ( ctx , next ) => {
if ( isMediaMessage ( ctx . type )) {
console . log ( "Media message received" );
}
await next ();
});
Best Practices
Handle download errors gracefully
Always wrap download() calls in try-catch blocks: bot . command ( "download" , async ( ctx ) => {
try {
const buffer = await ctx . download ();
console . log ( `Downloaded ${ buffer . length } bytes` );
} catch ( error ) {
await ctx . reply ( "Failed to download media." );
console . error ( error );
}
});
Use appropriate mimetypes
Specify correct mimetypes for better compatibility: // Images
await ctx . replyWithImage ( buffer , { mimetype: "image/jpeg" });
await ctx . replyWithImage ( buffer , { mimetype: "image/png" });
// Videos
await ctx . replyWithVideo ( buffer , { mimetype: "video/mp4" });
// Audio
await ctx . replyWithAudio ( buffer , { mimetype: "audio/mpeg" });
await ctx . replyWithAudio ( buffer , { mimetype: "audio/ogg; codecs=opus" });
Remove temporary media files after processing: import fs from "fs" ;
import os from "os" ;
import path from "path" ;
bot . command ( "process" , async ( ctx ) => {
if ( ctx . type !== "imageMessage" ) return ;
const buffer = await ctx . download ();
const tempFile = path . join ( os . tmpdir (), ` ${ ctx . id } .jpg` );
try {
fs . writeFileSync ( tempFile , buffer );
// Process the file...
} finally {
// Clean up
if ( fs . existsSync ( tempFile )) {
fs . unlinkSync ( tempFile );
}
}
});
Optimize media before sending
Error Handling
Common errors and how to handle them:
bot . use ( async ( ctx , next ) => {
if ( ctx . type === "imageMessage" ) {
try {
const buffer = await ctx . download ();
// Process the image
await processImage ( buffer );
await ctx . reply ( "Image processed successfully!" );
} catch ( error ) {
if ( error . message . includes ( "connection" )) {
await ctx . reply ( "Network error. Please try again." );
} else if ( error . message . includes ( "size" )) {
await ctx . reply ( "File is too large." );
} else {
await ctx . reply ( "Failed to process image." );
}
console . error ( "Media error:" , error );
}
}
await next ();
});
Next Steps
Groups Work with group chats and manage group media
Advanced Features Explore advanced bot patterns and utilities
Handling Messages Learn about message types and parsing
API Reference Full API documentation for Context methods