waga

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 27, 2026 License: MIT Imports: 13 Imported by: 0

README

WhatsApp Gateway Go SDK

A Go SDK for the WhatsApp Gateway API. This SDK provides an ergonomic client wrapper around generated types from the OpenAPI specification.

Installation

go get github.com/glennprays/whatsapp-gateway-sdk-go

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/glennprays/whatsapp-gateway-sdk-go"
)

func main() {
    // Create client
    client := waga.NewClient(waga.WithBaseURL("http://localhost:3000/api/v1"))

    // Register phone number and get JWT token
    _, err := client.Register(context.Background(), "6281234567890", "your_secret_key")
    if err != nil {
        log.Fatal(err)
    }

    // Get QR code for WhatsApp login
    qr, err := client.GetQRCode(context.Background(), "json")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Scan this QR code:", qr.QRCode)

    // Send a message
    resp, err := client.SendText(context.Background(), "6289876543210@s.whatsapp.net", "Hello!")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Message sent:", resp.MessageId)
}

Configuration

The client can be configured with various options:

client := waga.NewClient(
    waga.WithBaseURL("https://your-gateway.com/api/v1"),
    waga.WithTimeout(30 * time.Second),
    waga.WithToken("existing_jwt_token"),  // If you already have a token
    waga.WithUserAgent("MyApp/1.0"),
)
Available Options
Option Description Default
WithBaseURL(url) API base URL http://localhost:3000/api/v1
WithTimeout(d) HTTP request timeout 30s
WithToken(token) Pre-existing JWT token ""
WithHTTPClient(c) Custom HTTP client Default client
WithUserAgent(ua) Custom User-Agent header WhatsApp-Gateway-SDK-Go/1.0

Authentication

Register New Phone Number
resp, err := client.Register(ctx, "6281234567890", "your_secret_key")
if err != nil {
    log.Fatal(err)
}
// Token is automatically stored in client
fmt.Println("Token:", resp.Token)
Use Existing Token
// Option 1: During client creation
client := waga.NewClient(waga.WithToken("your_jwt_token"))

// Option 2: Set token later
client := waga.NewClient()
client.SetToken("your_jwt_token")

WhatsApp Authentication

Get QR Code
// Get QR code as base64 (json format)
qr, err := client.GetQRCode(ctx, "json")
if err != nil {
    log.Fatal(err)
}
fmt.Println("QR Code:", qr.QRCode)        // Base64 encoded image
fmt.Println("Expires in:", qr.ExpiresIn)  // Seconds until expiry

