본문 바로가기
Documentation menu

Python / SDK REFERENCE

Django SDK Guide

The official Django extension for sending Kakao Alimtalk, Brand Message and SMS — configured from settings, with a lazily built client.

이 문서의 목차
패키지
sendgo-django
언어
Python
레지스트리
PyPI
설치
pip install sendgo-django

The official Django extension for sending Kakao Alimtalk, Brand Message and SMS

sendgo-django wraps the sendgo-python core so the client is configured from settings.SENDGO and built lazily on first use.

Requires Django 4.2+ and Python 3.10+.


Install

pip install sendgo-django

The core sendgo-python comes along as a dependency.


Configure

1. Add the app

# settings.py
INSTALLED_APPS = [
    # ...
    "sendgo_django",
]

2. Add the settings dict

# settings.py
import os

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",
}

ACCESS_KEY and SECRET_KEY are required — omitting either raises ImproperlyConfigured on first use rather than failing silently at send time.


Quick start

from sendgo_django import client

client.alimtalk.send(
    template_code="ORDER_CONFIRM_001",
    contacts=[{"contact": "01012345678", "name": "Gildong Hong", "var1": "ORD-001"}],
)

client is a SimpleLazyObject, so importing it at module level does not read settings or build anything. The core client is created on first attribute access and memoised after that — safe to import in models.py, signals, or anywhere else that loads during startup.

Every channel from the core is available on it:

Attribute Channel
client.alimtalk Kakao Alimtalk
client.friendtalk Kakao Friendtalk
client.brand_message Kakao Brand Message (v2 only)
client.sms SMS / LMS / MMS

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. Set "API_VERSION": "v2".

from sendgo_django import client

# Single send — channel friends
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 contacts)
client.brand_message.broadcast(
    message_type="FW",
    friend_template_uuid="9cd5460b-6458-4edc-9b11-c26d3013c340",
)

# Campaign lookups. `from` is a Python keyword, so the argument is `from_`.
campaigns = client.brand_message.campaigns(from_="2026-08-01", count=10)
one = client.brand_message.campaign("1f0a6d0e-6b3b-4f0f-9b2f-2f6f6a1b7c11")

Sending from a signal

Send after the transaction commits, not inside it — otherwise a rollback still leaves the message delivered:

# orders/signals.py
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver

from .models import Order
from .tasks import send_order_confirm

@receiver(post_save, sender=Order)
def notify_order_created(sender, instance, created, **kwargs):
    if not created:
        return

    transaction.on_commit(
        lambda: send_order_confirm.delay(instance.phone, instance.number)
    )

Celery task

Sending is a network call, so keep it out of the request cycle:

# orders/tasks.py
from celery import shared_task
from sendgo import SendgoError
from sendgo_django import client

@shared_task(bind=True, max_retries=3)
def send_order_confirm(self, phone: str, order_no: str):
    try:
        client.alimtalk.send(
            template_code="ORDER_CONFIRM_001",
            contacts=[{"contact": phone, "var1": order_no}],
        )
    except SendgoError as exc:
        # 5xx is transient; a 4xx will not succeed on retry.
        if exc.status_code >= 500:
            raise self.retry(exc=exc, countdown=2 ** self.request.retries)
        raise

Management command

# orders/management/commands/send_promo.py
from django.core.management.base import BaseCommand
from sendgo_django import client

class Command(BaseCommand):
    help = "Broadcast the current promotion to every consenting channel friend"

    def handle(self, *args, **options):
        result = client.brand_message.broadcast(
            message_type="FW",
            friend_template_uuid="9cd5460b-6458-4edc-9b11-c26d3013c340",
        )

        self.stdout.write(self.style.SUCCESS(f"Accepted: {result['data']['campaignId']}"))

Testing

Patch the lazy object's underlying client so no request leaves your test suite:

from unittest.mock import MagicMock, patch

@patch("sendgo_django.conf.get_client")
def test_order_creation_sends_alimtalk(get_client, db):
    fake = MagicMock()
    get_client.return_value = fake

    Order.objects.create(phone="01012345678", number="ORD-001")

    fake.alimtalk.send.assert_called_once()

Patching get_client rather than the lazy object keeps each test isolated. sendgo_django.conf.reset() drops the memoised instance if you need to rebuild it mid-test.


Error handling

from sendgo import SendgoError
from sendgo_django import client

try:
    client.alimtalk.send(
        template_code="ORDER_CONFIRM_001",
        contacts=[{"contact": "01012345678"}],
    )
except SendgoError as e:
    if e.error_code in ("INVALID_ACCESS_KEY", "INVALID_SECRET_KEY"):
        logger.critical("Check the Sendgo API keys")
    elif e.error_code == "PAYMENT_REQUIRED":
        logger.critical("Out of Sendgo credit")
    elif e.error_code == "IP_NOT_ALLOWED":
        logger.critical("IP is not allow-listed")
    elif e.status_code >= 500:
        retry_later()
    else:
        logger.error("Sendgo %s: %s", e.status_code, e)

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 SDK: the token is reissued and the request retried once.


Settings reference

Key Required Default Description
ACCESS_KEY required — Sendgo access key
SECRET_KEY required — Sendgo secret key
KAKAO_SENDER_KEY optional None Kakao sender profile key
SMS_SENDER_KEY optional None SMS caller ID key
API_VERSION optional "v1" API version (v1 | v2)
BASE_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.

from sendgo_django import client

created = client.short_url.create(
    target_url="https://example.com/promotions/summer-sale",
    title="Summer sale landing",
)

code = created["data"]["code"]
stats = client.short_url.stats(code, from_="2026-08-01")

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 ↑