빠른 시작

5분 안에 첫 알림톡 템플릿을 등록하고 카카오 검수 요청 → 승인 확인까지 완주하는 가이드입니다. 순서는 다음과 같습니다.

  1. 콘솔에서 API 키 발급 — 코드 없이 콘솔에서 준비
  2. 사이트 ID 확인GET /sites
  3. 템플릿 신청POST /templates (등록 즉시 검수 요청)
  4. 승인 확인GET /templates/{templateCode} 폴링
  5. 반려 시 재검수POST /templates/{templateCode}/review
시작 전에 Base URL은 https://docs.almani-center.com/open/v1 입니다. 문서의 ak_live_xxxx 는 더미 키이며, 실행 전에 1단계에서 발급받은 실제 키로 바꿔 주세요. 인증 상세 규칙은 공통 규약 — 인증을 참고하세요.
발신프로필이 먼저 필요합니다 템플릿 검수 요청은 사이트에 발신프로필(카카오톡 채널)이 등록되어 있어야 처리됩니다. 미등록 상태면 템플릿 신청이 6001 에러로 거절됩니다. 발신프로필 레퍼런스에서 먼저 등록을 마쳐 주세요.

① 콘솔에서 API 키 발급

이 단계는 코드가 필요 없습니다. 알마니 콘솔에 로그인해 API 키를 발급받습니다.

  1. 키 발급 — 콘솔의 API 키 발급 화면에서 새 키를 만듭니다. 키는 계정에 속한 사이트에 접근하는 시크릿 키로, ak_live_ 로 시작합니다.
  2. 스코프 확인 — 키마다 허용 스코프가 지정됩니다. 이 가이드를 완주하려면 template:readtemplate:write 두 스코프가 모두 허용된 키가 필요합니다. 스코프가 부족하면 요청이 4005 에러로 거절됩니다.
  3. 요청 한도 확인 — Open API는 호출 IP를 제한하지 않으며, API 키당 분당 120회까지 호출할 수 있습니다. 초과 시 4290Retry-After가 반환됩니다.
  4. 키 상태 관리 — 만료되었거나 비활성화된 키는 4002 에러가 반환되며, 콘솔에서 재발급합니다.
키 보안 API 키는 지정된 스코프 범위에서 계정 소유 사이트에 접근할 수 있습니다. 브라우저 코드나 공개 저장소에 노출하지 말고, 서버 측 환경 변수로만 관리하세요.

② 사이트 ID 확인

GET /sites

거의 모든 API는 대상 사이트를 지정하는 X-Site-Id 헤더가 필수입니다. 발급받은 키로 접근할 수 있는 사이트 목록을 조회해, 이후 단계에서 사용할 siteId 를 확인합니다. 이 목록에는 키를 발급한 사이트 하나만이 아니라 키 소유 계정의 삭제되지 않은 전체 사이트가 포함됩니다. 전체 규격은 사이트 목록 조회 레퍼런스를 참고하세요.

X-Site-Id 없이 호출할 수 있습니다 GET /sitesGET /me 와 함께 X-Site-Id 헤더를 생략할 수 있는 엔드포인트입니다. 연동 초기, 사이트 ID를 아직 모르는 상태에서 호출하세요. 키 자체 검증에는 계정 정보 조회도 사용할 수 있습니다.

Response

items[] array of object

키 소유 계정의 삭제되지 않은 전체 사이트 목록입니다.

items[].siteId number

사이트 ID입니다. 이후 모든 요청의 X-Site-Id 헤더에 이 값을 넣습니다.

items[].siteName string

사이트 이름입니다.

items[].platform string

사이트의 쇼핑몰 플랫폼입니다. 사용 가능한 개인화 변수의 제공 범위는 플랫폼에 따라 다릅니다.

요청 예시
# X-Site-Id 없이 호출할 수 있는 엔드포인트입니다
curl "https://docs.almani-center.com/open/v1/sites" \
  -H "Authorization: Bearer ak_live_xxxx"
// Java 17 + Spring Framework 6 RestClient
// GET /sites 는 X-Site-Id 없이 호출할 수 있습니다
RestClient restClient = RestClient.builder()
        .baseUrl("https://docs.almani-center.com/open/v1")
        .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer ak_live_xxxx")
        .build();