// Get QR code as HTML img tag (html format)
qrHTML, err := client.GetQRCode(ctx, "html")
Get Pair Code
pair, err := client.GetPairCode(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Pair Code:", pair.PairCode)
fmt.Println("Expires in:", pair.ExpiresIn)
Check Login Status
status, err := client.GetLoginStatus(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Authenticated:", status.Authenticated)
Reconnect Session
err := client.Reconnect(ctx)
if err != nil {
    log.Fatal(err)
}
Logout
err := client.Logout(ctx)
if err != nil {
    log.Fatal(err)
}

Sending Messages

Text Message
// Format phone number to WhatsApp JID format
recipient := waga.FormatMSISDN("6289876543210")  // -> "6289876543210@s.whatsapp.net"

resp, err := client.SendText(ctx, recipient, "Hello from Go SDK!")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Message ID:", resp.MessageId)
Image Message
// Open image file
file, err := os.Open("photo.jpg")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

resp, err := client.SendImage(ctx, recipient, file, "Check this out!", false)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Message ID:", resp.MessageId)
Location Message
resp, err := client.SendLocation(ctx, recipient, -6.2088, 106.8456, "Jakarta", "Jakarta, Indonesia")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Message ID:", resp.MessageId)
Poll Message
options := []string{"Red", "Green", "Blue"}

// selectableCount limits how many options a user can select (0 = no limit)
resp, err := client.SendPoll(ctx, recipient, "What is your favorite color?", options, 1)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Message ID:", resp.MessageId)
Sticker Message
// Open sticker file (WebP format)
file, err := os.Open("sticker.webp")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

resp, err := client.SendSticker(ctx, recipient, file)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Message ID:", resp.MessageId)
Edit Message
err := client.EditMessage(ctx, recipient, messageID, "Updated message")
if err != nil {
    log.Fatal(err)
}
Delete Message
err := client.DeleteMessage(ctx, recipient, messageID)
if err != nil {
    log.Fatal(err)
}
React to Message
err := client.React(ctx, recipient, messageID, "👍")
if err != nil {
    log.Fatal(err)
}

Webhook Management

Register Webhook
err := client.RegisterWebhook(ctx, "https://example.com/webhook", "my_hmac_secret")
if err != nil {
    log.Fatal(err)
}
Get Registered Webhook
webhook, err := client.GetWebhook(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Webhook URL:", webhook.URL)
Unregister Webhook
err := client.UnregisterWebhook(ctx)
if err != nil {
    log.Fatal(err)
}

Webhook Verification

When receiving webhooks on your server, verify the signature:

import (
    "io"
    "net/http"
    "errors"

    "github.com/glennprays/whatsapp-gateway-sdk-go"
)

func handleWebhook(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    signature := r.Header.Get("X-Webhook-Signature")

    // Create verifier with your HMAC secret
    verifier := waga.NewWebhookVerifier("my_hmac_secret")

    // Verify and parse incoming message webhook
    payload, err := verifier.ParseIncomingWebhook(body, signature)
    if err != nil {
        if errors.Is(err, waga.ErrInvalidSignature) {
            http.Error(w, "Invalid signature", http.StatusUnauthorized)
            return
        }
        http.Error(w, "Invalid payload", http.StatusBadRequest)
        return
    }

    // Process the webhook
    fmt.Printf("Received message from %s: %s\n", payload.From, payload.Text)
}
Webhook Payload Types
Incoming Message
type IncomingWebhookPayload struct {
    Event      IncomingEventMessage      // "message.incoming"
    Chat       string                    // Chat ID
    From       string                    // Sender's ID
    IsGroup    bool                      // Is from group
    MessageId string                    // Message ID
    PushName   string                    // Sender's display name
    Timestamp  int                       // Unix timestamp
    Text       string                    // Message text (if text message)
    Type       IncomingMessageType       // "text", "image", "video", "audio", "document"
    Media      *IncomingMessageMediaInfo // Media info (if media message)
}
Outgoing Event
type OutgoingWebhookPayload struct {
    Event       OutgoingEventMessage // "message.queued", "message.sent", "message.failed"
    JobId       string               // Job ID
    To          string               // Recipient's ID
    PhoneNumber string               // Sender's phone number
    Timestamp   int                  // Unix timestamp
    MessageId   string               // Message ID
    Metadata    map[string]interface{} // Additional metadata
}

Error Handling

_, err := client.SendText(ctx, recipient, "Hello")
if err != nil {
    var sdkErr *waga.SDKError
    if waga.IsUnauthorized(err) {
        // Token expired or invalid
        fmt.Println("Please re-register")
    } else if waga.IsBadRequest(err) {
        // Invalid request parameters
        fmt.Println("Check your input")
    } else if waga.IsRateLimited(err) {
        // Too many requests
        fmt.Println("Please wait before retrying")
    } else if errors.As(err, &sdkErr) {
        // Other API error
        fmt.Printf("Error %d: %s\n", sdkErr.Code, sdkErr.Message)
    } else {
        // Network or other error
        fmt.Printf("Unexpected error: %v\n", err)
    }
}
Error Types
Error HTTP Code Description
ErrUnauthorized 401 Invalid or missing token
ErrBadRequest 400 Invalid request parameters
ErrForbidden 403 No permission for this action
ErrNotFound 404 Resource not found
ErrConflict 409 Resource conflict
ErrRateLimited 429 Too many requests
ErrInternalServer 500 Server error
ErrInvalidSignature - Webhook signature verification failed
ErrNotAuthenticated - Client has no token set

Helper Functions

Format MSISDN

Convert phone number to WhatsApp JID format:

jid := waga.FormatMSISDN("6281234567890")
// Returns: "6281234567890@s.whatsapp.net"
Format Group ID

Convert group ID to WhatsApp group JID format:

jid := waga.FormatGroupID("1234567890")
// Returns: "1234567890@g.us"
Compute Signature

Compute webhook signature (useful for testing):

payload := []byte(`{"event":"message.incoming"}`)
signature := waga.ComputeSignature(payload, "my_hmac_secret")
// Returns: "sha256=..."

Health Check

health, err := client.Health(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Status: %s\n", health.Status)

Development

Generate Types from OpenAPI
# Install oapi-codegen
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest

# Generate types
make generate
Run Tests
go test ./...

Requirements

  • Go 1.24 or later
  • Running WhatsApp Gateway instance

License

MIT License

Documentation

Overview

Package waga provides a Go client for interacting with the WhatsApp Gateway API.

The waga package enables Go applications to send WhatsApp messages, manage webhooks, and handle authentication with a WhatsApp Gateway service. It supports text and image messaging, message editing/deletion, webhook verification, and session management.

Quick Start

To get started, create a new client and register your phone number:

client := waga.NewClient()
resp, err := client.Register(ctx, "6281234567890", "your-secret-key")
if err != nil {
    log.Fatal(err)
}

Once registered, you can send messages:

msisdn := waga.FormatMSISDN("6281234567890")
msgResp, err := client.SendText(ctx, msisdn, "Hello, World!")
if err != nil {
    log.Fatal(err)
}

Authentication

The SDK supports two authentication methods:

1. Register a new phone number to obtain a JWT token 2. Use an existing JWT token with SetToken() or WithToken()

The client automatically stores the token returned by Register() for subsequent requests.

Message Formatting

Phone numbers should be formatted as MSISDN (WhatsApp JID format). Use the helper functions FormatMSISDN() for individuals and FormatGroupID() for groups:

individual := waga.FormatMSISDN("6281234567890")  // "6281234567890@s.whatsapp.net"
group := waga.FormatGroupID("1234567890")          // "1234567890@g.us"

Webhooks

Webhooks allow you to receive real-time notifications for incoming and outgoing messages. To handle webhooks securely, use the WebhookVerifier:

verifier := waga.NewWebhookVerifier("your-hmac-secret")
payload, err := verifier.ParseIncomingWebhook(body, signatureHeader)
if err != nil {
    log.Fatal(err)
}

Configuration

The client can be configured with options:

client := waga.NewClient(
    waga.WithBaseURL("https://api.example.com"),
    waga.WithTimeout(60*time.Second),
    waga.WithToken("existing-jwt-token"),
)

Error Handling

The SDK returns typed errors that can be checked using helper functions:

resp, err := client.SendText(ctx, msisdn, message)
if waga.IsUnauthorized(err) {
    // Handle authentication error
} else if waga.IsRateLimited(err) {
    // Handle rate limiting
}

For more examples and detailed usage, see the examples directory and README.md.

Index

Constants

View Source
const (
	// EventMessageQueued is emitted when a message is added to the queue
	EventMessageQueued = OutgoingEventMessageMessageQueued
	// EventMessageSent is emitted when a message is successfully sent
	EventMessageSent = OutgoingEventMessageMessageSent
	// EventMessageFailed is emitted when a message fails to send
	EventMessageFailed = OutgoingEventMessageMessageFailed
	// EventMessageIncoming is emitted when a message is received
	EventMessageIncoming = IncomingEventMessageMessageIncoming
)

Outgoing event type constants for convenience

View Source
const (
	// MessageTypeText represents a text message
	MessageTypeText = IncomingMessageTypeText
	// MessageTypeImage represents an image message
	MessageTypeImage = IncomingMessageTypeImage
	// MessageTypeVideo represents a video message
	MessageTypeVideo = IncomingMessageTypeVideo
	// MessageTypeAudio represents an audio message
	MessageTypeAudio = IncomingMessageTypeAudio
	// MessageTypeDocument represents a document message
	MessageTypeDocument = IncomingMessageTypeDocument
)

Incoming message type constants for convenience

View Source
const (
	// DefaultBaseURL is the default API endpoint
	DefaultBaseURL = "http://localhost:3000/api/v1"
	// DefaultTimeout is the default HTTP request timeout
	DefaultTimeout = 30 * time.Second
	// DefaultUserAgent is the default User-Agent header
	DefaultUserAgent = "WhatsApp-Gateway-SDK-Go/1.0"
)

Variables

View Source
var (
	// ErrUnauthorized is returned when authentication fails (401)
	ErrUnauthorized = &SDKError{Code: http.StatusUnauthorized, Message: "unauthorized"}
	// ErrBadRequest is returned for invalid requests (400)
	ErrBadRequest = &SDKError{Code: http.StatusBadRequest, Message: "bad request"}
	// ErrForbidden is returned when access is denied (403)
	ErrForbidden = &SDKError{Code: http.StatusForbidden, Message: "forbidden"}
	// ErrNotFound is returned when a resource is not found (404)
	ErrNotFound = &SDKError{Code: http.StatusNotFound, Message: "not found"}
	// ErrConflict is returned for conflicting requests (409)
	ErrConflict = &SDKError{Code: http.StatusConflict, Message: "conflict"}
	// ErrRateLimited is returned when rate limit is exceeded (429)
	ErrRateLimited = &SDKError{Code: http.StatusTooManyRequests, Message: "rate limited"}
	// ErrInternalServer is returned for server errors (500)
	ErrInternalServer = &SDKError{Code: http.StatusInternalServerError, Message: "internal server error"}
	// ErrInvalidSignature is returned when webhook signature verification fails
	ErrInvalidSignature = errors.New("invalid webhook signature")
	// ErrNotAuthenticated is returned when trying to use an authenticated method without setting a token
	ErrNotAuthenticated = errors.New("client not authenticated, call Register() or SetToken() first")
)

Common API errors These predefined errors can be used for comparison with errors.Is().

Functions

func ComputeSignature

func ComputeSignature(payload []byte, secret string) string

ComputeSignature computes the HMAC-SHA256 signature for a webhook payload. This is primarily useful for testing webhook implementations.

The returned signature is in the format "sha256=<hex_signature>", which matches the format used in the X-Webhook-Signature header.

Example:

payload := []byte(`{"event":"message.incoming"}`)
signature := waga.ComputeSignature(payload, "your-secret")
// Use signature to set X-Webhook-Signature header in test requests

func FormatGroupID

func FormatGroupID(groupID string) string

FormatGroupID formats a group ID to WhatsApp group JID format. If the groupID already contains "@", it is returned as-is. Otherwise, "@g.us" is appended to create a valid group JID.

Example:

groupID := waga.FormatGroupID("1234567890")  // "1234567890@g.us"

func FormatMSISDN

func FormatMSISDN(phoneNumber string) string

FormatMSISDN formats a phone number to WhatsApp JID format for individual contacts. If the phoneNumber already contains "@", it is returned as-is. Otherwise, "@s.whatsapp.net" is appended to create a valid JID.

Example:

msisdn := waga.FormatMSISDN("6281234567890")  // "6281234567890@s.whatsapp.net"

func IsBadRequest

func IsBadRequest(err error) bool

IsBadRequest checks if the error is a bad request error (HTTP 400). Returns true if err matches ErrBadRequest.

func IsConflict

func IsConflict(err error) bool

IsConflict checks if the error is a conflict error (HTTP 409). Returns true if err matches ErrConflict.

func IsForbidden

func IsForbidden(err error) bool

IsForbidden checks if the error is a forbidden error (HTTP 403). Returns true if err matches ErrForbidden.

func IsInternalServer

func IsInternalServer(err error) bool

IsInternalServer checks if the error is an internal server error (HTTP 500). Returns true if err matches ErrInternalServer.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound checks if the error is a not found error (HTTP 404). Returns true if err matches ErrNotFound.

func IsRateLimited

func IsRateLimited(err error) bool

IsRateLimited checks if the error is a rate limit error (HTTP 429). Returns true if err matches ErrRateLimited.

func IsUnauthorized

func IsUnauthorized(err error) bool

IsUnauthorized checks if the error is an unauthorized error (HTTP 401). Returns true if err matches ErrUnauthorized.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the main SDK client for interacting with the WhatsApp Gateway API. It provides methods for authentication, messaging, webhook management, and session control.

The client is safe for concurrent use.

func NewClient

func NewClient(opts ...Option) *Client

NewClient creates a new SDK client with the given options.

The client can be configured with functional options:

client := waga.NewClient(
    waga.WithBaseURL("https://api.example.com"),
    waga.WithTimeout(60*time.Second),
    waga.WithToken("existing-jwt-token"),
)

By default, the client uses:

func (*Client) DeleteMessage

func (c *Client) DeleteMessage(ctx context.Context, msisdn, messageID string) error

DeleteMessage deletes a previously sent message. The message will be removed from the chat for both sender and recipient.

The msisdn is the recipient's phone number, and messageID is the ID of the message to delete (returned by SendText or SendImage).

Requires authentication (call Register or SetToken first).

func (*Client) EditMessage

func (c *Client) EditMessage(ctx context.Context, msisdn, messageID, newMessage string) error

EditMessage edits a previously sent message. It replaces the message content with newMessage.

The msisdn is the recipient's phone number, and messageID is the ID of the message to edit (returned by SendText or SendImage).

Requires authentication (call Register or SetToken first).

func (*Client) GetIncomingMessages added in v0.2.0

func (c *Client) GetIncomingMessages(ctx context.Context, limit int) (*IncomingMessagesResponse, error)

GetIncomingMessages fetches the most recent incoming messages buffered by the gateway for the authenticated session, newest first.

The limit parameter caps the number of messages returned. If limit <= 0, the gateway substitutes its default (10). Values above the gateway's maximum (50) are clamped server-side.

Requires authentication (call Register or SetToken first).

func (*Client) GetLoginStatus

func (c *Client) GetLoginStatus(ctx context.Context) (*LoginStatus, error)

GetLoginStatus returns the current WhatsApp session status. It checks whether the WhatsApp session is authenticated and active.

Requires authentication (call Register or SetToken first).

func (*Client) GetPairCode

func (c *Client) GetPairCode(ctx context.Context) (*LoginPairResponse, error)

GetPairCode generates a pair code for WhatsApp login. The pair code is an 8-character code that can be used to link the device without scanning a QR code. Users can enter the code in WhatsApp > Linked Devices.

The pair code expires after the duration specified in ExpiresIn.

Requires authentication (call Register or SetToken first).

func (*Client) GetQRCode

func (c *Client) GetQRCode(ctx context.Context, format string) (*LoginQrResponse, error)

GetQRCode generates a QR code for WhatsApp login. The format parameter specifies the response format:

  • "json": returns a base64-encoded PNG image
  • "html": returns an HTML img tag with the embedded QR code

The QR code can be displayed to users for scanning with the WhatsApp mobile app to link their account. The QR code expires after the duration specified in ExpiresIn.

Requires authentication (call Register or SetToken first).

func (*Client) GetToken

func (c *Client) GetToken() string

GetToken returns the current JWT token stored in the client. Returns an empty string if no token has been set.

func (*Client) GetWebhook

func (c *Client) GetWebhook(ctx context.Context) (*WebhookResponse, error)

GetWebhook returns the currently registered webhook URL. Use this to check if a webhook is configured and retrieve its URL.

Requires authentication (call Register or SetToken first).

func (*Client) Health

func (c *Client) Health(ctx context.Context) (*HealthResponse, error)

Health checks the health status of the gateway service. This method does not require authentication and can be used to verify that the service is running and accessible.

func (*Client) Logout

func (c *Client) Logout(ctx context.Context) error

Logout logs out from the current WhatsApp session. This disconnects the WhatsApp account and invalidates the session. After logging out, you'll need to authenticate again using GetQRCode or GetPairCode.

Requires authentication (call Register or SetToken first).

func (*Client) React

func (c *Client) React(ctx context.Context, msisdn, messageID, emoji string) error

React adds a reaction emoji to a previously sent message. The emoji will appear on the message for all participants in the chat.

The msisdn is the recipient's phone number, messageID is the ID of the message to react to, and emoji is the emoji character(s) to use (e.g., "👍", "❤️").

Requires authentication (call Register or SetToken first).

func (*Client) Reconnect

func (c *Client) Reconnect(ctx context.Context) error

Reconnect attempts to reconnect to the existing WhatsApp session. Use this method if the connection to WhatsApp has been lost but the session is still valid. It attempts to restore the connection without requiring re-authentication.

Requires authentication (call Register or SetToken first).

func (*Client) Register

func (c *Client) Register(ctx context.Context, phoneNumber, secretKey string) (*RegisterResponse, error)

Register registers a new phone number with the WhatsApp Gateway service. It retrieves and stores a JWT token for subsequent API requests.

The phoneNumber should be in international format without the "+" symbol (e.g., "6281234567890"). The secretKey is provided by the gateway service.

After successful registration, the token is automatically stored in the client and used for all authenticated requests.

func (*Client) RegisterWebhook

func (c *Client) RegisterWebhook(ctx context.Context, url, hmacSecret string) error

RegisterWebhook registers a URL to receive webhook events. Webhooks allow you to receive real-time notifications for incoming and outgoing messages.

The url parameter is the endpoint where webhook events will be sent. The hmacSecret parameter is optional but highly recommended for verifying webhook signatures. If provided, all webhook payloads will include an X-Webhook-Signature header that can be verified using the WebhookVerifier.

Requires authentication (call Register or SetToken first).

func (*Client) SendImage

func (c *Client) SendImage(ctx context.Context, msisdn string, image io.Reader, caption string, isViewOnce bool) (*SendMessageResponse, error)

SendImage sends an image message to the specified recipient.

The msisdn parameter should be the recipient's phone number in WhatsApp JID format. The image parameter is an io.Reader containing the image data (JPEG, PNG, etc.). The caption parameter is optional text to accompany the image. The isViewOnce parameter, if true, sends the image as a view-once media that disappears after being viewed.

Example:

file, _ := os.Open("image.jpg")
defer file.Close()
resp, err := client.SendImage(ctx, msisdn, file, "Check this out!", false)

Requires authentication (call Register or SetToken first).

func (*Client) SendLocation added in v0.3.0

func (c *Client) SendLocation(ctx context.Context, msisdn string, latitude, longitude float64, name, address string) (*SendMessageResponse, error)

SendLocation sends a location message to the specified recipient.

The msisdn parameter should be the recipient's phone number in WhatsApp JID format. The latitude and longitude specify the geographic coordinates. The name and address parameters are optional metadata for the location pin.

Requires authentication (call Register or SetToken first).

func (*Client) SendPoll added in v0.3.0

func (c *Client) SendPoll(ctx context.Context, msisdn, question string, options []string, selectableCount int) (*SendMessageResponse, error)

SendPoll sends a poll message to the specified recipient.

The msisdn parameter should be the recipient's phone number in WhatsApp JID format. The question is the poll question text, and options is the list of answer choices. The selectableCount limits how many options a user can select (0 means no limit).

Requires authentication (call Register or SetToken first).

func (*Client) SendSticker added in v0.3.0

func (c *Client) SendSticker(ctx context.Context, msisdn string, sticker io.Reader) (*SendMessageResponse, error)

SendSticker sends a sticker message to the specified recipient.

The msisdn parameter should be the recipient's phone number in WhatsApp JID format. The sticker parameter is an io.Reader containing the sticker data (WebP format).

Example:

file, _ := os.Open("sticker.webp")
defer file.Close()
resp, err := client.SendSticker(ctx, msisdn, file)

Requires authentication (call Register or SetToken first).

func (*Client) SendText

func (c *Client) SendText(ctx context.Context, msisdn, message string) (*SendMessageResponse, error)

SendText sends a text message to the specified recipient.

The msisdn parameter should be the recipient's phone number in WhatsApp JID format. Use the FormatMSISDN() helper function to convert a phone number to the correct format:

msisdn := waga.FormatMSISDN("6281234567890")  // "6281234567890@s.whatsapp.net"

For group messages, use the group ID in the format "groupId@g.us".

Requires authentication (call Register or SetToken first).

func (*Client) SetToken

func (c *Client) SetToken(token string)

SetToken sets the JWT token for authentication. Use this method if you already have a valid token from a previous registration. The token will be used for all subsequent API requests that require authentication.

func (*Client) UnregisterWebhook

func (c *Client) UnregisterWebhook(ctx context.Context) error

UnregisterWebhook removes the currently registered webhook URL. After calling this method, webhook events will no longer be sent to the previously configured endpoint.

Requires authentication (call Register or SetToken first).

type ErrorResponse

type ErrorResponse struct {
	// Error is a human-readable error message
	Error string `json:"error"`
	// Code is the HTTP status code or API error code
	Code int `json:"code"`
}

ErrorResponse represents an error response from the API.

type HealthResponse

type HealthResponse struct {
	// Status is the health status of the service (e.g., "ok", "degraded")
	Status string `json:"status"`
	// Timestamp is the ISO 8601 timestamp of the health check
	Timestamp string `json:"timestamp"`
}

HealthResponse represents the response from Health.

type IncomingEventMessage

type IncomingEventMessage string

IncomingEventMessage represents the type of incoming message event.

const (
	// IncomingEventMessageMessageIncoming is emitted when a new message is received
	IncomingEventMessageMessageIncoming IncomingEventMessage = "message.incoming"
)

type IncomingMessage added in v0.2.0

type IncomingMessage struct {
	// MessageId is the unique identifier for the message
	MessageId string `json:"message_id"`
	// Chat is the chat ID where the message was received
	Chat string `json:"chat"`
	// From is the sender's phone number in WhatsApp JID format
	From string `json:"from"`
	// IsGroup indicates whether the message was received in a group chat
	IsGroup bool `json:"is_group"`
	// PushName is the display name of the sender
	PushName string `json:"push_name"`
	// Timestamp is the Unix timestamp when the message was received
	Timestamp int `json:"timestamp"`
	// Text is the text content of the message (for text messages)
	Text string `json:"text,omitempty"`
	// Type is the message type (text, image, video, audio, or document)
	Type IncomingMessageType `json:"type"`
	// Media contains media metadata for non-text messages.
	// Note: Url is not populated by /message/incoming in v1; only Type, MimeType,
	// Size, Filename, and Caption are filled. Use webhooks for media URLs.
	Media *IncomingMessageMediaInfo `json:"media,omitempty"`
}

IncomingMessage represents a single received message returned by the GET /message/incoming endpoint. Field names mirror IncomingWebhookPayload (minus the Event envelope) so consumers see a consistent vocabulary across the webhook delivery and the polled inbox.

type IncomingMessageMediaInfo

type IncomingMessageMediaInfo struct {
	// Type is the media type (image, video, audio, or document)
	Type IncomingMessageType `json:"type"`
	// Url is the direct URL to download the media file
	Url string `json:"url"`
	// MimeType is the MIME type of the media file
	MimeType string `json:"mime_type"`
	// Filename is the original filename of the media file (if applicable)
	Filename string `json:"filename,omitempty"`
	// Caption is the text caption accompanying the media (if applicable)
	Caption string `json:"caption,omitempty"`
	// Size is the file size in bytes (if available)
	Size int `json:"size,omitempty"`
}

IncomingMessageMediaInfo contains media information for incoming messages with attachments. This includes images, videos, audio, and documents.

type IncomingMessageType

type IncomingMessageType string

IncomingMessageType represents the type of incoming message content.

const (
	// IncomingMessageTypeText represents a text message
	IncomingMessageTypeText IncomingMessageType = "text"
	// IncomingMessageTypeImage represents an image message
	IncomingMessageTypeImage IncomingMessageType = "image"
	// IncomingMessageTypeVideo represents a video message
	IncomingMessageTypeVideo IncomingMessageType = "video"
	// IncomingMessageTypeAudio represents an audio message
	IncomingMessageTypeAudio IncomingMessageType = "audio"
	// IncomingMessageTypeDocument represents a document message
	IncomingMessageTypeDocument IncomingMessageType = "document"
)

type IncomingMessagesResponse added in v0.2.0

type IncomingMessagesResponse struct {
	// Success indicates the request completed successfully
	Success bool `json:"success"`
	// Timestamp is the Unix milliseconds when the response was generated
	Timestamp int64 `json:"timestamp"`
	// Count is the number of messages returned (<= requested limit)
	Count int `json:"count"`
	// Messages is the list of incoming messages, newest first
	Messages []IncomingMessage `json:"messages"`
}

IncomingMessagesResponse represents the response from the GET /message/incoming endpoint. Messages are sorted newest first.

type IncomingWebhookPayload

type IncomingWebhookPayload struct {
	// Event is the type of event (always "message.incoming")
	Event IncomingEventMessage `json:"event"`
	// Chat is the chat ID where the message was received
	Chat string `json:"chat"`
	// From is the sender's phone number in WhatsApp JID format
	From string `json:"from"`
	// IsGroup indicates whether the message was received in a group chat
	IsGroup bool `json:"is_group"`
	// MessageId is the unique identifier for the message
	MessageId string `json:"message_id"`
	// PushName is the display name of the sender
	PushName string `json:"push_name"`
	// Timestamp is the Unix timestamp when the message was received
	Timestamp int `json:"timestamp"`
	// Text is the text content of the message (for text messages)
	Text string `json:"text,omitempty"`
	// Type is the message type (text, image, video, audio, or document)
	Type IncomingMessageType `json:"type"`
	// Media contains media information for non-text messages
	Media *IncomingMessageMediaInfo `json:"media,omitempty"`
}

IncomingWebhookPayload represents the payload sent to webhooks for incoming message events. This contains all information about received messages.

type LoginPairResponse

type LoginPairResponse struct {
	// PairCode is the 8-character code for linking the device
	PairCode string `json:"pair_code"`
	// ExpiresIn is the number of seconds until the pair code expires
	ExpiresIn int `json:"expires_in"`
}

LoginPairResponse represents the response when requesting a pair code for login. The pair code can be used to link the WhatsApp account without scanning a QR code.

type LoginQrResponse

type LoginQrResponse struct {
	// QrCode is the base64-encoded QR code image or HTML img tag
	QrCode string `json:"qr_code"`
	// ExpiresIn is the number of seconds until the QR code expires
	ExpiresIn int `json:"expires_in"`
}

LoginQrResponse represents the response when requesting a QR code for login. The QR code can be displayed to users for scanning with the WhatsApp mobile app.

type LoginStatus

type LoginStatus struct {
	// Authenticated is true if the session is active and authenticated
	Authenticated bool `json:"authenticated"`
}

LoginStatus represents the response from GetLoginStatus. It indicates whether the WhatsApp session is currently authenticated.

type MessageDeleteRequest

type MessageDeleteRequest struct {
	// MessageId is the ID of the message to delete
	MessageId string `json:"message_id"`
	// Msisdn is the recipient's phone number in WhatsApp JID format
	Msisdn string `json:"msisdn"`
}

MessageDeleteRequest represents a request to delete a previously sent message.

type MessageEditRequest

type MessageEditRequest struct {
	// MessageId is the ID of the message to edit
	MessageId string `json:"message_id"`
	// Msisdn is the recipient's phone number in WhatsApp JID format
	Msisdn string `json:"msisdn"`
	// NewMessage is the updated message content
	NewMessage string `json:"new_message"`
}

MessageEditRequest represents a request to edit a previously sent message.

type MessageReactRequest

type MessageReactRequest struct {
	// MessageId is the ID of the message to react to
	MessageId string `json:"message_id"`
	// Msisdn is the recipient's phone number in WhatsApp JID format
	Msisdn string `json:"msisdn"`
	// Emoji is the emoji reaction to add
	Emoji string `json:"emoji"`
}

MessageReactRequest represents a request to add a reaction to a message.

type Option

type Option func(*Client)

Option configures the SDK client. Options are applied when creating a new client with NewClient.

func WithBaseURL

func WithBaseURL(url string) Option

WithBaseURL sets the base URL for the API. Use this option to connect to a different WhatsApp Gateway server.

The default base URL is "http://localhost:3000/api/v1".

Example:

client := waga.NewClient(waga.WithBaseURL("https://api.example.com"))

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient sets a custom HTTP client for making API requests. Use this option to customize HTTP behavior such as timeouts, redirects, transport settings, or proxy configuration.

Example:

httpClient := &http.Client{
    Timeout: 60 * time.Second,
    Transport: &http.Transport{...},
}
client := waga.NewClient(waga.WithHTTPClient(httpClient))

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the HTTP client timeout for API requests. The timeout applies to the entire HTTP request, including connection time, redirects, and reading the response body.

The default timeout is 30 seconds.

Example:

client := waga.NewClient(waga.WithTimeout(60 * time.Second))

func WithToken

func WithToken(token string) Option

WithToken sets a pre-existing JWT token for authentication. Use this option if you already have a valid token from a previous registration and want to skip the registration step.

The token will be used for all authenticated API requests.

Example:

client := waga.NewClient(waga.WithToken("your-jwt-token"))

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent sets a custom User-Agent header for API requests. Use this option to identify your application in API requests.

The default User-Agent is "WhatsApp-Gateway-SDK-Go/1.0".

Example:

client := waga.NewClient(waga.WithUserAgent("MyApp/1.0"))

type OutgoingEventMessage

type OutgoingEventMessage string

OutgoingEventMessage represents the type of outgoing message event. These events are sent to webhooks to track message delivery status.

const (
	// OutgoingEventMessageMessageQueued is emitted when a message is added to the queue
	OutgoingEventMessageMessageQueued OutgoingEventMessage = "message.queued"
	// OutgoingEventMessageMessageSent is emitted when a message is successfully sent
	OutgoingEventMessageMessageSent OutgoingEventMessage = "message.sent"
	// OutgoingEventMessageMessageFailed is emitted when a message fails to send
	OutgoingEventMessageMessageFailed OutgoingEventMessage = "message.failed"
)

type OutgoingWebhookPayload

type OutgoingWebhookPayload struct {
	// Event is the type of event that occurred
	Event OutgoingEventMessage `json:"event"`
	// JobId is the unique identifier for the message job
	JobId string `json:"job_id"`
	// To is the recipient's phone number in WhatsApp JID format
	To string `json:"to"`
	// PhoneNumber is the sender's phone number
	PhoneNumber string `json:"phone_number"`
	// Timestamp is the Unix timestamp of the event
	Timestamp int `json:"timestamp"`
	// MessageId is the unique identifier for the message
	MessageId string `json:"message_id"`
	// Metadata contains additional optional information about the message
	Metadata *map[string]interface{} `json:"metadata,omitempty"`
}

OutgoingWebhookPayload represents the payload sent to webhooks for outgoing message events. These events track the delivery status of messages sent through the API.

type RegisterRequest

type RegisterRequest struct {
	PhoneNumber string `json:"phone_number"`
	SecretKey   string `json:"secret_key"`
}

RegisterRequest represents a registration request for a new phone number. It contains the phone number and secret key required to authenticate with the WhatsApp Gateway service and obtain a JWT token.

type RegisterResponse

type RegisterResponse struct {
	// Token is the JWT authentication token for API requests
	Token string `json:"token"`
}

RegisterResponse represents the response from a successful registration. It contains the JWT token that should be used for subsequent API requests.

type SDKError

type SDKError struct {
	// Code is the HTTP status code or API error code
	Code int `json:"code"`
	// Message is the error message describing what went wrong
	Message string `json:"error"`
}

SDKError represents an error returned by the WhatsApp Gateway API. It contains both an HTTP status code and a human-readable error message.

func NewSDKError

func NewSDKError(code int, message string) *SDKError

NewSDKError creates a new SDKError with the given code and message. Use this function to create custom SDK errors with specific codes and messages.

Example:

err := waga.NewSDKError(418, "I'm a teapot")

func (*SDKError) Error

func (e *SDKError) Error() string

func (*SDKError) Is

func (e *SDKError) Is(target error) bool

Is implements the errors.Is interface for error comparison. It allows SDKError instances to be compared using errors.Is().

Example:

if errors.Is(err, waga.ErrUnauthorized) {
    // Handle unauthorized error
}

type SendLocationMessageRequest added in v0.3.0

type SendLocationMessageRequest struct {
	// Msisdn is the recipient's phone number in WhatsApp JID format
	Msisdn string `json:"msisdn"`
	// Latitude is the geographic latitude of the location
	Latitude float64 `json:"latitude"`
	// Longitude is the geographic longitude of the location
	Longitude float64 `json:"longitude"`
	// Name is the optional name of the location
	Name string `json:"name,omitempty"`
	// Address is the optional address of the location
	Address string `json:"address,omitempty"`
}

SendLocationMessageRequest represents a request to send a location message.

type SendMessageResponse

type SendMessageResponse struct {
	// Success indicates whether the message was successfully queued
	Success bool `json:"success"`
	// MessageId is the unique identifier for the sent message
	MessageId string `json:"message_id"`
}

SendMessageResponse represents the response from sending a message.

type SendMessageTextRequest

type SendMessageTextRequest struct {
	// Msisdn is the recipient's phone number in WhatsApp JID format (e.g., "6281234567890@s.whatsapp.net")
	Msisdn string `json:"msisdn"`
	// Message is the text content to send
	Message string `json:"message"`
}

SendMessageTextRequest represents a request to send a text message.

type SendPollMessageRequest added in v0.3.0

type SendPollMessageRequest struct {
	// Msisdn is the recipient's phone number in WhatsApp JID format
	Msisdn string `json:"msisdn"`
	// Question is the poll question text
	Question string `json:"question"`
	// Options is the list of poll options
	Options []string `json:"options"`
	// SelectableCount is the optional maximum number of options a user can select
	SelectableCount int `json:"selectable_count,omitempty"`
}

SendPollMessageRequest represents a request to send a poll message.

type SuccessResponse

type SuccessResponse struct {
	// Success indicates whether the operation completed successfully
	Success bool `json:"success"`
}

SuccessResponse represents a generic success response from API operations.

type WebhookRegisterRequest

type WebhookRegisterRequest struct {
	// Url is the endpoint URL where webhook events will be sent
	Url string `json:"url"`
	// HmacSecret is the optional secret key used to sign webhook payloads for verification
	HmacSecret *string `json:"hmac_secret,omitempty"`
}

WebhookRegisterRequest represents a request to register a webhook URL. Webhooks receive real-time notifications for message events.

type WebhookResponse

type WebhookResponse struct {
	// URL is the currently registered webhook endpoint
	URL string `json:"url"`
}

WebhookResponse represents the response from GetWebhook.

type WebhookVerifier

type WebhookVerifier struct {
	// contains filtered or unexported fields
}

WebhookVerifier handles verification of incoming webhook signatures. It ensures that webhook payloads are genuine and haven't been tampered with by verifying HMAC-SHA256 signatures.

Security considerations:

  • Always keep your HMAC secret secure and never expose it in client-side code
  • Use constant-time comparison to prevent timing attacks
  • Reject webhooks with invalid or missing signatures

func NewWebhookVerifier

func NewWebhookVerifier(secret string) *WebhookVerifier

NewWebhookVerifier creates a new webhook verifier with the given HMAC secret. The secret must match the one configured when registering the webhook.

Example:

verifier := waga.NewWebhookVerifier("your-hmac-secret")

func (*WebhookVerifier) ParseIncomingWebhook

func (v *WebhookVerifier) ParseIncomingWebhook(payload []byte, signature string) (*IncomingWebhookPayload, error)

ParseIncomingWebhook parses and verifies an incoming message webhook payload. It returns the parsed payload if the signature is valid, or an error otherwise.

This method verifies the signature first, then unmarshals the JSON payload. If either step fails, it returns an error.

Example:

body, _ := io.ReadAll(r.Body)
signature := r.Header.Get("X-Webhook-Signature")
webhook, err := verifier.ParseIncomingWebhook(body, signature)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Message from %s: %s\n", webhook.From, webhook.Text)

func (*WebhookVerifier) ParseOutgoingWebhook

func (v *WebhookVerifier) ParseOutgoingWebhook(payload []byte, signature string) (*OutgoingWebhookPayload, error)

ParseOutgoingWebhook parses and verifies an outgoing event webhook payload. It returns the parsed payload if the signature is valid, or an error otherwise.

Outgoing webhooks track the delivery status of messages sent through the API. This method verifies the signature first, then unmarshals the JSON payload.

Example:

body, _ := io.ReadAll(r.Body)
signature := r.Header.Get("X-Webhook-Signature")
webhook, err := verifier.ParseOutgoingWebhook(body, signature)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Message %s: %s\n", webhook.Event, webhook.MessageId)

func (*WebhookVerifier) VerifySignature

func (v *WebhookVerifier) VerifySignature(payload []byte, signatureHeader string) bool

VerifySignature validates the X-Webhook-Signature header from a webhook request. The signature header format is: "sha256=<hex_signature>".

This method uses constant-time comparison to prevent timing attacks. It returns false if the signature is missing, malformed, or doesn't match.

Example:

signature := r.Header.Get("X-Webhook-Signature")
if !verifier.VerifySignature(body, signature) {
    return ErrInvalidSignature
}

Directories

Path Synopsis
Example usage of the WhatsApp Gateway SDK
Example usage of the WhatsApp Gateway SDK

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL