Documentation

Ruby SDK Reference

Everything you need to integrate WhatsApp messaging into your Ruby application.

Installation

Add the gem to your Gemfile:

ruby
gem "whatsrb_cloud"

Then run:

shell
bundle install

Or install directly:

shell
gem install whatsrb_cloud

Requirements

Ruby 3.1+ — no external dependencies (pure Net::HTTP).

Configuration

Configure globally or per-client. Get your API key from theAPI Keys page.

Global configuration

ruby
WhatsrbCloud.configure do |c|
  c.api_key  = "wrb_live_xxx"
  c.base_url = "https://api.whatsrb.com"  # default
  c.timeout  = 30                          # seconds
end

client = WhatsrbCloud::Client.new

Per-client

ruby
client = WhatsrbCloud::Client.new(
  api_key:  "wrb_live_xxx",
  base_url: "https://api.whatsrb.com",
  timeout:  10
)

Rails initializer

ruby
# config/initializers/whatsrb_cloud.rb
WhatsrbCloud.configure do |c|
  c.api_key = Rails.application.credentials.whatsrb_cloud_api_key
end

Sessions

A session represents a linked WhatsApp account. Create a session, scan the QR code, and start sending messages.

Create a session

ruby
session = client.sessions.create(name: "Support Bot")
session.id      # => "sess_abc123"
session.status  # => "connecting"

List sessions

ruby
list = client.sessions.list
list.data   # => [Session, Session, ...]
list.meta   # => { "total" => 3, "plan_limit" => 5 }

Retrieve a session

ruby
session = client.sessions.retrieve("sess_abc123")
session.name          # => "Support Bot"
session.status        # => "connected"
session.connected?    # => true
session.phone_number  # => "+33612345678"

Delete a session

ruby
client.sessions.delete("sess_abc123")  # => true
AttributeTypeDescription
idStringSession identifier (read-only)
nameStringDisplay name
statusStringconnecting | qr_pending | connected | disconnected
phone_numberStringLinked phone number (after connection)
qr_codeStringBase64 QR code PNG (when status = qr_pending)

QR Code Pairing

After creating a session, poll for the QR code. The user scans it with WhatsApp > Linked Devices > Link a Device.

Polling with block

ruby
session = client.sessions.create(name: "My Bot")

session.wait_for_qr(timeout: 60, interval: 2) do |qr_data|
  # qr_data is a base64-encoded PNG
  puts "Scan this QR code!"

  # Save to file
  File.write("qr.png", Base64.decode64(qr_data))

  # Or display in terminal (with catimg, kitty, etc.)
end

puts "Connected as #{session.phone_number}"

Manual polling

ruby
session = client.sessions.create(name: "My Bot")

loop do
  session.reload
  break if session.connected?

  if session.status == "qr_pending" && session.qr_code
    puts "QR ready — scan it!"
  end

  sleep 2
end

QR Expiration

QR codes expire after ~60 seconds. A new one is generated automatically. The block receives each new QR code as it arrives.

Messages

Send and retrieve messages scoped to a session. The session must be connected.

Send a text message

ruby
# Via session object
msg = session.send_message(to: "+33612345678", text: "Hello!")
msg.id      # => "msg_xyz"
msg.status  # => "queued"

# Via client
msg = client.messages("sess_abc").create(
  to: "+33612345678",
  text: "Hello!"
)

Send media

ruby
# Image
session.send_image(to: "+33612345678", url: "https://example.com/photo.jpg")

# Document
session.send_document(to: "+33612345678", url: "https://example.com/invoice.pdf")

# Video
session.send_video(to: "+33612345678", url: "https://example.com/clip.mp4")

# Audio
session.send_audio(to: "+33612345678", url: "https://example.com/voice.ogg")

# Location
session.send_location(
  to: "+33612345678",
  latitude: 48.8566,
  longitude: 2.3522
)

