Skip to main content

WAMediaUpload

Union type representing the different ways to provide media for upload to WhatsApp.
type WAMediaUpload = Buffer | WAMediaPayloadStream | WAMediaPayloadURL
WAMediaUpload accepts three formats:
  1. Buffer - Raw binary data
  2. Stream - Readable stream (useful for large files)
  3. URL - URL string or URL object pointing to the media

Upload Formats

Buffer Upload

Provide media as a raw Buffer object. Best for small to medium-sized files already in memory.
media
Buffer
Raw binary data of the media file
Example:
import fs from 'fs'
import makeWASocket from '@whiskeysockets/baileys'

const sock = makeWASocket({ /* auth */ })

// Read file into Buffer
const imageBuffer = fs.readFileSync('./image.jpg')

// Send as buffer
await sock.sendMessage(jid, {
  image: imageBuffer,
  caption: 'Sent from buffer'
})
Use Cases:
  • Small images and files
  • In-memory media processing
  • Generated content (QR codes, charts, etc.)

Stream Upload (WAMediaPayloadStream)

Provide media as a readable stream. Recommended for large files to avoid loading entire file into memory.
stream
Readable
required
Node.js Readable stream containing the media data
Type Definition:
type WAMediaPayloadStream = { 
  stream: Readable 
}
Example:
import fs from 'fs'
import { Readable } from 'stream'

const sock = makeWASocket({ /* auth */ })

// Create read stream for large video file
const videoStream = fs.createReadStream('./large-video.mp4')

// Send as stream
await sock.sendMessage(jid, {
  video: { stream: videoStream },
  caption: 'Large video uploaded via stream'
})
Use Cases:
  • Large video files (>50MB)
  • Large audio files
  • Streaming from external sources
  • Memory-efficient uploads
Benefits:
  • Lower memory usage
  • Better performance for large files
  • Supports backpressure handling

URL Upload (WAMediaPayloadURL)

Provide media as a URL. Baileys will download the file from the URL before uploading to WhatsApp.
url
URL | string
required
URL pointing to the media file (can be string or URL object)
Type Definition:
type WAMediaPayloadURL = { 
  url: URL | string 
}
Example with String URL:
const sock = makeWASocket({ /* auth */ })

// Send image from URL (string)
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/image.jpg' },
  caption: 'Image from URL'
})

// Send document from URL
await sock.sendMessage(jid, {
  document: { url: 'https://example.com/document.pdf' },
  mimetype: 'application/pdf',
  fileName: 'document.pdf'
})
Example with URL Object:
import { URL } from 'url'

const imageUrl = new URL('https://example.com/photo.png')

await sock.sendMessage(jid, {
  image: { url: imageUrl },
  caption: 'Photo from URL object'
})
Use Cases:
  • Remote media files
  • CDN-hosted content
  • Public image URLs
  • Webhook media downloads
Important Notes:
  • URL must be publicly accessible
  • Baileys downloads the file first, then uploads to WhatsApp
  • Consider timeout settings for large files
  • HTTP/HTTPS protocols supported

Media Types Support

All media message types support WAMediaUpload:

Image

await sock.sendMessage(jid, {
  image: buffer | { stream } | { url },
  caption: 'Image caption'
})

Video

await sock.sendMessage(jid, {
  video: buffer | { stream } | { url },
  caption: 'Video caption',
  gifPlayback: false
})

Audio

await sock.sendMessage(jid, {
  audio: buffer | { stream } | { url },
  ptt: true // voice note
})

Document

await sock.sendMessage(jid, {
  document: buffer | { stream } | { url },
  mimetype: 'application/pdf',
  fileName: 'document.pdf'
})

Sticker

await sock.sendMessage(jid, {
  sticker: buffer | { stream } | { url },
  isAnimated: false
})

Complete Examples

Example 1: Image with All Three Methods

import fs from 'fs'
import makeWASocket from '@whiskeysockets/baileys'

const sock = makeWASocket({ /* auth */ })
const jid = '[email protected]'

// Method 1: Buffer
const imageBuffer = fs.readFileSync('./photo.jpg')
await sock.sendMessage(jid, {
  image: imageBuffer,
  caption: 'Sent as Buffer'
})

// Method 2: Stream
const imageStream = fs.createReadStream('./photo.jpg')
await sock.sendMessage(jid, {
  image: { stream: imageStream },
  caption: 'Sent as Stream'
})

// Method 3: URL
await sock.sendMessage(jid, {
  image: { url: 'https://example.com/photo.jpg' },
  caption: 'Sent from URL'
})

Example 2: Large Video with Stream

import fs from 'fs'

const sock = makeWASocket({ /* auth */ })

// Stream large video file (memory efficient)
const videoStream = fs.createReadStream('./movie.mp4')

await sock.sendMessage(jid, 
  {
    video: { stream: videoStream },
    caption: 'Full movie upload',
    mimetype: 'video/mp4'
  },
  {
    // Increase timeout for large file
    mediaUploadTimeoutMs: 300000 // 5 minutes
  }
)

Example 3: Document from URL with Custom Options

const sock = makeWASocket({ /* auth */ })

await sock.sendMessage(jid, 
  {
    document: { url: 'https://cdn.example.com/report.pdf' },
    mimetype: 'application/pdf',
    fileName: 'Monthly_Report_2024.pdf',
    caption: 'Here is the monthly report'
  },
  {
    quoted: previousMessage,
    mediaUploadTimeoutMs: 60000
  }
)

Example 4: Voice Note from Buffer

import fs from 'fs'

const sock = makeWASocket({ /* auth */ })

// Read audio file
const audioBuffer = fs.readFileSync('./voice-note.ogg')

// Send as voice note (PTT)
await sock.sendMessage(jid, {
  audio: audioBuffer,
  ptt: true, // Push-to-talk (voice note)
  mimetype: 'audio/ogg; codecs=opus'
})

Example 5: Product Image Upload

import fs from 'fs'

const sock = makeWASocket({ /* auth */ })

const productImageBuffer = fs.readFileSync('./product.jpg')

await sock.sendMessage(jid, {
  product: {
    productImage: productImageBuffer, // WAMediaUpload type
    productId: '12345',
    title: 'Amazing Product',
    description: 'Best product ever',
    currencyCode: 'USD',
    priceAmount1000: 99000, // $99.00
    retailerId: 'shop123'
  },
  businessOwnerJid: '[email protected]'
})

Best Practices

When to Use Buffer

  • Files < 10MB
  • In-memory generated content
  • Already loaded data
  • Quick operations

When to Use Stream

  • Files > 10MB
  • Large videos (>50MB)
  • Memory-constrained environments
  • Processing pipelines

When to Use URL

  • Remote hosted files
  • CDN content
  • Webhook media
  • Public images

Upload Configuration

Timeout Settings

Control upload timeout using mediaUploadTimeoutMs option:
// Default timeout
const UPLOAD_TIMEOUT = 30000 // 30 seconds

// Custom timeout for large files
await sock.sendMessage(jid, 
  { video: largeVideoStream },
  { mediaUploadTimeoutMs: 120000 } // 2 minutes
)

Upload Intervals

// Minimum interval between uploads
const MIN_UPLOAD_INTERVAL = 5000 // 5 seconds
To avoid rate limiting, space out media uploads:
const mediaFiles = [file1, file2, file3]

for (const file of mediaFiles) {
  await sock.sendMessage(jid, { image: file })
  // Wait 5 seconds before next upload
  await new Promise(resolve => setTimeout(resolve, 5000))
}

Media Cache

To optimize media uploads and avoid re-uploading the same file, implement caching in your custom store by tracking media URLs and hashes:
import makeWASocket from '@whiskeysockets/baileys'

// Implement your own media cache
const mediaCache = new Map()

const sock = makeWASocket({
  // ... auth config
})

// Cache media URLs after upload
sock.ev.on('messages.upsert', ({ messages }) => {
  for (const msg of messages) {
    if (msg.message?.imageMessage) {
      const { url, fileSha256 } = msg.message.imageMessage
      if (url && fileSha256) {
        const hash = Buffer.from(fileSha256).toString('base64')
        mediaCache.set(hash, url)
      }
    }
  }
})

// Reuse cached URLs when sending same media
const cachedUrl = mediaCache.get(mediaHash)
if (cachedUrl) {
  await sock.sendMessage(jid, { image: { url: cachedUrl } })
}

Type Reference

Complete Type Definitions

import type { Readable } from 'stream'
import type { URL } from 'url'

type WAMediaPayloadURL = { 
  url: URL | string 
}

type WAMediaPayloadStream = { 
  stream: Readable 
}

type WAMediaUpload = 
  | Buffer 
  | WAMediaPayloadStream 
  | WAMediaPayloadURL

MediaType Enum

type MediaType = 
  | 'image'
  | 'video'
  | 'document'
  | 'audio'
  | 'sticker'
  | 'thumbnail-link'
  | 'product-catalog-image'
  | 'md-app-state'
  | 'md-msg-hist'
  | 'biz-cover-photo'

Upload Function Type

type WAMediaUploadFunction = (
  encFilePath: string,
  opts: {
    fileEncSha256B64: string
    mediaType: MediaType
    timeoutMs?: number
  }
) => Promise<{
  mediaUrl: string
  directPath: string
  meta_hmac?: string
  ts?: number
  fbid?: number
}>

Build docs developers (and LLMs) love