Skip to main content

Overview

Baileys supports sending various media types efficiently. Media can be provided as a Buffer, Stream, or URL, and Baileys handles encryption and upload automatically.

Media Upload Types

All media messages accept a WAMediaUpload type, which can be:
type WAMediaUpload = 
  | Buffer 
  | { stream: Readable } 
  | { url: URL | string }
Using stream or url is recommended to save memory, especially for large files. Baileys will encrypt the media as a stream without loading the entire file into memory.

Image Messages

Send images with optional captions:
import { readFileSync } from 'fs'

await sock.sendMessage(
  jid,
  {
    image: readFileSync('./image.jpg'),
    caption: 'Hello World!'
  }
)

Image with Mentions

await sock.sendMessage(
  jid,
  {
    image: { url: './image.jpg' },
    caption: 'Look at this @12345678901!',
    mentions: ['12345678901@s.whatsapp.net']
  }
)

Video Messages

Send video files with captions:
await sock.sendMessage(
  jid,
  {
    video: { url: './video.mp4' },
    caption: 'Check out this video!'
  }
)

Video Notes (PTV)

Send as a video note (circular video):
await sock.sendMessage(
  jid,
  {
    video: { url: './video.mp4' },
    ptv: true // Send as video note
  }
)
ptv stands for “picture-taking video” - WhatsApp’s internal name for video notes.

GIF Messages

WhatsApp doesn’t support actual .gif files. GIFs are sent as MP4 videos with the gifPlayback flag.
import { readFileSync } from 'fs'

await sock.sendMessage(
  jid,
  {
    video: readFileSync('./animation.mp4'),
    caption: 'Animated content',
    gifPlayback: true
  }
)

Audio Messages

Send audio files or voice notes:

Regular Audio

await sock.sendMessage(
  jid,
  {
    audio: { url: './audio.mp3' },
    mimetype: 'audio/mp4'
  }
)

Voice Notes (PTT)

await sock.sendMessage(
  jid,
  {
    audio: { url: './voice.ogg' },
    mimetype: 'audio/ogg; codecs=opus',
    ptt: true // Send as voice note
  }
)
ptt stands for “push to talk” - WhatsApp’s internal name for voice notes.

Audio Requirements

For audio to work across all devices, convert to OGG format with ffmpeg:
ffmpeg -i input.mp4 -avoid_negative_ts make_zero -ac 1 output.ogg
Required flags:
  • codec: libopus - OGG file format
  • ac: 1 - Single audio channel
  • avoid_negative_ts make_zero - Timestamp handling

Audio with Duration

await sock.sendMessage(
  jid,
  {
    audio: { url: './audio.ogg' },
    mimetype: 'audio/ogg; codecs=opus',
    ptt: true,
    seconds: 60 // Optional duration in seconds
  }
)

Document Messages

Send any file as a document:
await sock.sendMessage(
  jid,
  {
    document: { url: './file.pdf' },
    mimetype: 'application/pdf',
    fileName: 'document.pdf',
    caption: 'Here is the document'
  }
)

Common MIME Types

const mimeTypes = {
  pdf: 'application/pdf',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  zip: 'application/zip',
  txt: 'text/plain'
}

Sticker Messages

Send stickers (WebP format):
await sock.sendMessage(
  jid,
  {
    sticker: { url: './sticker.webp' }
  }
)

Animated Stickers

await sock.sendMessage(
  jid,
  {
    sticker: { url: './animated-sticker.webp' },
    isAnimated: true
  }
)

View Once Messages

Send media that can only be viewed once:
await sock.sendMessage(
  jid,
  {
    image: { url: './secret.jpg' },
    viewOnce: true,
    caption: 'This can only be viewed once'
  }
)
viewOnce works with images, videos, and audio messages.

Thumbnails

Baileys can automatically generate thumbnails for media messages.

Automatic Thumbnail Generation

Install image processing libraries:
# For images and stickers
yarn add sharp
# or
yarn add jimp

# For video thumbnails (requires ffmpeg installed on system)
# No additional packages needed

Manual Thumbnail

await sock.sendMessage(
  jid,
  {
    image: { url: './large-image.jpg' },
    jpegThumbnail: thumbnailBuffer, // Buffer or base64 string
    caption: 'Large image with custom thumbnail'
  }
)

Media with Context

Combine media with other message features:
await sock.sendMessage(
  jid,
  {
    image: { url: './photo.jpg' },
    caption: 'Check this out @12345678901!',
    mentions: ['12345678901@s.whatsapp.net']
  },
  {
    quoted: originalMessage,
    ephemeralExpiration: 86400 // 24 hours
  }
)

Media Upload Process

Understanding how media is uploaded:
  1. Encryption: Media is encrypted using AES-256-CBC
  2. Upload: Encrypted media is uploaded to WhatsApp servers
  3. Message: A message containing the media key and URL is sent
  4. Decryption: Recipient decrypts media using the media key
// This all happens automatically
const msg = await sock.sendMessage(jid, {
  image: { url: './image.jpg' }
})

// The message contains:
// - directPath: WhatsApp CDN path
// - mediaKey: Encryption key
// - fileEncSha256: Encrypted file hash
// - fileSha256: Original file hash

Media Configuration Options

Configure media upload behavior in socket config:
const sock = makeWASocket({
  // Timeout for media uploads (default: 30 seconds)
  mediaUploadTimeoutMs: 60000,
  
  // Cache media to avoid re-uploading
  mediaCache: new NodeCache({
    stdTTL: 3600 // 1 hour cache
  })
})

Best Practices

  1. Use Streams: For large files, use streams to avoid memory issues
  2. Set MIME Types: Always specify correct MIME types for documents
  3. Optimize Images: Compress images before sending
  4. Generate Thumbnails: Install sharp/jimp for automatic thumbnails
  5. Handle Errors: Wrap uploads in try-catch blocks
  6. Rate Limiting: Don’t send too many media messages rapidly

Media Message Types

From the Baileys source code:
type AnyMediaMessageContent = 
  | { image: WAMediaUpload; caption?: string; jpegThumbnail?: string }
  | { video: WAMediaUpload; caption?: string; gifPlayback?: boolean; ptv?: boolean }
  | { audio: WAMediaUpload; ptt?: boolean; seconds?: number }
  | { sticker: WAMediaUpload; isAnimated?: boolean }
  | { document: WAMediaUpload; mimetype: string; fileName?: string; caption?: string }

Troubleshooting

Upload Failures

try {
  await sock.sendMessage(jid, { image: { url: './image.jpg' } })
} catch (error) {
  if (error.message.includes('upload failed')) {
    // Retry with exponential backoff
  }
}

Large Files

For files larger than 100MB, consider:
  • Breaking into smaller chunks
  • Using a more reliable network connection
  • Increasing mediaUploadTimeoutMs

Next Steps

Downloading Media

Learn how to download media from received messages

Message Options

Configure ephemeral and other message options