본문 바로가기
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-rails

The 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. friendtalk still works and is still the only path for free-form FT/FI/FW to 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

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.

이 패키지로 할 수 있는 것

관련 패키지

Focus on building. Leave messaging to Sendgo.Back to top ↑