How to Send Text, Media, Location, Contact, and Poll Messages with Baileys
Use sock.sendMessage(jid, content, options?) with a content object matching the AnyMessageContent interface—Baileys automatically handles protocol encoding, encryption, and delivery for text, images, video, audio, documents, location, contacts, and polls.
Baileys provides a unified API to send various message types through WhatsApp Web without manually constructing protobuf payloads. The library's sendMessage method in src/Socket/messages-send.ts orchestrates message generation via generateWAMessage() ([src/Utils/messages.ts#L666-L672]), content normalization in generateWAMessageContent() ([src/Utils/messages.ts#L395-L795]), and encrypted delivery through relayMessage() ([src/Socket/messages-send.ts#L16-L31]). This guide covers the canonical content shapes for each message type with runnable TypeScript examples.
Sending Text Messages with Baileys
The simplest message type requires only a text property. Baileys detects this path at if (hasNonNullishProperty(message, 'text')) ([src/Utils/messages.ts#L996-L1005]).
await sock.sendMessage('1234567890@s.whatsapp.net', {
text: 'Hello from Baileys!'
})
Link Preview Options
Baileys automatically fetches link previews for URLs in your text. Control this behavior:
await sock.sendMessage(jid, {
text: 'Check out https://github.com/WhiskeySockets/Baileys'
// linkPreview: false // disable if needed
})
The link preview logic is implemented in src/Utils/link-preview.ts.
Sending Media Messages (Image, Video, Audio, Document, Sticker)
Media messages use the AnyMediaMessageContent union type ([src/Types/Message.ts#L74-L104]). Baileys branches on media type at lines L1026-L1065 in generateWAMessageContent().
| Media Type | Required Field | Key Optional Fields |
|---|---|---|
| Image | image: WAMediaUpload |
caption, jpegThumbnail, width, height |
| Video | video: WAMediaUpload |
caption, gifPlayback, ptv (picture-in-video), width, height |
| Audio | audio: WAMediaUpload |
ptt (push-to-talk), seconds |
| Document | document: WAMediaUpload, mimetype |
fileName, caption |
| Sticker | sticker: WAMediaUpload |
isAnimated, width, height |
Image Example
await sock.sendMessage(jid, {
image: { url: './photo.jpg' }, // WAMediaUpload: URL, Buffer, or Stream
caption: 'Check this out!'
})
Document with Custom Filename
await sock.sendMessage(jid, {
document: { buffer: pdfBuffer },
mimetype: 'application/pdf',
fileName: 'report-2024.pdf',
caption: 'Monthly report'
})
Voice Note (Push-to-Talk)
await sock.sendMessage(jid, {
audio: { url: './voice.ogg' },
ptt: true, // displays as waveform, not playable audio
seconds: 15
})
WAMediaUpload accepts URLs, Buffers, or Streams—Baileys handles upload to WhatsApp's CDN internally.
Sending Location Messages with Baileys
Location messages use the WALocationMessage interface, processed at else if (hasNonNullishProperty(message, 'location')) ([src/Utils/messages.ts#L1038-L1040]).
await sock.sendMessage(jid, {
location: {
degreesLatitude: 37.7749,
degreesLongitude: -122.4194,
name: 'San Francisco',
address: 'Golden Gate Bridge'
}
})
The name and address fields are optional but recommended—they appear in the WhatsApp location card header.
Sending Contact Cards (vCard)
Contact messages wrap vCard data in the contacts structure, handled at lines L1042-L1052 ([src/Utils/messages.ts]).
await sock.sendMessage(jid, {
contacts: {
displayName: 'Business Contacts', // optional section header
contacts: [
{
displayName: 'John Doe',
vcard: `BEGIN:VCARD
VERSION:3.0
FN:John Doe
ORG:Acme Inc.
TEL;type=CELL;type=VOICE;waid=1234567890:1234567890
EMAIL:john@example.com
END:VCARD`
}
]
}
})
Multiple contacts can be included in the contacts.contacts array. The waid parameter in the vCard TEL field should match the WhatsApp ID for proper linking.
Creating and Sending Polls
Polls use PollMessageOptions ([src/Types/Message.ts#L136-L144]), processed at else if (hasNonNullishProperty(message, 'poll')) ([src/Utils/messages.ts#L1077-L1085]).
await sock.sendMessage(jid, {
poll: {
name: 'Pick a fruit',
selectableCount: 1, // 0 = unlimited choices, 1+ = limit
values: ['🍎 Apple', '🍊 Orange', '🍌 Banana'],
messageSecret: crypto.randomBytes(32) // optional: encrypts selections
}
})
Baileys validates selectableCount against the values array size at lines L1079-L1084. Set selectableCount: 0 for unlimited selections, or >1 for multiple-choice limits.
Complete Working Example
This script demonstrates all five message types sequentially. Replace authentication with your preferred Baileys auth method.
import makeWASocket, { useMultiFileAuthState } from '@whiskeysockets/baileys'
import { Boom } from '@hapi/boom'
import { readFileSync } from 'fs'
import crypto from 'crypto'
async function main() {
const { state, saveCreds } = await useMultiFileAuthState('./baileys-auth')
const sock = makeWASocket({
auth: state,
printQRInTerminal: true
})
sock.ev.on('creds.update', saveCreds)
sock.ev.on('connection.update', ({ connection }) => {
if (connection === 'open') console.log('Connected!')
})
const jid = '1234567890@s.whatsapp.net'
// 1️⃣ Text message
await sock.sendMessage(jid, { text: 'Hello from Baileys!' })
// 2️⃣ Image with caption
await sock.sendMessage(jid, {
image: { url: './assets/demo.jpg' },
caption: 'Photo demonstration'
})
// 3️⃣ Location pin
await sock.sendMessage(jid, {
location: {
degreesLatitude: 40.7128,
degreesLongitude: -74.0060,
name: 'New York City',
address: 'Manhattan, NY 10001'
}
})
// 4️⃣ Contact card
await sock.sendMessage(jid, {
contacts: {
contacts: [{
displayName: 'Support Team',
vcard: readFileSync('./assets/contact.vcf', 'utf-8')
}]
}
})
// 5️⃣ Interactive poll
await sock.sendMessage(jid, {
poll: {
name: 'Preferred stack?',
selectableCount: 1,
values: ['MERN', 'PERN', 'LAMP', 'Django/DRF'],
messageSecret: crypto.randomBytes(32)
}
})
}
main().catch(err => {
if (err instanceof Boom) {
console.error('WhatsApp error:', err.output.statusCode, err.message)
} else {
console.error(err)
}
})
Key Implementation Details from Source Code
Understanding Baileys' internal flow helps debug issues:
- Message generation:
generateWAMessage()→generateWAMessageContent()builds protocol buffers - Type detection: Uses
hasNonNullishProperty()checks to branch on content keys (text,image,video, etc.) - Encryption:
relayMessage()insrc/Socket/messages-send.tshandles multi-device encryption and delivery - Type safety: All content shapes are defined in
src/Types/Message.ts—reference this for exact interfaces
Summary
- Text:
{ text: string }with optionallinkPreviewcontrol - Media: Use
image,video,audio,document, orstickerkeys withWAMediaUploadvalues - Location:
{ location: { degreesLatitude, degreesLongitude, name?, address? } } - Contact:
{ contacts: { contacts: [{ displayName, vcard }] } }with valid vCard 3.0 strings - Poll:
{ poll: { name, selectableCount?, values, messageSecret? } }for interactive voting
All message types route through sock.sendMessage()—Baileys abstracts protocol details, encryption, and device synchronization.
Frequently Asked Questions
What is the maximum size for media files in Baileys?
Baileys does not enforce size limits—WhatsApp's servers do. Typical limits are 16 MB for documents, 64 MB for most media in regular chats. The library streams large files automatically via WAMediaUpload stream support.
How do I send a message to a group instead of an individual?
Use the group's JID format: 1234567890@g.us instead of 1234567890@s.whatsapp.net. The sendMessage API remains identical; only the JID changes.
Can I combine multiple content types in one message?
No—WhatsApp's protocol enforces single-message-type payloads. To send text with media, use the caption field on media messages rather than a separate text property.
Why does my poll not show secret ballot results?
Include messageSecret: crypto.randomBytes(32) in your poll object. Without this, poll selections are visible to the message sender. The secret enables end-to-end encryption of votes per PollMessageOptions in src/Types/Message.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →