Command Palette

Search for a command to run...

0

Command Palette

Search for a command to run...

Blog

How to Send WhatsApp Messages Using Go: Building a Seamless Client with whatsmeow

A step-by-step guide to building a WhatsApp messaging client in Go using the whatsmeow library—covering setup, QR authentication, and sending messages programmatically.

Originally published on Medium.

WhatsApp automation in Go is surprisingly approachable once you understand the whatsmeow library. This article walks through building a working client from scratch—covering QR-code authentication, session persistence, and sending text messages to any number.

Why whatsmeow?

whatsmeow is an open-source Go library that implements the WhatsApp Web protocol. Unlike the official Business API, it gives you direct control over the connection and works without a Meta developer account.

Key capabilities:

  • QR-code login with session persistence (no re-scan on restart)
  • Send and receive messages, media, and reactions
  • Handle group events and presence updates
  • Lightweight—runs on a single binary with no external runtime

Core flow

┌─────────────────────────────────────────────────────┐
│  1. Init SQLite store                               │
│  2. Create whatsmeow client                         │
│  3. QR scan (first run) / restore session           │
│  4. Connect to WhatsApp servers                     │
│  5. Send message via client.SendMessage()           │
└─────────────────────────────────────────────────────┘

Setup

go get go.mau.fi/whatsmeow
go get go.mau.fi/whatsmeow/store/sqlstore

Initialise the store

whatsmeow stores session keys in an SQL database. SQLite works fine for single-instance services:

container, err := sqlstore.New("sqlite3", "file:session.db?_foreign_keys=on", nil)
if err != nil {
    log.Fatal(err)
}
deviceStore, err := container.GetFirstDevice()
if err != nil {
    log.Fatal(err)
}

Create the client and handle QR

client := whatsmeow.NewClient(deviceStore, nil)
 
if client.Store.ID == nil {
    // First login — show QR code
    qrChan, _ := client.GetQRChannel(context.Background())
    if err := client.Connect(); err != nil {
        log.Fatal(err)
    }
    for evt := range qrChan {
        if evt.Event == "code" {
            qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
        }
    }
} else {
    // Session already exists
    if err := client.Connect(); err != nil {
        log.Fatal(err)
    }
}

Send a message

recipient, _ := types.ParseJID("628123456789@s.whatsapp.net")
 
msg := &waProto.Message{
    Conversation: proto.String("Hello from Go!"),
}
 
_, err = client.SendMessage(context.Background(), recipient, msg)
if err != nil {
    log.Printf("send error: %v", err)
}

The JID format for individual numbers is <country_code><number>@s.whatsapp.net (no +, no spaces).

Production tips

  • Session persistence: commit session.db to persistent storage (volume mount in Docker, not ephemeral FS).
  • Reconnect logic: listen for events.Disconnected and call client.Connect() with exponential back-off.
  • Rate limits: WhatsApp will ban numbers that send bulk messages aggressively. Space out sends and keep volumes reasonable.
  • Multi-device: whatsmeow supports the multi-device protocol—your phone does not need to stay online after the initial QR scan.

Read the full article with complete runnable code on Medium →