String response = restClient.get()
        .uri("/sites")
        .retrieve()
        .body(String.class);
응답 예시
HTTP/1.1 200 OK

{
  "code": "0000",
  "message": "OK",
  "data": {
    "items": [
      {
        "siteId": 1024,
        "siteName": "알마니몰",
        "platform": "cafe24",
        "siteUrl": "https://almanimall.example.com",
        "active": true,
        "registeredAt": "2026-06-10T09:12:00+09:00",
        "senderProfileRegistered": true
      }
    ],
    "nextCursor": null
  },
  "meta": {
    "requestId": "req_1d72e0a4",
    "quota": {
      "rateLimit": {
        "limit": 120,
        "remaining": 119,
        "resetAt": "2026-07-25T15:21:00+09:00"
      }
    }
  }
}

③ 템플릿 신청

POST /templates

필요 스코프: template:write

첫 템플릿을 신청합니다. autoReview 는 기본값이 true 이므로, 별도 옵션 없이 이 요청 한 번으로 등록과 동시에 카카오 검수까지 즉시 요청됩니다. 성공 시 templateCode 가 발급되고 상태는 곧바로 REQ(검수중)가 됩니다. 초안(REG)으로만 저장하려면 autoReview: false 를 보내세요.

Request Body (필수 필드)

templateName 필수 string

템플릿 이름입니다. 최대 30자, 사이트 내 중복 불가(중복 시 4091).

templateType 필수 string

템플릿 유형입니다. BA 기본형 · IM 이미지형 · EX 강조표기형 · IT 아이템리스트형. 이 가이드는 기본형(BA)을 사용합니다.

category 필수 string

알림톡 템플릿 카테고리 코드입니다.

content 필수 string

메시지 본문입니다. 변수는 #{변수명} 형식으로 넣습니다. 기본형 최대 1,000자. 변수만으로 구성된 본문은 반려됩니다.

버튼·강조표기·이미지 등 선택 필드의 전체 규격은 템플릿 신청 레퍼런스를 참고하세요. 예시 본문의 #{고객명}, #{주문번호} 처럼 표준 개인화 변수명을 그대로 쓰면 mappingData 없이 자동 매핑됩니다.

검수 쿼터를 소모합니다 autoReview: true(기본값) 신청은 사이트 단위 일일 검수 한도를 소모합니다. 잔여 한도는 응답의 meta.quota.templateReview 에서 확인하세요. 한도를 초과하면 4291 에러(HTTP 429)가 반환됩니다.

주요 에러

코드HTTP설명
4000400필수 필드 누락 등 요청 형식 오류
4091409템플릿명 중복
4291429일일 템플릿 검수 요청 한도 초과
6001502 발신프로필 미등록 또는 유효하지 않음 — 발신프로필 참조
요청 예시
curl -X POST "https://docs.almani-center.com/open/v1/templates" \
  -H "Authorization: Bearer ak_live_xxxx" \
  -H "X-Site-Id: 1024" \
  -H "Idempotency-Key: 2f8f4d1a-6c3b-4e0d-9a52-b71c0e5d8f36" \
  -H "Content-Type: application/json" \
  -d '{
    "templateName": "주문완료 안내 (빠른 시작)",
    "templateType": "BA",
    "category": "004001",
    "content": "#{고객명}님, 주문이 완료되었습니다.\n\n▶ 주문번호: #{주문번호}\n\n배송이 시작되면 다시 알려드릴게요."
  }'
// Java 17 + Spring Framework 6 RestClient
RestClient restClient = RestClient.builder()
        .baseUrl("https://docs.almani-center.com/open/v1")
        .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer ak_live_xxxx")
        .defaultHeader("X-Site-Id", "1024")
        .build();

record CreateTemplateRequest(
        String templateName, String templateType,
        String category, String content) {}

var request = new CreateTemplateRequest(
        "주문완료 안내 (빠른 시작)", "BA", "004001",
        "#{고객명}님, 주문이 완료되었습니다.\n\n▶ 주문번호: #{주문번호}\n\n"
                + "배송이 시작되면 다시 알려드릴게요.");

