DEVELOPERS

SDK · 코드 예제

API가 REST + JSON이라 별도 라이브러리 없이 표준 HTTP 클라이언트만으로 연동됩니다. 바로 붙여 쓸 수 있는 예제를 정리했습니다.

공식 SDK 상태

Node.js · Python 패키지 준비 중

아직 배포된 공식 패키지가 없습니다(npm install 대상 없음). 아래 예제는 언어 기본 HTTP 클라이언트만 사용하므로 의존성 추가 없이 그대로 쓰실 수 있고, SDK가 나와도 호출 규약은 동일합니다.

공통 규약

  • Base URL: https://api.apexstack.com/api/send/v1
  • 인증 헤더: X-API-Key: axk_live_… (서버에서만 사용 — 클라이언트 코드에 넣지 마세요)
  • 요청·응답 모두 JSON(UTF-8), 응답 봉투는 { success, message, data }
  • 재시도는 같은 idempotencyKey로 — 중복 발송되지 않습니다

Node.js

런타임 내장 fetch 사용 (Node 18+). 별도 패키지가 필요 없습니다.

javascript
const BASE = "https://api.apexstack.com/api/send/v1";

async function call(path, { method = "GET", body } = {}) {
  const res = await fetch(BASE + path, {
    method,
    headers: {
      "X-API-Key": process.env.APEX_SEND_KEY,
      "Content-Type": "application/json",
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok || json.success === false) {
    // message 는 한국어 사유가 그대로 담겨 온다 — 로그에 남겨두면 원인 파악이 빠르다
    throw new Error(`[${res.status}] ${json.message ?? "요청 실패"}`);
  }
  return json.data;
}

// 승인된 템플릿 조회 → id 를 하드코딩하지 않는다(재등록 시 바뀜)
const templates = await call("/templates?status=APPROVED");
const tpl = templates.find((t) => t.name === "주문 완료 안내");

// 발송
const result = await call("/messages", {
  method: "POST",
  body: {
    channelId: "alimtalk",
    templateId: String(tpl.id),
    campaignName: "주문 완료 안내",
    idempotencyKey: `order-${order.id}`,
    recipients: [{
      to: order.phone,
      ref: `order-${order.id}`,
      vars: { 고객명: order.customerName, 주문번호: order.code },
    }],
  },
});

console.log(result.campaignId, result.estimatedCost, result.balanceAfter);

Python

requests 사용. 표준 라이브러리만 쓰려면 urllib.request 로 바꿔도 동일합니다.

python
import os, requests

BASE = "https://api.apexstack.com/api/send/v1"
HEADERS = {"X-API-Key": os.environ["APEX_SEND_KEY"], "Content-Type": "application/json"}

def call(path, method="GET", body=None):
    res = requests.request(method, BASE + path, headers=HEADERS, json=body, timeout=15)
    data = res.json()
    if not res.ok or data.get("success") is False:
        raise RuntimeError(f"[{res.status_code}] {data.get('message', '요청 실패')}")
    return data["data"]

templates = call("/templates?status=APPROVED")
tpl = next(t for t in templates if t["name"] == "주문 완료 안내")

result = call("/messages", "POST", {
    "channelId": "alimtalk",
    "templateId": str(tpl["id"]),
    "idempotencyKey": f"order-{order_id}",
    "recipients": [{
        "to": phone,
        "ref": f"order-{order_id}",
        "vars": {"고객명": customer_name, "주문번호": order_code},
    }],
})
print(result["campaignId"], result["balanceAfter"])

PHP

php
<?php
$base = "https://api.apexstack.com/api/send/v1";
$payload = json_encode([
  "channelId" => "alimtalk",
  "templateId" => "128",
  "idempotencyKey" => "order-" . $orderId,
  "recipients" => [[
    "to" => $phone,
    "ref" => "order-" . $orderId,
    "vars" => ["고객명" => $name, "주문번호" => $code],
  ]],
], JSON_UNESCAPED_UNICODE);

$ch = curl_init("$base/messages");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["X-API-Key: " . getenv("APEX_SEND_KEY"), "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => $payload,
]);
$res = json_decode(curl_exec($ch), true);
if (empty($res["success"])) { throw new RuntimeException($res["message"] ?? "요청 실패"); }
echo $res["data"]["campaignId"];

연동할 때 자주 걸리는 것

  • templateId 하드코딩 — 템플릿이 반려되어 재등록되면 id가 바뀌어 조용히 실패합니다. 매번 /templates로 조회하거나 앱 시작 시 캐시하세요.
  • 변수 누락 — 템플릿의 #{변수}를 하나라도 빠뜨리면 400입니다. /templates 응답의 변수 목록으로 검증하세요.
  • 전화번호 형식 — 하이픈은 있어도 되지만 국가번호 없이 010… 형태를 권장합니다.
  • 잔액 감시 — 선불이라 0이 되면 발송이 402로 막힙니다. READ_ONLY 키로 /balance를 주기 점검하세요.
  • 키를 클라이언트에 두지 않기 — 브라우저·모바일 앱에서 직접 호출하면 키가 그대로 노출됩니다. 반드시 서버 경유로.

원하는 언어의 SDK가 있으신가요?

어떤 언어·프레임워크에서 쓰실지 알려주시면 우선순위에 반영하겠습니다.