Documentation menu
Ruby / SDK REFERENCE
Ruby on Rails SDK Guide
The official Rails integration for sending Kakao Alimtalk, Brand Message and SMS — a Railtie-configured client with ENV fallback.
이 문서의 목차
- 패키지
- sendgo-rails
- 언어
- Ruby
- 레지스트리
- RubyGems
bundle add sendgo-railsThe official Rails integration for sending Kakao Alimtalk, Brand Message and SMS
sendgo-rails wraps the sendgo core gem with a Railtie: it adds a config.sendgo namespace, falls back to environment variables, and exposes a memoised client as Sendgo::Rails.client.
Install
# Gemfile
gem "sendgo-rails"
bundle install
bin/rails g sendgo:install
The generator writes config/initializers/sendgo.rb. The core sendgo gem comes along as a dependency.
Requires Ruby 3.1+ and Rails 6.1 or newer (railties >= 6.1).
Configure
# config/initializers/sendgo.rb
Rails.application.config.sendgo.tap do |config|
config.access_key = ENV["SENDGO_ACCESS_KEY"]
config.secret_key = ENV["SENDGO_SECRET_KEY"]
config.kakao_sender_key = ENV["SENDGO_KAKAO_SENDER_KEY"]
config.sms_sender_key = ENV["SENDGO_SMS_SENDER_KEY"]
config.api_version = ENV.fetch("SENDGO_API_VERSION", "v2")
config.url = ENV.fetch("SENDGO_URL", "https://sendgo.io")
end
Every setting resolves in two steps: config.sendgo.<key> first, then the matching environment variable. Leaving a value nil in the initializer is therefore the same as not setting it — the ENV value wins. That means you can drop the initializer entirely and configure the gem with environment variables alone.
Use Rails credentials instead of ENV if you prefer:
config.access_key = Rails.application.credentials.dig(:sendgo, :access_key)
Note that api_version defaults to v2 in the Rails integration (the bare Ruby core defaults to v1).
Quick start
Sendgo::Rails.client.alimtalk.send(
template_code: "ORDER_CONFIRM_001",
contacts: [{ contact: "01012345678", var1: "ORD-001" }]
)
Sendgo::Rails.client is memoised and lazy — nothing is built until the first call, so requiring the gem does not read your configuration at boot. Every channel from the core gem is available on it:
| Method | Channel |
|---|---|
client.alimtalk |
Kakao Alimtalk |
client.friendtalk |
Kakao Friendtalk |
client.brand_message |
Kakao Brand Message (v2 only) |
client.sms |
SMS / LMS / MMS |
Because the client is memoised, changing config.sendgo at runtime has no effect until you call Sendgo::Rails.reset!.
Wrap it in your own class
Calling Sendgo::Rails.client from controllers scatters template codes through the app. A thin object keeps them in one place and gives you something to stub in tests:
# app/services/order_notifier.rb
class OrderNotifier
def initialize(client: Sendgo::Rails.client)
@client = client
end
def confirmed(order)
@client.alimtalk.send(
template_code: "ORDER_CONFIRM_001",
contacts: [{ contact: order.phone, var1: order.number }]
)
end
end
Brand Message
Friendtalk was discontinued on 2025-12-31. Since 2026-01-01 Kakao delivers Friendtalk requests as Brand Message (free-form) automatically.
friendtalkstill works and is still the only path for free-formFT/FI/FWto individual recipients — use Brand Message for template-based rich types (FL/FC/FM/FP/FA), non-friend targeting (N/I) and broadcasts (F). Brand Message is the successor channel to Friendtalk: it reaches recipients who are not channel friends (targeting: "N") and can broadcast to every consenting channel friend (targeting: "F"). Message types map one-to-one with Friendtalk (FT→BT,FI→BI,FW→BW,FL→BL,FC→BC,FM→BM,FP→BP,FA→BA) — pass the Friendtalk code and the server converts it.
v2 only, which is the Rails integration's default.
client = Sendgo::Rails.client
# Single send — targeting M/N/I requires `contacts`
client.brand_message.send(
targeting: "M",
message_type: "FL",
friend_template_uuid: "9cd5460b-6458-4edc-9b11-c26d3013c340",
contacts: [{ contact: "01012345678", var1: "29,000 KRW" }]
)
# Broadcast — every consenting channel friend, no recipient list
result = client.brand_message.broadcast(
message_type: "FW",
friend_template_uuid: "9cd5460b-6458-4edc-9b11-c26d3013c340"
)
# A broadcast is asynchronous upstream, so poll for progress
client.brand_message.campaign(result.dig("data", "campaignId"))
client.brand_message.campaigns(from: "2026-08-01", count: 10)
friend_template_uuid is a required keyword argument on send; message_type defaults to "FT" and targeting to "M". broadcast is send with targeting forced to "F", so it accepts the same arguments.
Sending from a model callback
Send after the transaction commits — a callback that fires inside the transaction still delivers the message if a later validation rolls the record back:
class Order < ApplicationRecord
after_commit :notify_customer, on: :create
private
def notify_customer
OrderConfirmJob.perform_later(id)
end
end
ActiveJob
Sending is a network call, so keep it out of the request cycle:
# app/jobs/order_confirm_job.rb
class OrderConfirmJob < ApplicationJob
queue_as :notifications
# 5xx is transient; a 4xx will not succeed on retry.
# `:polynomially_longer` needs Rails 7.1+; on 6.1–7.0 use `:exponentially_longer`.
retry_on Sendgo::SendgoError, wait: :polynomially_longer, attempts: 3 do |job, error|
raise error if error.status_code >= 500
Rails.logger.error("Sendgo #{error.status_code} [#{error.error_code}]: #{error.message}")
end
def perform(order_id)
order = Order.find(order_id)
Sendgo::Rails.client.alimtalk.send(
template_code: "ORDER_CONFIRM_001",
contacts: [{ contact: order.phone, var1: order.number }]
)
end
end
retry_on with a block runs the block after the attempts are exhausted. If you want to stop immediately on a 4xx, rescue inside perform and call discard_on instead:
discard_on Sendgo::SendgoError do |_job, error|
Rails.logger.error("Dropped: Sendgo #{error.error_code}")
end
Rake task
# lib/tasks/promo.rake
namespace :promo do
desc "Broadcast the current promotion to every consenting channel friend"
task broadcast: :environment do
result = Sendgo::Rails.client.brand_message.broadcast(
message_type: "FW",
friend_template_uuid: "9cd5460b-6458-4edc-9b11-c26d3013c340"
)
puts "Accepted: #{result.dig('data', 'campaignId')}"
end
end
Testing
reset! drops the memoised client, which is what makes a stubbed client stick:
# spec/support/sendgo.rb
RSpec.configure do |config|
config.before do
Sendgo::Rails.reset!
allow(Sendgo::Rails).to receive(:client).and_return(fake_sendgo)
end
end
def fake_sendgo
@fake_sendgo ||= instance_double(
Sendgo::Client,
alimtalk: instance_double(Sendgo::AlimtalkService, send: { "message" => "Success" }),
brand_message: instance_double(Sendgo::BrandMessageService, send: { "message" => "Success" })
)
end
it "sends an alimtalk when an order is created" do
expect(fake_sendgo.alimtalk).to receive(:send).with(
hash_including(template_code: "ORDER_CONFIRM_001")
)
Order.create!(phone: "01012345678", number: "ORD-001")
end
Without reset!, a client memoised by an earlier example leaks into the next one.
Error handling
begin
Sendgo::Rails.client.alimtalk.send(
template_code: "ORDER_CONFIRM_001",
contacts: [{ contact: "01012345678" }]
)
rescue Sendgo::SendgoError => e
case e.error_code
when "INVALID_ACCESS_KEY", "INVALID_SECRET_KEY"
Rails.logger.error("Check the Sendgo API keys")
when "PAYMENT_REQUIRED"
Rails.logger.error("Out of Sendgo credit")
when "IP_NOT_ALLOWED"
Rails.logger.error("IP is not allow-listed")
else
raise if e.status_code >= 500 # transient — let the job retry
Rails.logger.error("Sendgo #{e.status_code}: #{e.message}")
end
end
Sendgo::SendgoError exposes status_code, error_code, endpoint, api_version and response_body. Branch on error_code, not on the message text — messages can change, codes are the contract.
TOKEN_EXPIRED and TOKEN_MISMATCH are handled inside the core gem: the token is reissued and the request retried once.
Configuration reference
config.sendgo key |
Environment variable | Required | Default | Description |
|---|---|---|---|---|
access_key |
SENDGO_ACCESS_KEY |
required | — | Sendgo access key |
secret_key |
SENDGO_SECRET_KEY |
required | — | Sendgo secret key |
kakao_sender_key |
SENDGO_KAKAO_SENDER_KEY |
optional | nil |
Kakao sender profile key |
sms_sender_key |
SENDGO_SMS_SENDER_KEY |
optional | nil |
SMS caller ID key |
api_version |
SENDGO_API_VERSION |
optional | "v2" |
API version (v1 | v2) |
url |
SENDGO_URL |
optional | "https://sendgo.io" |
API base URL |
Short URL
Short URLs shrink the links in your message body and count whether they were actually clicked. SMS is billed by byte, so a shorter link leaves more room for copy.
v2 only.
Shortening the same target URL again returns the existing link. Pass forceNew
to mint a new code when you want per-campaign reaction figures kept separate.
deactivate does not delete the link — it only stops the redirect. Use it when a link
in an already-sent message has to be killed; the accumulated stats stay, and visitors
to a stopped link get 410 Gone.
created = Sendgo::Rails.client.short_url.create(
target_url: promotion_url(@promotion),
title: @promotion.name
)
code = created.dig("data", "code")
stats = Sendgo::Rails.client.short_url.stats(code)
stats returns a daily series (daily) plus breakdowns by device (byDevice), referrer (byReferer) and country (byCountry). The daily series is read from a pre-aggregated table, so response time stays flat no matter how many clicks accumulate.
Package information
- Package:
sendgo-rails(RubyGems) - Repository: send-go/rails
- Registry: https://rubygems.org/gems/sendgo-rails
- License: MIT
Getting your API keys
Sign in to Sendgo and open API/SDK → API integration to issue an access key and secret key.
Register a Kakao sender profile under Kakao channel to get your kakao_sender_key, and a caller ID under Sender numbers for SMS.
이 패키지로 할 수 있는 것
Send a Kakao Alimtalk — PHP, Node.js, Python, Java, Go examples →
Send Kakao Alimtalk with an approved template code. Runnable code for Laravel, Node.js, Python, PHP, Java, Go, Ruby and .NET, plus every required field and why sends fail.
Send SMS, LMS and MMS in South Korea — code examples →
Send Korean SMS (90 bytes), LMS (long text) and MMS (with images) through Sendgo. Type selection, byte counting, verification-code patterns and advertising rules.