Ruby Whatsapp SDK

The SDK provides a set of operations and classes to use the Whatsapp API.
Send stickers, messages, audio, videos, locations, react and reply to messages or just ask for the phone numbers through this library in a few steps!
Table of Contents
Features
- Send Messages: Text, images, audio, video, documents, stickers, locations, and contacts.
- React & Reply: React to messages with emojis and reply to specific messages.
- Message Templates: Send pre-approved message templates for notifications.
- Interactive Messages: List messages and reply buttons.
- Media Management: Upload, download, and delete media files.
- Phone Numbers: Register, deregister, and query phone numbers.
- Business Profile: Get and update business profile details.
- Templates Management: Create, list, and delete message templates.
Demo
https://user-images.githubusercontent.com/11672878/173238826-6fc0a6f8-d0ee-4eae-8947-7dfd3b8b3446.mov
Check the Ruby on Rails Whatsapp example repository that uses this library to send messages.
Installation
Add this line to your application’s Gemfile:
And then execute:
Or install it yourself as:
$ gem install whatsapp_sdk
Quick Start
There are three primary resources, Messages, Media and PhoneNumbers. Messages allows users to send any message (text, audio, location, video, image, etc.). Media allows users to manage media, and Phone Numbers enable clients to query the associated phone numbers.
To use Messages, Media or PhoneNumbers, you need to initialize the Client that contains auth information. There are two ways to do it.
Option 1) Use an initializer
# config/initializers/whatsapp_sdk.rb
WhatsappSdk.configure do |config|
config.access_token = ACCESS_TOKEN
config.api_version = API_VERSION
config.logger = Logger.new(STDOUT) # optional, Faraday logger to attach
config.logger_options = { bodies: true } # optional, they are all valid logger_options for Faraday
end
Notes:
- Optionally, you can specify the desired API version to use (defaults to the latest version if omitted).
Available API version can be found here.
- You can attach a logger. More Details on Faraday Logger Options are here.
Option 2) Create a Client instance :
# Basic usage
client = WhatsappSdk::Api::Client.new("<ACCESS TOKEN>") # replace this with a valid access token
# With optional logger and API version
logger = Logger.new(STDOUT)
logger_options = { bodies: true }
client = WhatsappSdk::Api::Client.new("<ACCESS TOKEN>", "<API VERSION>", logger, logger_options)
Clients reuse Faraday connections per host and multipart mode. To use persistent
HTTP connections, install faraday-net_http_persistent in your application and require
it before selecting the adapter:
require "faraday/net_http_persistent"
client = WhatsappSdk::Api::Client.new(
"<ACCESS TOKEN>", "v25.0",
adapter: :net_http_persistent,
request_options: { open_timeout: 5, timeout: 15 },
multipart_request_options: { timeout: 60 }
)
# Make requests with this client. At shutdown, when no request is running, release its connections.
client.close
The default adapter and its timeout defaults are unchanged. These options apply to
Faraday API requests; download_file uses Net::HTTP separately. The SDK does not
retry requests automatically. Each client keeps its own token and connection cache.
To add Faraday middleware, such as faraday-retry, pass middleware:. It is called
with each new Faraday builder, before the adapter is set:
require "faraday/retry"
client = WhatsappSdk::Api::Client.new(
"<ACCESS TOKEN>", "v25.0",
middleware: ->(builder) { builder.request(:retry, max: 2) }
)
The same options work with the global configuration, which shares one client across
API objects. Client.new also uses them as defaults, so clients built without these
keywords get the configured transport:
WhatsappSdk.configure do |config|
config.access_token = "<ACCESS TOKEN>"
config.adapter = :net_http_persistent
config.request_options = { open_timeout: 5, timeout: 15 }
config.middleware = ->(builder) { builder.request(:retry, max: 2) }
end
Hash settings are stored frozen. Assign a new hash to change them, so the shared
client is rebuilt; unknown request options raise ArgumentError when assigned.
1) Create a Meta Business app
2) Add Whatsapp to your Application
3) Add a phone number to your account
Try sending a message to your phone in the UI.
4) Copy the ACCESS_TOKEN, the SENDER_ID, the BUSINESS_ID and the RECIPIENT_NUMBER
5) Use the GEM to interact with Whatsapp
Example:
1. Install the gem by running `gem install whatsapp_sdk` in the gem.
2. Open the irb terminal by running `irb`
3. `require "whatsapp_sdk"`
4. Set up the `ACCESS_TOKEN`, the `SENDER_ID`, the `BUSINESS_ID` and the `RECIPIENT_NUMBER` in variables.
```ruby
ACCESS_TOKEN = "EAAZAvvr0DZBs0BABRLF8zohP5Epc6pyNu"
BUSINESS_ID = 1213141516171819
SENDER_ID = 1234567891011
RECIPIENT_NUMBER = 12398765432
```
5. Configure the Client by running
```ruby
WhatsappSdk.configure do |config|
config.access_token = ACCESS_TOKEN
end
```
6. Try the Phone Numbers API or Messages API
Phone Numbers API
```ruby
registered_number = client.phone_numbers.registered_number(SENDER_ID)
```
Messages API
```ruby
message_sent = client.messages.send_text(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER,
message: "Hey there! it's Whatsapp Ruby SDK")
```
Check the [example.rb file](https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk/blob/main/example.rb) for more examples.
Usage Overview
client = WhatsappSdk::Api::Client.new("<ACCESS TOKEN>") # replace this with a valid access token
client.phone_numbers.register_number(SENDER_ID, 123456) # register the phone number to upload media and send messages from.
# send a text and a location
client.messages.send_text(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, message: "Hey there! it's Whatsapp Ruby SDK")
client.messages.send_location(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, longitude: -75.6898604, latitude: 45.4192206, name: "Ignacio", address: "My house")
# upload a photo and send it
image = client.media.upload(sender_id: SENDER_ID, file_path: "test/fixtures/assets/whatsapp.png", type: "image/png")
client.messages.send_image(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, image_id: image.id)
# upload a sticker and send it
sticker = client.media.upload(sender_id: SENDER_ID, file_path: "test/fixtures/assets/sticker.webp", type: "image/webp")
client.messages.send_sticker(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, sticker_id: sticker.id)
APIs
Templates
```ruby
# Get list of templates
client.templates.list(business_id: BUSINESS_ID)
# Get Message Template Namespace
# The message template namespace is required to send messages using the message templates.
client.templates.get_message_template_namespace(business_id: BUSINESS_ID)
# Get template by id
template = client.templates.get(template_id: id)
# Create a template
client.templates.create(
business_id: BUSINESS_ID, name: "seasonal_promotion", language: "en_US", category: "MARKETING",
components_json: components_json, allow_category_change: true
)
# Delete a template
client.templates.delete(business_id: BUSINESS_ID, name: "my_name") # delete by name
# Get templates analytics
client.templates.template_analytics(
business_id: BUSINESS_ID, start_timestamp: 1767236400,
end_timestamp: 1767322799, template_ids: [id]
)
```
Business Profile API
```ruby
# Get the details of your business
client.business_profiles.get(SENDER_ID)
# Update the details of your business
client.business_profiles.update(phone_number_id: SENDER_ID, params: { about: "A very cool business" } )
```
Business Account API
```ruby
# Get the details of your business account
client.business_accounts.get(BUSINESS_ID)
# Get some fields of your business account
client.business_accounts.get(BUSINESS_ID, fields: ["id", "name"])
# Update your business account
# Note: You can only update the `name` and `timezone_id` fields of your business account.
client.business_accounts.update(business_id: BUSINESS_ID, params: { name: "My new business name", timezone_id: "1" })
```
Phone numbers API
```ruby
# Get the list of phone numbers registered
client.phone_numbers.list(BUSINESS_ID)
# Get a phone number by id
client.phone_numbers.get(SENDER_ID)
# Register a phone number
client.phone_numbers.register_number(SENDER_ID, pin)
# Deregister a phone number
client.phone_numbers.deregister_number(SENDER_ID)
```
Onboard a phone number
phone = client.phone_numbers.add(
business_id: WABA_ID, country_code: "55", phone_number: "11999999999", verified_name: "Example Store"
)
client.phone_numbers.request_code(phone_number_id: phone.id, code_method: "SMS", language: "pt_BR")
client.phone_numbers.verify_code(phone_number_id: phone.id, code: "001234")
client.phone_numbers.register_number(phone.id, "123456")
client.business_accounts.subscribe_app(business_id: WABA_ID)
Each call is explicit. Adding a number does not request a code, verify it, or register it.
Code delivery accepts SMS or VOICE and is not retried automatically. Account permissions,
onboarding state, and storage of identifiers remain the application’s responsibility.
See Meta onboarding.
Configure the public key used by a phone number’s Flow data exchange endpoint:
client.phone_numbers.set_public_key(phone_number_id: PHONE_NUMBER_ID,
business_public_key: File.read("public.pem"))
key = client.phone_numbers.get_public_key(phone_number_id: PHONE_NUMBER_ID)
key.dig("data", 0, "business_public_key_signature_status") # VALID or MISMATCH
Pass the PEM text of a 2048-bit RSA public key. Private key storage and request decryption
remain in your application. See Meta’s encryption setup.
The onboarding operations (phone_numbers.add, request_code, verify_code, register_number,
business_accounts.subscribe_app) and phone_numbers.set_public_key also accept raw_response: true,
which returns the unmodified Faraday::Response. Their default return values stay the same.
```ruby
# Upload a media
client.media.upload(sender_id: SENDER_ID, file_path: "tmp/whatsapp.png", type: "image/png")
# Get a media
media = client.media.get(media_id: MEDIA_ID)
# Download media
client.media.download(url: MEDIA_URL, file_path: 'tmp/downloaded_whatsapp.png', media_type: "image/png")
# Delete a media
client.media.delete(media_id: MEDIA_ID)
```
Messages API
```ruby
# Send a text message
client.messages.send_text(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, message: "hola")
# Read a message
client.messages.read_message(sender_id: SENDER_ID, message_id: "wamid.HBgLMTM0M12345678910=")
# Note: To get the `message_id` you can set up [Webhooks](https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/components) that will listen and fire an event when a message is received.
# Send a reaction to message
# To send a reaction to a message, you need to obtain the message id and look for the emoji's unicode you want to use.
client.messages.send_reaction(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, message_id: "12345", emoji: "\u{1f550}")
client.messages.send_reaction(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, message_id: "12345", emoji: "⛄️")
# Reply to a message
# To reply to a message, just include the id of the message in the `client.messages` methods. For example, to reply to a text message include the following:
client.messages.send_text(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, message: "I'm a reply", message_id: "wamid.1234")
# Send a location message
client.messages.send_location(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER,
longitude: 45.4215, latitude: 75.6972, name: "nacho", address: "141 cooper street"
)
# Send an image message
# It uses a link or an image_id.
# with a link
client.messages.send_image(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, link: "image_link", caption: "Ignacio Chiazzo Profile")
# with an image id
client.messages.send_image(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, image_id: "1234", caption: "Ignacio Chiazzo Profile")
# Send an audio message
# It uses a link or an audio_id.
# with a link
client.messages.send_audio(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, link: "audio_link")
# with an audio id
client.messages.send_audio(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, audio_id: "1234")
# Send a document message
# It uses a link or a document_id.
# with a link
client.messages.send_document(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, link: "document_link", caption: "Ignacio Chiazzo")
# with a document id
client.messages.send_document(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, document_id: "1234", caption: "Ignacio Chiazzo")
# Note, you can specify the filename via argument [`filename`](https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages).
# Send a sticker message
# It could use a link or a sticker_id.
# with a link
client.messages.send_sticker(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, link: "link")
# with a sticker_id
client.messages.send_sticker(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, sticker_id: "1234")
# Send contacts message
# To send a contact, you need to create a Contact instance object that contain objects embedded like `addresses`, `birthday`, `emails`, `name`, `org`. See this [guide](/test/contact_helper.rb) to learn how to create contacts objects.
client.messages.send_contacts(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, contacts: [create_contact(params)])
# Alternatively, you could pass a plain json like this:
client.messages.send_contacts(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, contacts_json: {...})
# Send a template message
# WhatsApp message templates are specific message formats that businesses use to send out notifications or customer care messages to people that have opted in to notifications. Messages can include appointment reminders, shipping information, issue resolution or payment updates.
# Before sending a message template, you need to create one. visit the [Official API Documentation](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates)
```
Component's example
```ruby
currency = WhatsappSdk::Resource::Currency.new(code: "USD", amount: 1000, fallback_value: "1000")
date_time = WhatsappSdk::Resource::DateTime.new(fallback_value: "2020-01-01T00:00:00Z")
media_component = WhatsappSdk::Resource::MediaComponent.new(type: "image", link: "http(s)://URL")
location = WhatsappSdk::Resource::Location.new(
latitude: 25.779510, longitude: -80.338631, name: "miami store", address: "820 nw 87th ave, miami, fl"
)
parameter_image = WhatsappSdk::Resource::ParameterObject.new(type: "image", image: media_component)
parameter_text = WhatsappSdk::Resource::ParameterObject.new(type: "text", text: "TEXT_STRING")
parameter_currency = WhatsappSdk::Resource::ParameterObject.new(type: "currency", currency: currency)
parameter_date_time = WhatsappSdk::Resource::ParameterObject.new(type: "date_time", date_time: date_time)
parameter_location = WhatsappSdk::Resource::ParameterObject.new(type: "location", location: location)
header_component = WhatsappSdk::Resource::Component.new(type: "header", parameters: [parameter_image])
body_component = WhatsappSdk::Resource::Component.new(
type: "body",
parameters: [parameter_text, parameter_currency, parameter_date_time]
)
button_component1 = WhatsappSdk::Resource::Component.new(
type: "button",
index: 0,
sub_type: "quick_reply",
parameters: [
WhatsappSdk::Resource::ButtonParameter.new(type: "payload", payload: "PAYLOAD")
]
)
button_component2 = WhatsappSdk::Resource::Component.new(
type: "button",
index: 1,
sub_type: "quick_reply",
parameters: [
WhatsappSdk::Resource::ButtonParameter.new(type: "payload", payload: "PAYLOAD")
]
)
location_component = WhatsappSdk::Resource::Component.new(type: "header", parameters: [parameter_location])
client.messages.send_template(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, name: "hello_world", language: "en_US", components: [...]
)
```
Alternatively, you could pass a plain json like this:
```ruby
client.messages.send_template(sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER, name: "hello_world", language: "en_US", components_json: [{...}])
```
**Send interactive messages**
Visit the [Official API Documentation](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages#interactive-messages)
List Message's example
```ruby
interactive_header = WhatsappSdk::Resource::InteractiveHeader.new(type: "text", text: "I am the header!")
interactive_body = WhatsappSdk::Resource::InteractiveBody.new(text: "I am the body!")
interactive_footer = WhatsappSdk::Resource::InteractiveFooter.new(text: "I am the footer!")
interactive_action = WhatsappSdk::Resource::InteractiveAction.new(type: "list_message")
interactive_action.button = "I am the button CTA"
interactive_section_1 = WhatsappSdk::Resource::InteractiveActionSection.new(title: "I am the section 1")
interactive_section_1_row_1 = WhatsappSdk::Resource::InteractiveActionSectionRow.new(
title: "I am the row 1 title",
id: "section_1_row_1",
description: "I am the optional section 1 row 1 description"
)
interactive_section_1.add_row(interactive_section_1_row_1)
interactive_action.add_section(interactive_section_1)
interactive_list_messages = WhatsappSdk::Resource::Interactive.new(
type: "list",
header: interactive_header,
body: interactive_body,
footer: interactive_footer,
action: interactive_action
)
client.messages.send_interactive_list_messages(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER,
interactive: interactive_list_messages
)
```
Alternatively, you could pass a plain json like this:
```ruby
client.messages.send_interactive_list_messages(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER
interactive_json: {...}
)
```
Reply Button's example
```ruby
interactive_header = WhatsappSdk::Resource::InteractiveHeader.new(type: "text", text: "I am the header!")
interactive_body = WhatsappSdk::Resource::InteractiveBody.new(text: "I am the body!")
interactive_footer = WhatsappSdk::Resource::InteractiveFooter.new(text: "I am the footer!")
interactive_action = WhatsappSdk::Resource::InteractiveAction.new(type: "reply_button")
interactive_reply_button_1 = WhatsappSdk::Resource::InteractiveActionReplyButton.new(
title: "I am the reply button 1",
id: "button_1"
)
interactive_action.add_reply_button(interactive_reply_button_1)
interactive_reply_button_2 = WhatsappSdk::Resource::InteractiveActionReplyButton.new(
title: "I am the reply button 2",
id: "button_2"
)
interactive_action.add_reply_button(interactive_reply_button_2)
interactive_reply_buttons = WhatsappSdk::Resource::Interactive.new(
type: "reply_button",
header: interactive_header,
body: interactive_body,
footer: interactive_footer,
action: interactive_action
)
client.messages.send_interactive_reply_buttons(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER,
interactive: interactive_reply_buttons
)
```
Alternatively, you could pass a plain json like this:
```ruby
client.messages.send_interactive_reply_buttons(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER
interactive_json: {...}
)
```
Errors
If the API returns an error then an exception WhatsappSdk::Api::Responses::HttpResponseError is raised. The object contains information returned by the Cloud API. For more information about the potential error check the official documentation.
Examples
Visit the example file with examples to call the API in a single file.
Whatsapp Cloud API
Troubleshooting
If you try to send a text message directly without a message template created and approved in your Meta control panel, you can’t start a chat with other people. But if you receive a message before, it’s possible to send a message.
If the API response is still success, but the message is not delivered:
- Ensure the device you’re sending the message to is using a supported Whatsapp version. Check documentation. Try also replying a message to the number you are registered on your Whatsapp.
- Ensure your Meta App uses an API version greater than or equal to
v.14.
- Ensure that the Panel in the Facebook dashboard doesn’t display any errors.
Note: Sometimes the messages are delayed; see Meta documentation.
Voice messages
Pass voice: true to send_audio with an uploaded audio_id or hosted link.
Voice messages require an Ogg file encoded with Opus; the SDK does not transcode media.
Omitting voice (or passing false) sends basic audio.
client.messages.send_audio(sender_id: sender_id, recipient_number: recipient_number,
audio_id: media_id, voice: true)
See Meta’s audio message documentation.
Upload handles for templates and profile photos
Use Graph upload sessions when an endpoint requires a media handle instead of a WhatsApp media ID:
session = client.media.create_upload_session(app_id: app_id, file_path: "header.png", type: "image/png")
upload = client.media.upload_file_to_session(session_id: session.fetch("id"), file_path: "header.png")
handle = upload.fetch("h")
Both methods use the client’s configured API version and token. An optional access_token:
overrides the token for that request only. They return the Graph response hashes and raise
WhatsappSdk::Api::Responses::HttpResponseError on Graph errors, like the other APIs.
Missing files or directories raise WhatsappSdk::Api::Medias::FileNotFoundError.
The upload reads the entire file into memory and starts at offset zero; automatic retries and
resuming interrupted uploads are not included.
See Meta’s upload guide.
Business-scoped user IDs (BSUIDs)
All message sends accept recipient: as an alternative to recipient_number:.
Use the complete BSUID (or parent BSUID), including its country prefix:
response = client.messages.send_text(sender_id: sender_id, recipient: "US.123abc", message: "Hello")
response.contacts.first.user_id # => "US.123abc"
response.contacts.first.wa_id # => nil when Meta omits the phone number
Phone numbers take precedence when both destinations are supplied. Missing or blank
selected destinations raise WhatsappSdk::Resource::Errors::MissingArgumentError before sending.
Authentication templates require a phone number; the SDK does not infer template category from its name or buttons.
See Meta’s BSUID documentation.
Send marketing templates
response = client.messages.send_marketing_template(
sender_id: SENDER_ID, recipient_number: RECIPIENT_NUMBER,
name: "special_offer", language: "en_US", components_json: [],
product_policy: "STRICT"
)
response.messages.first.id
response.messages.first.message_status # Present only when returned by Meta.
This explicitly calls /marketing_messages; ordinary send_template continues to
call /messages. Use recipient: for a BSUID when no phone number is available;
a supplied phone number takes precedence. Meta disables delivery optimization for
BSUID sends and rejects templates using bid_spec for those recipients.
product_policy: "CLOUD_API_FALLBACK" asks Meta to handle fallback; "STRICT" forbids
it; symbols and lowercase are accepted. Pass message_activity_sharing: true or false
to set Meta’s activity sharing; omitting either option leaves Meta’s default unchanged. The SDK never retries a
failed send or sends a second request to /messages after a timeout. Template
category and account eligibility are enforced by Meta, not inferred from names.
See Marketing Messages.
Inspect the complete HTTP response
response = client.send_request(endpoint: "#{SENDER_ID}/messages", params: message_payload,
headers: { "Content-Type" => "application/json" }, raw_response: true)
response.status
response.headers
response.body
raw_response: true returns the Faraday response unchanged, including empty or non-JSON
error bodies. The caller must check the HTTP status and parse the body. The default mode
returns parsed JSON and raises HttpResponseError for Graph API errors and for any HTTP status of 400 or above. Transport exceptions still propagate.
Manage WhatsApp Flows
page = client.flows.list(business_id: WABA_ID, limit: 20, fields: %w[id name status])
page.records # Raw Flow hashes; use page.after as the after: cursor for the next page.
flow = client.flows.create(business_id: WABA_ID, name: "Customer survey", categories: ["SURVEY"])
validation = client.flows.upload_json(flow_id: flow["id"], file_path: "flow.json")
details = client.flows.get(flow_id: flow["id"], fields: %w[id status validation_errors])
# Inspect validation errors and review the draft before publishing explicitly.
client.flows.publish(flow_id: flow["id"])
Creation leaves the Flow in draft status. Upload returns Graph’s raw response, including
validation_errors even when success is true. Individual calls do not retry or publish
implicitly. Business templates, draft recovery, and data exchange endpoint encryption
remain in the application. See the Flows API reference.
List the assets uploaded to a Flow:
assets = client.flows.assets(flow_id: flow["id"], limit: 20)
assets.records # Raw asset hashes with name, asset_type, and download_url; no automatic download.
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests.
Run bundle exec rake install to install this gem onto your local machine. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Run all the tests
- Unit tests: Run
rake test
- Linters:
bundle exec rubocop
To update the Cloud API version update the version in lib/whatsapp_sdk/api/api_configuration.rb. Check the Cloud API changelog for API updates.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/ignacio-chiazzo/ruby_whatsapp_sdk This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
If you want a feature to be implemented in the gem, please, open an issue and we will take a look as soon as we can.
Do you want to contribute and are unsure where to start? Ping me on Twitter, and I will help you!
Check Contributing file.
Changelog
See CHANGELOG.md for a list of changes.
License
The gem is available as open source under the terms of the MIT License.