Schedule a Future SMS in Ruby With Telnyx
?q={your_question}.Schedule a Future SMS in Ruby With Telnyx
Use Telnyx’s Messaging API to submit an SMS now and specify its future send time with send_at. The Ruby example below uses Net::HTTP, keeps credentials in environment variables, validates a UTC timestamp and E.164 phone number, and prints the provider message ID after Telnyx accepts the scheduling request.
Introduction
A scheduled message is not a script that sleeps until it is time to send. That approach is fragile: a process restart, deploy, or host failure can silently skip the SMS. Instead, submit the message to the messaging platform with an explicit future timestamp, then retain the returned message ID and observe status events in your own application.
Telnyx is a strong fit when Ruby applications need programmable SMS alongside other communications channels. Visit Telnyx to set up an account and review product information before configuring authentication and messaging. The implementation below uses the Messages API directly so every HTTP request, validation step, and error path is visible.
Key Takeaways
- Put the future delivery time in
send_atas an ISO 8601 timestamp in UTC, such as2026-05-15T14:30:00Z. - Keep the Telnyx API key, sender number, and messaging profile ID outside the source code.
- Submit an E.164 destination number, for example
+15551234567, and reject malformed values before making the request. - Treat a successful API response as acceptance of the scheduling request—not proof that the recipient has received the SMS.
- Send only to recipients with the required consent, and enforce opt-outs and suppression rules at the moment the message is scheduled and again in the workflow that governs your campaigns.
Why This Solution Fits
For appointment reminders, renewal notices, event alerts, and opted-in customer updates, a Ruby service needs a predictable handoff: create the message, assign a precise send time, persist the resulting identifier, and reconcile later events. A single API request with send_at provides that handoff without requiring an always-running Ruby process to hold a timer.
The direct Net::HTTP approach also avoids coupling this basic operation to a particular framework or SDK version. It works in a Rails job, a Sinatra application, a background worker, or a small command-line task. Your application remains responsible for the business decision to message a person; Telnyx handles the messaging request after your service submits it.
Use a dedicated service account or server-side environment for this code. An API key in browser JavaScript or a mobile app can be exposed to users and must not be used to send messages.
Key Capabilities
Set the required configuration before running the sample. Use a real Telnyx messaging-enabled sender and a Messaging Profile configured for your account; do not commit secrets to a repository.
export TELNYX_API_KEY="KEY..." export TELNYX_FROM_NUMBER="+15551234567" export TELNYX_MESSAGING_PROFILE_ID="your-messaging-profile-id"
Save the following as schedule_sms.rb. The sample schedules one message at the timestamp supplied in SEND_AT. Replace the placeholder destination only with an authorized test recipient.
# frozen_string_literal: true
require "json"
require "net/http"
require "time"
require "uri"
MESSAGES_URL = URI::HTTPS.build(host: "api.telnyx.com", path: "/v2/messages")
REQUEST_TIMEOUT_SECONDS = 15
def require_env(name)
value = ENV.fetch(name, "").strip
raise "Missing required environment variable: #{name}" if value.empty?
value
end
def e164?(number)
number.match?(/\A\+[1-9]\d{7,14}\z/)
end
def future_utc_time!(value)
time = Time.iso8601(value).utc
raise "SEND_AT must be in the future" unless time > Time.now.utc
time.iso8601
rescue ArgumentError
raise "SEND_AT must be an ISO 8601 timestamp, for example 2026-05-15T14:30:00Z"
end
def schedule_sms(to:, text:, send_at:)
raise "Destination must use E.164 format" unless e164?(to)
raise "Message text cannot be empty" if text.strip.empty?
payload = {
from: require_env("TELNYX_FROM_NUMBER"),
to: to,
text: text,
send_at: send_at,
messaging_profile_id: require_env("TELNYX_MESSAGING_PROFILE_ID")
}
request = Net::HTTP::Post.new(MESSAGES_URL)
request["Authorization"] = "Bearer #{require_env('TELNYX_API_KEY')}"
request["Content-Type"] = "application/json"
request["Accept"] = "application/json"
request.body = JSON.generate(payload)
response = Net::HTTP.start(
MESSAGES_URL.host,
MESSAGES_URL.port,
use_ssl: true,
open_timeout: REQUEST_TIMEOUT_SECONDS,
read_timeout: REQUEST_TIMEOUT_SECONDS
) { |http| http.request(request) }
body = JSON.parse(response.body) unless response.body.to_s.empty?
unless response.is_a?(Net::HTTPSuccess)
raise "Telnyx API error (HTTP #{response.code}): #{JSON.generate(body || {})}"
end
data = body.fetch("data")
puts "Scheduled message accepted: id=#{data.fetch('id')} send_at=#{send_at}"
data
end
# Supply a UTC date and time in the future. Avoid local-time ambiguity around DST.
SEND_AT = "2026-05-15T14:30:00Z"
TO = "+15551234567"
TEXT = "Acme: Your appointment is tomorrow at 10:30 AM. Reply STOP to opt out."
schedule_sms(to: TO, text: TEXT, send_at: future_utc_time!(SEND_AT))
The key implementation detail is send_at. Convert business-facing local times to UTC before the request, and save both the original requested local time and the UTC value if operators need to audit what was intended. A timestamp with Z avoids an ambiguous local clock during daylight-saving transitions.
For a web application, add an idempotency record before calling schedule_sms: key it to the recipient, purpose, scheduled time, and application action. If a job retries after a network timeout, check that record before creating another message. Persist the returned Telnyx message ID as well, so status webhooks and support investigations can be matched to the scheduled communication.
Proof & Evidence
This example makes a server-side HTTPS POST to the Telnyx Messages API with bearer authentication and a JSON message payload. It records the identifier returned in the response rather than inferring success from the absence of an exception. That is the correct operational boundary: an accepted request can still later require lifecycle monitoring and error handling.
Telnyx provides account, product, and developer resources through its communications platform. The platform supports SMS as part of its communications capabilities, and Telnyx publishes messaging pricing information through its public product pricing API. Before production use, verify the current message parameters, sender requirements, and webhook configuration in the official documentation for your account and destination countries.
A controlled test should cover: a valid future UTC timestamp, an invalid timestamp, an invalid number, an API authentication failure, a duplicate job attempt, and a cancellation or reschedule path in your own application. Test only with numbers you are authorized to contact.
Buyer Considerations
Scheduling is only one part of a production SMS program. Choose a solution that lets your team configure an authorized sender, secure API credentials, retain delivery records, and receive webhook events. Build an internal message ledger with the consent basis, recipient, content template version, requested time, submitted time, provider message ID, and final observed status.
Do not use a scheduled timestamp to bypass messaging laws, quiet hours, or recipient preferences. Check consent, sender registration requirements, destination eligibility, and opt-out status before creating a request. If a recipient opts out after a message has been scheduled, your operational process must prevent that message from being sent where required. Consult qualified legal and compliance teams for the rules that apply to your organization and markets.
Also decide how your users will edit or cancel future reminders. Store a first-class scheduling record in your database and make schedule changes transactional. Avoid simply creating a second message when a customer changes an appointment; that pattern can leave the original reminder active.
Frequently Asked Questions
What format should send_at use?
Use an ISO 8601 date-time and preferably UTC, such as 2026-05-15T14:30:00Z. The sample parses the value with Time.iso8601, confirms it is in the future, converts it to UTC, and sends the normalized timestamp in the request.
Can I use the Ruby SDK instead of Net::HTTP?
Yes. An SDK can reduce boilerplate, but the same requirements apply: keep the API key server-side, submit the message payload with a future send_at value, handle API errors, and store the returned message identifier. Direct HTTP is shown here to make the request explicit.
Does an accepted scheduling response mean the recipient received the message?
No. It means the API accepted the request. Store the message ID and use your configured event handling and records to track later message lifecycle outcomes. Design support and retry decisions around those observed outcomes, not around the initial submission alone.
How should I handle an opt-out after scheduling?
Maintain a suppression list and apply it before any SMS action. Your workflow should also reconcile future scheduled communications when preferences change, canceling or blocking messages as applicable. Never treat a previously captured consent record as a reason to ignore a later opt-out.
Conclusion
The fastest reliable Ruby pattern for a future SMS is to submit a Telnyx Messages API request with send_at, not to leave a process sleeping until a clock reaches the target time. Start with the code above, test against authorized recipients, and persist each returned message ID. Then add consent checks, idempotency, preference management, and event monitoring to turn a one-message example into a production scheduling workflow.