Ruby SDK
The official Ruby SDK for Sent provides an elegant, Ruby-idiomatic interface for sending messages. Designed for Rails applications, with Yard docstrings and RBS and RBI type signatures.
Requirements
Ruby 3.2.0 or later.
Installation
To use this gem, install via Bundler by adding the following to your app's Gemfile:
gem "sentdm"Then run:
bundle installOr install directly:
gem install sentdmQuick Start
Initialize the client
require "sentdm"
sent_dm = Sentdm::Client.new(
api_key: ENV["SENT_DM_API_KEY"] # This is the default and can be omitted
)Send your first message
require "sentdm"
sent_dm = Sentdm::Client.new
result = sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome",
parameters: {
name: "John Doe",
order_id: "12345"
}
},
channel: ["sms", "whatsapp", "rcs"]
)
puts(result.data.recipients[0].message_id)
puts(result.data.status)Authentication
The client reads SENT_DM_API_KEY from the environment by default, or you can pass it explicitly:
require "sentdm"
# Using environment variables
sent_dm = Sentdm::Client.new
# Or explicit configuration
sent_dm = Sentdm::Client.new(
api_key: "your_api_key"
)Send Messages
The Ruby SDK uses send_ (with trailing underscore) instead of send because send is a reserved method in Ruby's Object class.
Send a message
result = sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome",
parameters: {
name: "John Doe",
order_id: "12345"
}
},
channel: ["sms", "whatsapp", "rcs"]
)
puts(result.data.recipients[0].message_id)
puts(result.data.status)Sandbox mode
Use sandbox: true to validate requests without sending real messages:
result = sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome"
},
sandbox: true # Validates but doesn't send
)
# Response will have test data
puts(result.data.recipients[0].message_id)
puts(result.data.status)Check message status
Retrieve the current status of a sent message. The direction field indicates whether the message is "OUTBOUND" (sent by you) or "INBOUND" (a reply or opt-out keyword received from an end user):
status = sent_dm.messages.retrieve_status("msg-uuid")
puts status.data.status # e.g. "DELIVERED"
puts status.data.channel # e.g. "sms"
puts status.data.direction # "OUTBOUND" | "INBOUND"Message activities
Retrieve the full activity log for a message, useful for auditing delivery attempts across carriers:
activities = sent_dm.messages.retrieve_activities("msg-uuid")
activities.data.activities.each do |activity|
puts "#{activity.timestamp}: #{activity.status} via #{activity.from}"
puts " Price: #{activity.price}"
puts " Active contact price: #{activity.active_contact_price}"
endNumbers
Look up carrier and line-type information for any phone number before sending:
result = sent_dm.numbers.lookup("+12025551234")
puts result.data.is_valid # true/false
puts result.data.carrier_name # e.g. "T-Mobile"
puts result.data.line_type # "mobile", "landline", "voip"
puts result.data.is_voip # true/falseHandling errors
When the library is unable to connect to the API, or if the API returns a non-success status code (that is, 4xx or 5xx response), a subclass of Sentdm::Errors::APIError will be thrown:
begin
result = sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome",
parameters: {name: "John Doe", order_id: "12345"}
}
)
rescue Sentdm::Errors::APIConnectionError => e
puts("The server could not be reached")
puts(e.cause) # an underlying Exception, likely raised within `net/http`
rescue Sentdm::Errors::RateLimitError => e
puts("A 429 status code was received; we should back off a bit.")
rescue Sentdm::Errors::APIStatusError => e
puts("Another non-200-range status code was received")
puts(e.status)
endError codes are as follows:
| Cause | Error Type |
|---|---|
| HTTP 400 | BadRequestError |
| HTTP 401 | AuthenticationError |
| HTTP 403 | PermissionDeniedError |
| HTTP 404 | NotFoundError |
| HTTP 409 | ConflictError |
| HTTP 422 | UnprocessableEntityError |
| HTTP 429 | RateLimitError |
| HTTP >= 500 | InternalServerError |
| Other HTTP error | APIStatusError |
| Timeout | APITimeoutError |
| Network error | APIConnectionError |
Retries
Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
# Configure the default for all requests:
sent_dm = Sentdm::Client.new(
max_retries: 0 # default is 2
)
# Or, configure per-request:
sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome"
},
request_options: {max_retries: 5}
)Timeouts
Requests time out after 60 seconds by default. Set timeout (in seconds) on the client or per request; timeout: nil disables the timeout entirely.
# Configure the default for all requests:
sent_dm = Sentdm::Client.new(
timeout: 20 # seconds (default is 60; nil disables the timeout)
)
# Or, configure per-request:
sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome"
},
request_options: {timeout: 5}
)BaseModel
All parameter and response objects inherit from Sentdm::Internal::Type::BaseModel, which provides several conveniences:
- All fields, including unknown ones, are accessible with
obj[:prop]syntax - Structural equivalence for equality
- Both instances and classes can be pretty-printed
- Helpers such as
#to_h,#deep_to_h,#to_json, and#to_yaml
result = sent_dm.templates.list(page: 1, page_size: 100)
# Access fields
template = result.data.templates.first
template.name
template[:name] # Same thing
# Convert to hash
template.to_h
# Serialize to JSON
template.to_json
# Pretty print
puts template.inspectContacts
Create and manage contacts:
# Create a contact
result = sent_dm.contacts.create(
phone_number: "+1234567890"
)
puts "Contact ID: #{result.data.id}"
puts "Channels: #{result.data.available_channels}"
# List contacts
result = sent_dm.contacts.list(page: 1, page_size: 100)
result.data.contacts.each do |contact|
puts "#{contact.phone_number} - #{contact.available_channels}"
end
# Get a contact
result = sent_dm.contacts.retrieve("contact-uuid")
# Update a contact
result = sent_dm.contacts.update(
"contact-uuid",
default_channel: "whatsapp"
)
# Delete a contact
sent_dm.contacts.delete("contact-uuid")Templates
List and retrieve templates:
# List templates
result = sent_dm.templates.list(page: 1, page_size: 100)
result.data.templates.each do |template|
puts "#{template.name} (#{template.status}): #{template.id}"
puts " Category: #{template.category}"
puts " Channels: #{template.channels.join(', ')}"
end
# Get a specific template
result = sent_dm.templates.retrieve("template-uuid")
puts "Name: #{result.data.name}"
puts "Status: #{result.data.status}"Webhooks
Recommended pattern: Webhooks are the primary way to track message delivery, so don't poll the API. Save the message ID when you send, then update your database as webhook events arrive.
Sent delivers signed POST requests to your endpoint for every status change. Two event types exist:
message: Message status changes (QUEUED,ROUTED,SCHEDULED,SENT,DELIVERED,READ,FAILED,FILTERED,BLOCKED,RECEIVED); each fires as a sub-type (for example,message.delivered,message.filtered). Usemessage.receivedto receive inbound messages from contacts.templates: WhatsApp template approval/rejection
The signing secret (from the Sent Dashboard) has a whsec_ prefix. Strip it and base64-decode the remainder to obtain the raw HMAC key. The signed content is {X-Webhook-ID}.{X-Webhook-Timestamp}.{rawBody} and the signature format is v1,{base64(hmac)}. Complete verified webhook handlers are available in the Rails integration guide and the Sinatra integration guide; the Webhooks reference documents the full payload schema and all status values.
Making custom or undocumented requests
Undocumented properties
You can send undocumented parameters to any endpoint using extra_* options:
result = sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome"
},
request_options: {
extra_query: {my_query_parameter: value},
extra_body: {my_body_parameter: value},
extra_headers: {"my-header" => value}
}
)
puts(result[:my_undocumented_property])Undocumented endpoints
To make requests to undocumented endpoints:
response = sent_dm.request(
method: :post,
path: '/undocumented/endpoint',
query: {"dog" => "woof"},
headers: {"useful-header" => "interesting-value"},
body: {"hello" => "world"}
)Concurrency & connection pooling
The Sentdm::Client instances are thread-safe, but are only fork-safe when there are no in-flight HTTP requests.
Each instance of Sentdm::Client has its own HTTP connection pool with a default size of 99 (or the number of CPU cores, if greater). As such, Sent recommends instantiating the client once per app in most settings.
When all available connections from the pool are checked out, requests wait for a new connection to become available, with queue time counting towards the request timeout.
Framework Integration
Dedicated guides cover client setup, message sending, verified webhook handling, and testing for each framework:
Ruby on Rails
Configure the client, send template messages from a controller, verify webhooks, and test with sandbox mode.
Sinatra
Send template messages, verify signed webhooks, validate payloads, and test with RSpec in a modular Sinatra app.
Sorbet
This library provides RBI definitions and has no dependency on sorbet-runtime.
You can provide typesafe request parameters:
sent_dm.messages.send_(
to: ["+1234567890"],
template: {
id: "7ba7b820-9dad-11d1-80b4-00c04fd430c8",
name: "welcome",
parameters: {name: "John Doe"}
}
)Source & Issues
- Releases: GitHub Releases
- GitHub:
sentdm/sent-dm-ruby - RubyGems:
sentdm - RubyDoc:
gemdocs.org/gems/sentdm - Issues: Report a bug
Getting Help
- Documentation: API Reference
- Troubleshooting: Common Issues
- Support: email support@sent.dm with your request ID
Sending messages from Symfony with the Sent PHP SDK
Wire the Sent PHP SDK into a Symfony app: install, register the client service, send messages from a controller, verify webhooks, and test with sandbox mode.
Sending messages from Rails with the Sent Ruby SDK
Wire the Sent Ruby SDK into a Rails app: install, configure the client, send a template message from a controller, verify webhooks, and test with sandbox mode.