String response = restClient.post()
        .uri("/templates")
        .header("Idempotency-Key", UUID.randomUUID().toString())
        .contentType(MediaType.APPLICATION_JSON)
        .body(request)
        .retrieve()
        .body(String.class);
응답 예시
HTTP/1.1 200 OK

{
  "code": "0000",
  "message": "OK",
  "data": {
    "templateId": 5812,
    "templateCode": "TPL_20260725153000",
    "approveStatus": "REQ",
    "requestedAt": "2026-07-25T15:30:00+09:00"
  },
  "meta": {
    "requestId": "req_a1c94e07",
    "quota": {
      "rateLimit": {
        "limit": 120,
        "remaining": 118,
        "resetAt": "2026-07-25T15:31:00+09:00"
      },
      "templateReview": {
        "dailyLimit": 30,
        "usedToday": 6,
        "remaining": 24,
        "resetAt": "2026-07-26T00:00:00+09:00"
      }
    }
  }
}

④ 승인(APR) 상태 확인

GET /templates/{templateCode}

필요 스코프: template:read

3단계에서 발급받은 templateCode 로 검수 결과를 확인합니다. 이 API는 AL_TEMPLATE.APPROVE_STATUS에 저장된 현재 검수 상태를 반환합니다. REQ 상태여도 요청을 처리하면서 비즈톡 상태를 실시간으로 재조회하지 않습니다.

  • approveStatusAPR이면 승인 완료 — sendable: true 가 되어 발송에 사용할 수 있습니다.
  • REJ면 반려 — rejectReason 으로 사유를 확인하고 5단계로 진행합니다.

Path 파라미터

templateCode 필수 string

템플릿 코드입니다 (예: TPL_20260725153000).

Response (핵심 필드)

approveStatus string

현재 상태입니다 — REG/REQ/APR/REJ/UPT.

rejectReason string | null

REJ일 때 카카오 심사 코멘트 전문(최신)입니다.

sendable boolean

APR 여부 — 발송에 사용할 수 있는지를 나타냅니다.

전체 응답 필드는 템플릿 조회 레퍼런스를 참고하세요.

상태 조회 방식 상세 조회는 비즈톡을 호출하지 않고 저장된 상태를 즉시 반환합니다. 반복 조회가 필요하면 API 키당 분당 120회 한도와 RateLimit-* 응답 헤더를 기준으로 호출 주기를 정하세요.
요청 예시
# 필요한 시점에 저장된 approveStatus를 조회합니다
curl "https://docs.almani-center.com/open/v1/templates/TPL_20260725153000" \
  -H "Authorization: Bearer ak_live_xxxx" \
  -H "X-Site-Id: 1024"
// Java 17 + Spring Framework 6 RestClient
RestClient restClient = RestClient.builder()
        .baseUrl("https://docs.almani-center.com/open/v1")
        .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer ak_live_xxxx")
        .defaultHeader("X-Site-Id", "1024")
        .build();

// 필요한 시점에 저장된 data.approveStatus를 조회합니다
String response = restClient.get()
        .uri("/templates/{templateCode}", "TPL_20260725153000")
        .retrieve()
        .body(String.class);
응답 예시 (승인)
HTTP/1.1 200 OK

{
  "code": "0000",
  "message": "OK",
  "data": {
    "templateCode": "TPL_20260725153000",
    "templateName": "주문완료 안내 (빠른 시작)",
    "approveStatus": "APR",
    "rejectReason": null,
    "templateType": "BA",
    "category": "004001",
    "securityEnabled": false,
    "content": "#{고객명}님, 주문이 완료되었습니다.\n\n▶ 주문번호: #{주문번호}\n\n배송이 시작되면 다시 알려드릴게요.",
    "buttons": [],
    "mappingData": {
      "고객명": "고객명",
      "주문번호": "주문번호"
    },
    "sendable": true,
    "recentHistory": [
      {
        "status": "APR",
        "comment": "승인되었습니다.",
        "at": "2026-07-25T16:11:52+09:00"
      },
      {
        "status": "REQ",
        "comment": "주문 완료 시 발송되는 정보성 메시지입니다.",
        "at": "2026-07-25T15:30:00+09:00"
      }
    ]
  },
  "meta": {
    "requestId": "req_7e30ba9c",
    "quota": {
      "rateLimit": {
        "limit": 120,
        "remaining": 119,
        "resetAt": "2026-07-25T16:13:00+09:00"
      }
    }
  }
}

