API 레퍼런스 — 공통 규약
알마니 Open API의 모든 엔드포인트에 공통으로 적용되는 규약입니다. 인증 방법, 요청·응답 규격, 응답 래퍼와 쿼터, 에러 코드, 처리율 제한, 커서 페이지네이션, 멱등성 처리를 다룹니다.
https://docs.almani-center.com/open/v1 을 기준으로 합니다.
예시의 ak_live_xxxx 는 더미 키이며, 실제 키는 알마니 콘솔에서 발급합니다.
인증
모든 요청은 Authorization 헤더의 API 키로 인증합니다.
키는 알마니 콘솔에서 발급하며 ak_live_ 로 시작합니다.
발급 사이트가 속한 계정의 사이트에 접근할 수 있고, 키마다 template:read,
template:write 같은 허용 스코프를 가집니다.
요청 헤더
Bearer ak_live_xxxx 형식의 시크릿 키입니다.
알마니 콘솔에서 발급하며, 키마다 허용된 스코프를 가집니다.
대상 사이트입니다. 서버가 사이트 소유권을 검증한 뒤 요청 컨텍스트를 구성합니다.
단, GET /sites,
GET /me 만 생략할 수 있습니다.
쓰기 API 재시도 안전장치입니다. 최대 100자입니다. 멱등성 (Idempotency-Key) 를 참고하세요.
검증 순서
서버는 매 요청을 다음 순서로 검증합니다.
- 키 유효성 확인 (활성·만료 여부) — 실패 시
4001/4002 - API 키 단위 rate limit 차감 — 초과 시
4290 - 스코프 확인 — 실패 시
4005 - X-Site-Id 소유권 — 실패 시
4003/4004
실패 지점별 에러 코드의 의미와 대응은 에러 형식과 코드 표를 참고하세요.
curl "https://docs.almani-center.com/open/v1/templates" \
-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();
String response = restClient.get()
.uri("/templates")
.retrieve()
.body(String.class);
HTTP/1.1 200 OK
{
"code": "0000",
"message": "OK",
"data": { ... },
"meta": {
"requestId": "req_9f83ab21",
"quota": {
"rateLimit": {
"limit": 120,
"remaining": 119,
"resetAt": "2026-07-25T14:31:00+09:00"
}
}
}
}
HTTP/1.1 403 Forbidden
{
"code": "4005",
"message": "요청에 필요한 스코프가 없습니다. template:write 스코프를 포함한 키로 다시 요청해 주세요.",
"data": null,
"meta": {
"requestId": "req_9f83ab21",
"quota": {
"rateLimit": {
"limit": 120,
"remaining": 118,
"resetAt": "2026-07-25T14:31:00+09:00"
}
}
}
}
요청·응답 규격
모든 엔드포인트에 공통 적용되는 기본 규약입니다.
| 항목 | 규약 |
|---|---|
| Base URL | https://docs.almani-center.com/open/v1 |
| Content-Type | 요청/응답 모두 application/json; charset=utf-8 |
| 날짜/시간 | ISO-8601, KST 오프셋 포함 — 2026-07-25T14:30:00+09:00 |
| ID 표기 | 템플릿은 코드 문자열(templateCode, 예: TPL_20260725143012)이 조회 키 |
| Y/N 필드 | Y/N 형태의 상태 값은 API에서 boolean으로 제공 (예: "active": true) |
| 버저닝 | URL 경로 버전(/open/v1). 하위호환 깨지는 변경은 /open/v2 |
응답 래퍼와 meta.quota
모든 응답(에러 포함)은 공통 래퍼를 따릅니다.
code 는 결과 코드("0000" = 성공),
data 는 엔드포인트별 페이로드(에러 시 null),
meta.requestId 는 추적용 ID입니다.
meta.quota 에 항상 포함되며, 유효한 API 키가 식별된 요청에는
동일 정보가 RateLimit-* 헤더로도 제공됩니다.
에이전트/클라이언트는 별도 쿼터 조회 없이 매 응답에서 잔여치를 확인할 수 있습니다.
| 블록 | 포함 범위 | 설명 |
|---|---|---|
rateLimit |
모든 응답 | API 키 단위 분당 요청 한도 — 항상 포함 |
templateReview |
템플릿 신청/재검수/취소 + GET /quota |
사이트 단위 일일 템플릿 검수 한도 (일자 집계) |
points |
GET /quota |
포인트 잔액 — 발송·과금 관련 API 응답에 포함 |
사이트 기준 전체 쿼터를 한 번에 조회하려면 계정 API를 참고하세요.
커서 페이지네이션
목록 API는 커서 방식입니다. 응답의 data.nextCursor 가
null 이면 마지막 페이지입니다.
기본 20, 최대 100.
이전 응답의 data.nextCursor 값 (opaque). 없으면 첫 페이지.
멱등성 (Idempotency-Key)
템플릿 생성·검수 요청과 발신프로필 인증번호 요청·등록에
Idempotency-Key 헤더(UUID 권장)를 보내면
같은 API 키·엔드포인트·사이트에서 동일한 키와 요청 바디로 재요청할 때
최초 성공 결과를 그대로 반환합니다(24시간 보관).
네트워크 재시도로 인한 중복 템플릿 신청을 방지합니다.
같은 조건에서 동일한 키에 다른 요청 바디를 사용하거나 첫 요청이 아직 처리 중이면
4091 을 반환합니다.
에러 형식과 코드를 참고하세요.
{
"code": "0000", // 결과 코드 — "0000" = 성공
"message": "OK",
"data": { ... }, // 엔드포인트별 페이로드 (에러 시 null)
"meta": {
"requestId": "req_9f83ab21", // 추적용 ID — 문의 시 함께 전달
"quota": {
"rateLimit": { // 항상 포함 (키 단위)
"limit": 120, // API 키당 분당 허용 요청 수
"remaining": 118,
"resetAt": "2026-07-25T14:31:00+09:00"
},
"templateReview": { // 템플릿 쓰기 API 응답에 포함 (사이트 단위)
"dailyLimit": 30,
"usedToday": 4,
"remaining": 26,
"resetAt": "2026-07-26T00:00:00+09:00"
},
"points": { "balance": 1523000 } // 발송·과금 관련 API 응답에 포함
}
}
}
HTTP/1.1 403 Forbidden
{
"code": "4004",
"message": "요청한 사이트는 키 소유자의 사이트가 아닙니다. 사이트 ID를 확인해 주세요.",
"data": null,
"meta": {
"requestId": "req_9f83ab21",
"quota": {
"rateLimit": {
"limit": 120,
"remaining": 118,
"resetAt": "2026-07-25T14:31:00+09:00"
}
}
}
}
에러 형식과 코드
에러 응답도 공통 래퍼를 따릅니다. code 에 결과 코드,
message 에 사유가 담기며 data 는 null 입니다.
문의 시에는 meta.requestId 를 함께 전달해 주세요.
| 코드 | HTTP | 의미 | 대응 |
|---|---|---|---|
0000 | 200 | 성공 | — |
4000 | 400 | 요청 형식 오류 (필드 누락/타입/제약 위반) | message에 필드별 사유 |
4001 | 401 | 키 없음/형식 오류/존재하지 않는 키 | 키 확인 |
4002 | 401 | 키 만료 또는 비활성 | 콘솔에서 재발급 |
4003 | 400 | X-Site-Id 헤더 누락 | GET /sites로 사이트 선택 |
4004 | 403 | 키 소유자의 사이트가 아님 | 사이트 ID 확인 |
4005 | 403 | 스코프 부족 (예: template:write 없음) | 키 스코프 확인 |
4040 | 404 | 리소스 없음 (템플릿 코드 불일치) | — |
4090 | 409 | 상태 전이 불가 (예: APR 템플릿에 검수 요청) | message에 현재 상태 포함 |
4091 | 409 | 중복 (템플릿명 중복, Idempotency 충돌) | — |
4290 | 429 | 분당 rate limit 초과 | Retry-After 후 재시도 |
4291 | 429 | 일일 템플릿 검수 요청 한도 초과 | 다음 날 자정 이후 재시도 |
5000 | 500 | 서버 내부 오류 | meta.requestId로 문의 |
6000 | 502 | 카카오 검수·채널 연동 오류 (상세 사유 message에 포함) | 재시도 또는 문의 |
6001 | 502 | 발신프로필 미등록 또는 유효하지 않음 | 발신프로필 등록 |
HTTP/1.1 400 Bad Request
{
"code": "4000",
"message": "templateName: 필수 항목이 누락되었습니다.",
"data": null,
"meta": {
"requestId": "req_9f83ab21",
"quota": {
"rateLimit": {
"limit": 120,
"remaining": 117,
"resetAt": "2026-07-25T14:31:00+09:00"
}
}
}
}
처리율 제한
요청 한도는 API 키별로 분당 120회입니다.
잔여치는 매 응답의 meta.quota.rateLimit 에 항상 포함되고,
유효한 API 키가 식별된 요청에는 동일 정보가 RateLimit-* 응답 헤더로도 제공됩니다.
-
4290— 분당 rate limit 초과.Retry-After헤더(초 단위)만큼 기다린 뒤 재시도하세요. -
4291— 일일 템플릿 검수 요청 한도 초과. 다음 날 자정 이후 재시도하세요.
Retry-After 헤더는 분당 한도 초과(4290) 응답에만 포함됩니다.
해당 시간(초) 이후에 재시도하도록 클라이언트를 구현하세요.
RateLimit-Limit: 120
RateLimit-Remaining: 118
RateLimit-Reset: 42 # 초 단위
Retry-After: 42 # 4290 응답에만
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 120
RateLimit-Remaining: 0
RateLimit-Reset: 42
Retry-After: 42
{
"code": "4290",
"message": "분당 요청 한도를 초과했습니다.",
"data": null,
"meta": {
"requestId": "req_9f83ab21",
"quota": {
"rateLimit": {
"limit": 120,
"remaining": 0,
"resetAt": "2026-07-25T14:31:00+09:00"
}
}
}
}