# Contact card
session.send_contact(
  to: "+33612345678",
  name: "John Doe",
  phone: "+33698765432"
)

List messages

ruby
messages = client.messages("sess_abc").list
messages.data.each do |msg|
  puts "#{msg.message_type}: #{msg.content} (#{msg.status})"
end

Retrieve a message

ruby
msg = client.messages("sess_abc").retrieve("msg_xyz")
msg.status        # => "delivered"
msg.sent_at       # => 2026-02-26 12:00:00 +0000
msg.delivered_at  # => 2026-02-26 12:00:03 +0000

Phone number format

Always use the international format with country code: +33612345678 (not 0612345678).

AttributeTypeDescription
idStringMessage identifier
toStringRecipient phone number
message_typeStringtext | image | document | video | audio | location | contact
contentStringMessage content or media URL
statusStringqueued → sent → delivered → read | failed
sent_atTimeTimestamp when sent
delivered_atTimeTimestamp when delivered

Webhooks

Receive real-time events when messages are received, delivered, or when sessions change status.

Create a webhook

ruby
wh = client.webhooks.create(
  url: "https://yourapp.com/webhooks/whatsrb",
  events: ["message.received", "session.connected"]
)
wh.id      # => "wh_abc"
wh.secret  # => "whsec_..." (only returned on create — save it!)

List webhooks

ruby
client.webhooks.list.data.each do |wh|
  puts "#{wh.url} — #{wh.events.join(', ')}"
end

Update a webhook

ruby
client.webhooks.update("wh_abc",
  events: ["message.received", "message.delivered", "session.disconnected"]
)

Delete a webhook

ruby
client.webhooks.delete("wh_abc")  # => true

Available events

EventDescription
message.receivedA message was received on a session
message.sentA message was sent successfully
message.deliveredA message was delivered to the recipient
message.readA message was read by the recipient
message.failedA message failed to send
session.connectedA session connected to WhatsApp
session.disconnectedA session was disconnected

Signature Verification

Every webhook request includes an X-Webhook-Signature header (HMAC-SHA256). Always verify the signature to ensure the payload is authentic.

Rails controller example

ruby
class WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    payload   = request.body.read
    signature = request.headers["X-Webhook-Signature"]

    unless WhatsrbCloud::WebhookSignature.verify?(
      payload:   payload,
      secret:    ENV["WHATSRB_WEBHOOK_SECRET"],
      signature: signature
    )
      head :unauthorized
      return
    end

    event = JSON.parse(payload)

    case event["type"]
    when "message.received"
      handle_incoming(event["data"])
    when "session.disconnected"
      handle_disconnect(event["data"])
    end

    head :ok
  end
end

Security

Never skip signature verification in production. Store the webhook secret securely (credentials, env vars) — never hardcode it.

Usage & Billing

Check your current plan usage and limits.

ruby
usage = client.usage
usage["messages_sent"]    # => 150
usage["sessions_active"]  # => 3
usage["plan"]             # => "pro"
usage["plan_limit"]       # => 2_000

Error Handling

The SDK raises specific error classes based on HTTP status codes.

ruby
begin
  session.send_message(to: "+33612345678", text: "Hello!")
rescue WhatsrbCloud::AuthenticationError
  # 401 — Invalid or missing API key
rescue WhatsrbCloud::ForbiddenError
  # 403 — Plan limit reached
rescue WhatsrbCloud::NotFoundError
  # 404 — Session or message not found
rescue WhatsrbCloud::ValidationError
  # 422 — Invalid parameters
rescue WhatsrbCloud::ConflictError
  # 409 — Session not connected
rescue WhatsrbCloud::RateLimitError => e
  # 429 — Too many requests
  sleep(e.retry_after || 1)
  retry
rescue WhatsrbCloud::ServerError
  # 5xx — Server error, retry later
rescue WhatsrbCloud::Error => e
  # Catch-all
  puts "#{e.message} (HTTP #{e.status})"