⑤ 반려(REJ) 시 — 수정 후 재검수 요청

POST /templates/{templateCode}/review

필요 스코프: template:write

4단계 폴링 결과가 REJ라면 rejectReason(카카오 심사 코멘트 전문)을 확인하고, 사유를 반영해 수정한 뒤 재검수를 요청합니다. 요청 바디에 수정할 필드 (content / buttons 등, 템플릿 신청과 동일 규격)를 함께 보내면 수정과 재검수를 한 번에 처리합니다. 성공 시 상태는 다시 REQ가 되며, 4단계 폴링으로 돌아가 결과를 확인합니다.

Path 파라미터

templateCode 필수 string

템플릿 코드입니다.

Request Body

content / buttons / emphasis / image / itemList / mappingData 선택

템플릿 신청과 동일 규격입니다. 보낸 필드만 수정한 뒤 검수를 요청합니다.

comments 선택 string

카카오 심사 담당자에게 전달되는 의견입니다. 반려 사유에 대한 소명을 권장합니다.

주요 에러

코드HTTP설명
4090409 상태 전이 불가 — 재검수는 REG·REJ·UPT 상태에서만 가능 (message에 현재 상태 포함)
4291429일일 템플릿 검수 요청 한도 초과
재검수도 일일 검수 한도를 소모합니다 잔여 한도는 응답의 meta.quota.templateReview 에서 확인하세요. 재검수 전에 사전 정책 검증으로 반려 위험을 먼저 진단하면 한도 소모를 줄일 수 있습니다(검증은 한도를 소모하지 않습니다). 반려 사유 확인 방법과 대응 절차 전체는 템플릿 심사 가이드를 참고하세요.
요청 예시
curl -X POST "https://docs.almani-center.com/open/v1/templates/TPL_20260725153000/review" \
  -H "Authorization: Bearer ak_live_xxxx" \
  -H "X-Site-Id: 1024" \
  -H "Idempotency-Key: 9b41e7d2-5a80-4c6f-b3e9-2c7f0d8a6e15" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "#{고객명}님, 주문이 완료되었습니다.\n\n▶ 주문번호: #{주문번호}\n\n배송이 시작되면 알림톡으로 다시 안내드립니다.",
    "comments": "반려 사유를 반영해 본문을 수정했습니다."
  }'
// Java 17 + Spring Framework 6 RestClient
RestClient restClient = RestClient.builder()
        .baseUrl("https://docs.almani-center.com/open/v1")
        .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer ak_live_xxxx")
        .defaultHeader("X-Site-Id", "1024")
        .build();

record ReviewRequest(String content, String comments) {}

var request = new ReviewRequest(
        "#{고객명}님, 주문이 완료되었습니다.\n\n▶ 주문번호: #{주문번호}\n\n"
                + "배송이 시작되면 알림톡으로 다시 안내드립니다.",
        "반려 사유를 반영해 본문을 수정했습니다.");

String response = restClient.post()
        .uri("/templates/{templateCode}/review", "TPL_20260725153000")
        .header("Idempotency-Key", UUID.randomUUID().toString())
        .contentType(MediaType.APPLICATION_JSON)
        .body(request)
        .retrieve()
        .body(String.class);
응답 예시
HTTP/1.1 200 OK

{
  "code": "0000",
  "message": "OK",
  "data": {
    "templateCode": "TPL_20260725153000",
    "approveStatus": "REQ",
    "requestedAt": "2026-07-25T16:25:31+09:00"
  },
  "meta": {
    "requestId": "req_e62f18ad",
    "quota": {
      "rateLimit": {
        "limit": 120,
        "remaining": 117,
        "resetAt": "2026-07-25T16:26:00+09:00"
      },
      "templateReview": {
        "dailyLimit": 30,
        "usedToday": 7,
        "remaining": 23,
        "resetAt": "2026-07-26T00:00:00+09:00"
      }
    }
  }
}

다음 단계

승인(APR)된 템플릿은 sendable: true 가 되어 발송에 사용할 수 있습니다. 발송 연결(캠페인 설정)은 알마니 콘솔에서 수행합니다. 이어서 읽어 보세요.