Send your first Kakao Alimtalk in 5 minutes
From issuing an access key to sending a Kakao Alimtalk, with working code in Node.js, Python, PHP, Laravel, Java and Go.
POST /api/v2/notices/sendGetting the first Kakao Alimtalk out takes five steps. Only the last one is code — the other four are account setup you do once.
In a hurry? If steps 1–3 are already done, jump to step 5.
Step 1 — Issue your API keys
Sign in to the Sendgo console and create an app under Integration → Apps. You get an accessKey and a secretKey.
# .env
SENDGO_ACCESS_KEY=your_access_key
SENDGO_SECRET_KEY=your_secret_key
SENDGO_KAKAO_SENDER_KEY=your_kakao_sender_key
SENDGO_SMS_SENDER_KEY=your_sms_sender_key
SENDGO_API_VERSION=v2
These two values carry the full sending permission for the account. Keep them in the environment, never in the repository. If you have a fixed server IP, add an IP allowlist to the app — a leaked key is then useless from anywhere else.
Step 2 — Register a sending number and Kakao channel
In South Korea you may only send from a pre-registered number (전기통신사업법). This is the step that delays integrations, because it needs document review and teams usually start it after the code is finished.
- SMS sending number — submit proof that the number belongs to you. Approval takes business days.
- Kakao sender profile — connect a KakaoTalk channel to Sendgo and you get a
kakaoSenderKey. The channel must already be converted to a business channel in Kakao Business.
Step 3 — Register and approve a template
An Alimtalk body is a template approved in advance. At send time you only fill in the blanks.
[#{var2}] Your order is confirmed.
Order number: #{var1}
Total: #{var3}
Register that, get a template code such as ORDER_CONFIRM_001, and you are ready.
Promotional wording is not approved as an Alimtalk template — informational only. For marketing, use Brand Message or an advertising SMS.
Step 4 — Install the SDK
Framework packages pull their core in, so do not install both.
composer require sendgo/laravel # Laravel
composer require sendgo/php # plain PHP
npm install @sendgo/node # Node.js / TypeScript
pip install sendgo-python # Python
go get github.com/send-go/go # Go
gem install sendgo # Ruby
dotnet add package Sendgo.SDK # .NET
Full list and selection guide: Choosing an SDK.
Step 5 — Send
Create the client once and reuse it. Put the sender keys on the client so you do not repeat them per call.
Node.js / TypeScript
import Sendgo from '@sendgo/node';
const sendgo = new Sendgo({
accessKey: process.env.SENDGO_ACCESS_KEY!,
secretKey: process.env.SENDGO_SECRET_KEY!,
kakaoSenderKey: process.env.SENDGO_KAKAO_SENDER_KEY,
smsSenderKey: process.env.SENDGO_SMS_SENDER_KEY,
apiVersion: 'v2',
});
await sendgo.alimtalk.send({
templateCode: 'ORDER_CONFIRM_001',
contacts: [
{ contact: '01012345678', name: 'Hong Gildong', var1: 'ORD-001', var3: '29,000원' },
],
});
@sendgo/node is a default export. import { Sendgo } from '@sendgo/node' does not work.
Python
import os
from sendgo import Sendgo
client = Sendgo(
access_key=os.environ["SENDGO_ACCESS_KEY"],
secret_key=os.environ["SENDGO_SECRET_KEY"],
kakao_sender_key=os.environ.get("SENDGO_KAKAO_SENDER_KEY"),
sms_sender_key=os.environ.get("SENDGO_SMS_SENDER_KEY"),
api_version="v2",
)
client.alimtalk.send(
template_code="ORDER_CONFIRM_001",
contacts=[{"contact": "01012345678", "name": "Hong Gildong", "var1": "ORD-001"}],
)
PHP
<?php
use Sendgo\Php\Sendgo;
$sendgo = new Sendgo([
'access_key' => $_ENV['SENDGO_ACCESS_KEY'],
'secret_key' => $_ENV['SENDGO_SECRET_KEY'],
'kakao_sender_key' => $_ENV['SENDGO_KAKAO_SENDER_KEY'],
'sms_sender_key' => $_ENV['SENDGO_SMS_SENDER_KEY'],
'api_version' => 'v2',
]);
$sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => [
['contact' => '01012345678', 'name' => 'Hong Gildong', 'var1' => 'ORD-001'],
],
]);
Laravel
sendgo/laravel auto-registers its ServiceProvider, so inject the client directly.
<?php
use Sendgo\Php\Sendgo;
class OrderController extends Controller
{
public function __construct(private Sendgo $sendgo) {}
public function confirm(Order $order)
{
$this->sendgo->alimtalk->send([
'templateCode' => 'ORDER_CONFIRM_001',
'contacts' => [[
'contact' => $order->user->phone,
'name' => $order->user->name,
'var1' => $order->number,
]],
]);
return response()->json(['success' => true]);
}
}
Push this onto a queue in production — an external API call inside the request cycle makes your own response time depend on Kakao's.
Java
import io.sendgo.*;
import io.sendgo.model.*;
import java.util.List;
SendgoClient sendgo = new SendgoClient(SendgoConfig.builder()
.accessKey(System.getenv("SENDGO_ACCESS_KEY"))
.secretKey(System.getenv("SENDGO_SECRET_KEY"))
.kakaoSenderKey(System.getenv("SENDGO_KAKAO_SENDER_KEY"))
.smsSenderKey(System.getenv("SENDGO_SMS_SENDER_KEY"))
.apiVersion("v2")
.build());
sendgo.alimtalk().send(AlimtalkRequest.builder()
.templateCode("ORDER_CONFIRM_001")
.contacts(List.of(
Contact.builder().contact("01012345678").name("Hong Gildong").var1("ORD-001").build()
))
.build());
Go
client, err := sendgo.New(sendgo.Config{
AccessKey: os.Getenv("SENDGO_ACCESS_KEY"),
SecretKey: os.Getenv("SENDGO_SECRET_KEY"),
KakaoSenderKey: os.Getenv("SENDGO_KAKAO_SENDER_KEY"),
SmsSenderKey: os.Getenv("SENDGO_SMS_SENDER_KEY"),
ApiVersion: "v2",
})
if err != nil {
log.Fatal(err)
}
_, err = client.Alimtalk.Send(sendgo.AlimtalkRequest{
TemplateCode: "ORDER_CONFIRM_001",
Contacts: []sendgo.Contact{
{Contact: "01012345678", Name: "Hong Gildong", Var1: "ORD-001"},
},
})
If the first send failed
| Code | Meaning | Fix |
|---|---|---|
INVALID_ACCESS_KEY |
Wrong key | Check the environment variables are actually loaded |
ACCESS_KEY_NOT_APPROVED |
App not approved yet | Check the app status in the console |
IP_NOT_ALLOWED |
Called from outside the allowlist | Add your server's outbound IP |
INVALID_TEMPLATE_CODE |
Unknown or unapproved template | Compare against the console; it must be approved, not pending |
INVALID_KAKAO_SENDER_KEY |
Wrong sender profile key | Recheck the key in the console |
EMPTY_CONTACTS |
Recipient array is empty | Make sure contacts is actually populated |
PAYMENT_REQUIRED |
Out of credit | Top up; retrying will not help |
Full list: Error codes and retry strategy.
Next
자주 묻는 질문
- Do I really have to register a template before sending an Alimtalk?
- Yes. Alimtalk only delivers to templates that passed Kakao's review. You cannot compose the body at send time; you fill the variables (var1 to var8) of an approved template. If you need free-form text, use Brand Message or SMS/LMS instead.
- Can I test without registering a sending number?
- No. Korean telecommunications law requires the caller ID to be pre-registered, and an unregistered number fails at send time rather than at signup. Start the registration before you write any code — it is the longest lead time in the whole integration.
- Do I need to manage the auth token myself?
- No. Every official SDK issues the token, caches it, refreshes it on expiry and retries once on 401/403. Writing your own token loop means fetching a new token on every request.
- What is the base URL?
- https://sendgo.io/api, with a version segment of v1 or v2. Use v2 for new code. The SDKs default to the correct host, so you normally do not set it.