end
ExceptionStatusDescription
AuthenticationError401Invalid or missing API key
ForbiddenError403Plan limit reached or insufficient permissions
NotFoundError404Resource not found
ValidationError422Invalid request parameters
ConflictError409Session not in expected state (e.g. not connected)
RateLimitError429Too many requests — check retry_after
ServerError5xxInternal server error

Rate Limits

API requests are limited to 100/second. The RateLimitError exposes a retry_after attribute (in seconds) when available.

Rails Integration

A complete example showing how to integrate WhatsRB Cloud into a Rails app — from setup to sending messages and receiving webhooks.

1. Initializer

ruby
# config/initializers/whatsrb_cloud.rb
WhatsrbCloud.configure do |c|
  c.api_key = Rails.application.credentials.whatsrb_cloud_api_key
end

2. Service object

ruby
# app/services/whatsapp_sender.rb
class WhatsappSender
  def initialize(session_id: nil)
    @client = WhatsrbCloud::Client.new
    @session_id = session_id || default_session_id
  end

  def send_text(to:, text:)
    session.send_message(to: to, text: text)
  end

  def send_image(to:, url:)
    session.send_image(to: to, url: url)
  end

  private

  def session
    @session ||= @client.sessions.retrieve(@session_id)
  end

  def default_session_id
    # Cache the connected session ID
    Rails.cache.fetch("whatsrb:default_session", expires_in: 5.minutes) do
      @client.sessions.list.data.find(&:connected?)&.id
    end
  end
end

3. Send from anywhere

ruby
# In a controller
class OrdersController < ApplicationController
  def confirm
    @order = Order.find(params[:id])
    @order.confirm!

    WhatsappSender.new.send_text(
      to: @order.customer_phone,
      text: "Your order ##{@order.number} is confirmed!"
    )

    redirect_to @order, notice: "Order confirmed & customer notified."
  end
end

# In a background job
class OrderNotificationJob < ApplicationJob
  def perform(order_id)
    order = Order.find(order_id)

    WhatsappSender.new.send_text(
      to: order.customer_phone,
      text: "Your order ##{order.number} has been shipped!"
    )
  end
end

4. Receive webhooks

ruby
# config/routes.rb
post "webhooks/whatsrb", to: "whatsrb_webhooks#create"

# app/controllers/whatsrb_webhooks_controller.rb
class WhatsrbWebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    payload   = request.body.read
    signature = request.headers["X-Webhook-Signature"]

    unless WhatsrbCloud::WebhookSignature.verify?(
      payload:   payload,
      secret:    Rails.application.credentials.whatsrb_webhook_secret,
      signature: signature
    )
      head :unauthorized and return
    end

    event = JSON.parse(payload)

    case event["type"]
    when "message.received"
      handle_incoming(event["data"])
    when "session.disconnected"
      notify_admin("WhatsApp session disconnected!")
    end

    head :ok
  end

  private

  def handle_incoming(data)
    IncomingMessage.create!(
      from:    data["from"],
      body:    data["content"],
      received_at: Time.current
    )
  end

  def notify_admin(message)
    AdminMailer.alert(message).deliver_later
  end
end

5. Full flow in Rake task

ruby
# lib/tasks/whatsapp.rake
namespace :whatsapp do
  desc "Setup a new WhatsApp session"
  task setup: :environment do
    client  = WhatsrbCloud::Client.new
    session = client.sessions.create(name: "Production Bot")

    puts "Session created: #{session.id}"
    puts "Waiting for QR code..."

    session.wait_for_qr(timeout: 120) do |qr_data|
      File.write("tmp/qr.png", Base64.decode64(qr_data))
      puts "QR saved to tmp/qr.png — scan it with WhatsApp!"
    end

    puts "Connected as #{session.phone_number}"
  end
end

Background jobs

Always send messages from background jobs (Sidekiq, Solid Queue) to avoid blocking your web requests. The API call typically takes 200-500ms.