Ruby SDK Reference
Everything you need to integrate WhatsApp messaging into your Ruby application.
Installation
Add the gem to your Gemfile:
gem "whatsrb_cloud"Then run:
bundle installOr install directly:
gem install whatsrb_cloudRequirements
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
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.newPer-client
client = WhatsrbCloud::Client.new(
api_key: "wrb_live_xxx",
base_url: "https://api.whatsrb.com",
timeout: 10
)Rails initializer
# config/initializers/whatsrb_cloud.rb
WhatsrbCloud.configure do |c|
c.api_key = Rails.application.credentials.whatsrb_cloud_api_key
endSessions
A session represents a linked WhatsApp account. Create a session, scan the QR code, and start sending messages.
Create a session
session = client.sessions.create(name: "Support Bot")
session.id # => "sess_abc123"
session.status # => "connecting"List sessions
list = client.sessions.list
list.data # => [Session, Session, ...]
list.meta # => { "total" => 3, "plan_limit" => 5 }Retrieve a session
session = client.sessions.retrieve("sess_abc123")
session.name # => "Support Bot"
session.status # => "connected"
session.connected? # => true
session.phone_number # => "+33612345678"Delete a session
client.sessions.delete("sess_abc123") # => true| Attribute | Type | Description |
|---|---|---|
| id | String | Session identifier (read-only) |
| name | String | Display name |
| status | String | connecting | qr_pending | connected | disconnected |
| phone_number | String | Linked phone number (after connection) |
| qr_code | String | Base64 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
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
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
endQR 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
# 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
# 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
messages = client.messages("sess_abc").list
messages.data.each do |msg|
puts "#{msg.message_type}: #{msg.content} (#{msg.status})"
endRetrieve a message
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 +0000Phone number format
Always use the international format with country code: +33612345678 (not 0612345678).
| Attribute | Type | Description |
|---|---|---|
| id | String | Message identifier |
| to | String | Recipient phone number |
| message_type | String | text | image | document | video | audio | location | contact |
| content | String | Message content or media URL |
| status | String | queued → sent → delivered → read | failed |
| sent_at | Time | Timestamp when sent |
| delivered_at | Time | Timestamp when delivered |
Webhooks
Receive real-time events when messages are received, delivered, or when sessions change status.
Create a webhook
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
client.webhooks.list.data.each do |wh|
puts "#{wh.url} — #{wh.events.join(', ')}"
endUpdate a webhook
client.webhooks.update("wh_abc",
events: ["message.received", "message.delivered", "session.disconnected"]
)Delete a webhook
client.webhooks.delete("wh_abc") # => trueAvailable events
| Event | Description |
|---|---|
| message.received | A message was received on a session |
| message.sent | A message was sent successfully |
| message.delivered | A message was delivered to the recipient |
| message.read | A message was read by the recipient |
| message.failed | A message failed to send |
| session.connected | A session connected to WhatsApp |
| session.disconnected | A 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
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
endSecurity
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.
usage = client.usage
usage["messages_sent"] # => 150
usage["sessions_active"] # => 3
usage["plan"] # => "pro"
usage["plan_limit"] # => 2_000Error Handling
The SDK raises specific error classes based on HTTP status codes.
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| Exception | Status | Description |
|---|---|---|
| AuthenticationError | 401 | Invalid or missing API key |
| ForbiddenError | 403 | Plan limit reached or insufficient permissions |
| NotFoundError | 404 | Resource not found |
| ValidationError | 422 | Invalid request parameters |
| ConflictError | 409 | Session not in expected state (e.g. not connected) |
| RateLimitError | 429 | Too many requests — check retry_after |
| ServerError | 5xx | Internal 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
# config/initializers/whatsrb_cloud.rb
WhatsrbCloud.configure do |c|
c.api_key = Rails.application.credentials.whatsrb_cloud_api_key
end2. Service object
# 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
end3. Send from anywhere
# 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
end4. Receive webhooks
# 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
end5. Full flow in Rake task
# 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
endBackground jobs
Always send messages from background jobs (Sidekiq, Solid Queue) to avoid blocking your web requests. The API call typically takes 200-500ms.