슈퍼관리자 (리셀러) / 가맹점 API

상위계정(리셀러)이 REST로 자기 하위 가맹점을 생성·API키발급·발신번호 신청·발송(차감제어)·포인트기록하고, 가맹점이 자기 포인트를 기록·조회하는 API입니다.

2026-09-23부터 https://imsend.kr/docs/admin-api 로 공개합니다(이전: 관리자 화면 안에서만 열람). 이 API 는 imsend 가 발급한 슈퍼관리자 키가 있어야 호출할 수 있습니다.

공통 규칙
• 키: 모든 /api/admin/** 는 role='ADMIN' 키만 받습니다. 일반 가맹점 키·무효·폐기·만료 키, 소유 계정이 활성이 아니면 401.
• 범위: 대상 {tenantId} 는 내 하위 가맹점(parent_tenant_id = 키 소유 계정)이어야 합니다. 없는 가맹점 404, 남의 가맹점·상위계정 자신 403.
• 접미사: 생성 시 tenant_id 는 접미사(영숫자·하이픈 1~20자, 언더스코어 불가)이고 최종 ID 는 {소유자}_{접미사}(36자 이하)로 강제됩니다. 소유자 접두를 붙여 보내도 한 번만 붙습니다.
• 선불 한도 0: 선불(PREPAID)은 신용한도가 항상 0 으로 저장됩니다(보낸 값 무시). 후불 한도는 0~100억 원.
Base URL https://imsend.kr
인증 헤더 X-API-Key: <key> 또는 Authorization: Bearer <key>
응답 공통 { "success": true, "data": {...} } 또는 { "success": false, "message": "..." }

리셀러 모델 개요

대상키저장범위
슈퍼관리자 API (①②③④)슈퍼관리자 키im_api_key.role='ADMIN', tenant_seq=상위계정자기 하위 가맹점만
가맹점 포인트 API가맹점 키im_api_key.role='TENANT'자기 자신만
  • 슈퍼관리자 키는 특정 상위계정(예: impacsaas)에 묶임. 그 계정이 만든 하위 가맹점 impacsaas_*(정확히는 im_tenant.parent_tenant_id == impacsaas)만 관리합니다.
  • 다른 고객 테넌트나 자기 상위계정 자체는 관리 불가(403). 전역 운영·상위계정 관리는 웹 관리자에서만.

슈퍼관리자 키 발급 (웹 관리자)

가맹점 상세 화면(/imSiteCtrl/tenant/detail?tenantId=...) → API 키 관리 → "슈퍼관리자 API 키 발급"(주황 버튼).

  • 발급 시 비고(remark) 입력 가능. 키 목록에 "슈퍼관리자" 뱃지와 비고가 표시됩니다.
  • (수동 대안) UPDATE im_api_key SET role='ADMIN' WHERE key_seq=발급키번호;

① 하위 가맹점 생성/조회 슈퍼관리자 키

POST /api/admin/tenants

하위 가맹점 생성. tenant_id 는 접미사로만 사용되어 {소유자}_{접미사}로 강제됩니다.

필드필수설명
tenant_id✅접미사(영숫자·하이픈 1~20자). 최종 ID = {소유자}_{이값}
tenant_name가맹점명
callback_url콜백 URL
billing_typePREPAID(기본) / POSTPAID
status기본 ACTIVE
credit_limit후불 신용한도(원), 0~10,000,000,000원. 선불은 0
# impacsaas 소유 슈퍼관리자 키로 호출 → impacsaas_1234 생성
curl -X POST https://imsend.kr/api/admin/tenants \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"tenant_id":"1234","tenant_name":"ACME지점","billing_type":"POSTPAID","credit_limit":1000000}'
{"success":true,"data":{"tenant_seq":42,"tenant_id":"impacsaas_1234","parent_tenant_id":"impacsaas","tenant_name":"ACME지점","billing_type":"POSTPAID","status":"ACTIVE","credit_limit":1000000}}

에러: 400(접미사 형식/길이, 또는 최종 tenant_id 가 플랫폼 예약 계정 ID 와 같아 사용할 수 없는 경우), 409(중복).

GET /api/admin/tenants/{tenantId}

하위 가맹점 상세. 자기 하위가 아니면 403, 없으면 404.

GET /api/admin/tenants

내 하위 가맹점 목록(parent_tenant_id == 소유자).

{"success":true,"data":{"owner":"impacsaas","count":2,"list":[{"tenant_id":"impacsaas_1234","status":"ACTIVE","billing_type":"POSTPAID","point_balance":50000}]}}
PATCH /api/admin/tenants/{tenantId}

신용한도는 0~100억 원으로 제한합니다. 선불은 한도 0이며, 선불→후불 전환 시 한도를 생략하면 0입니다. billing_type·credit_limit을 모두 생략한 수정은 기존 과금 설정을 유지합니다.

키 원본은 발급 응답에서 한 번만 제공합니다. 목록의 api_key는 호환성을 위해 null이며, key_seq로 관리합니다. 기존 키 인증은 유지됩니다.

하위 가맹점 수정(제공한 필드만 변경). 필드: tenant_name, callback_url, billing_type(PREPAID|POSTPAID), status(ACTIVE|INACTIVE|SUSPENDED), credit_limit.

curl -X PATCH https://imsend.kr/api/admin/tenants/impacsaas_1234 \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"credit_limit":2000000,"status":"SUSPENDED"}'
DELETE /api/admin/tenants/{tenantId}

하위 가맹점 비활성(soft delete) — status='INACTIVE'로만 전환(완전 삭제 아님). 비활성 가맹점의 키는 인증 시 자동 차단. 재활성은 PATCH status=ACTIVE.

{"success":true,"data":{"tenantId":"impacsaas_1234","status":"INACTIVE","note":"비활성 처리(soft delete). 완전 삭제는 웹 관리자에서 수행하세요. 재활성은 PATCH status=ACTIVE."}}
완전 삭제는 웹 관리자에서만 가능하며 2단계입니다: ① 가맹점 상세 → 비활성화(→INACTIVE) → ② 영구 삭제(3중 가드: INACTIVE 상태·tenant_id 입력 일치·하위 없음). 비활성 상태에서 활성화 버튼으로 재활성(→ACTIVE). 상세는 아래 웹 관리자 가맹점 수명주기 참조.

② 하위 API 키 발급/조회/폐기 슈퍼관리자 키

하위 가맹점이 /api/v1/**(발송·조회·충전 세션 등)를 호출할 때 쓰는 일반 가맹점 키(role='TENANT')를 관리합니다. 슈퍼관리자 키는 여기서 만들 수 없습니다(웹 관리자 전용). 세 API 모두 공통 규칙(401·404·403)을 따릅니다.

POST /api/admin/tenants/{tenantId}/api-keys

본문(선택) key_name — 비우면 "API 키". 키는 ims_ + 32자이며 이 응답에서 한 번만 원문을 줍니다(서버는 해시만 저장, 다시 조회 불가). 분실하면 새로 발급하고 옛 키를 폐기하세요.

curl -X POST https://imsend.kr/api/admin/tenants/impacsaas_1234/api-keys \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"key_name":"ACME 운영"}'
{"success":true,"data":{"tenant_id":"impacsaas_1234","tenant_seq":42,"api_key":"ims_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","key_name":"ACME 운영"}}
GET /api/admin/tenants/{tenantId}/api-keys

키 목록(최신 발급순). api_key 는 호환용으로 항상 null 이고 key_seq 로 관리합니다. status: Y(사용 중) / REVOKED(폐기).

{"success":true,"data":{"tenant_id":"impacsaas_1234","list":[{"key_seq":501,"tenant_seq":42,"api_key":null,"key_name":"ACME 운영","role":"TENANT","status":"Y","reg_date":"(등록 시각)"}]}}
DELETE /api/admin/tenants/{tenantId}/api-keys/{keySeq}

키 폐기(status='REVOKED'). 폐기한 키는 다음 요청부터 401 입니다. 그 가맹점의 키가 아니면 404.

{"success":true,"data":{"key_seq":501,"status":"REVOKED"}}

⑤ 연동 표준 — 보장·키 교체·공유 지갑 슈퍼관리자 키 2026-09-24

하위 사이트(기관·캠프·지점)가 생길 때마다 재시도해도 안전하게 가맹점과 키를 붙이는 API 입니다. 응답을 잃고 다시 불러도 가맹점이 두 번 생기거나 키가 쌓이지 않습니다. 오류는 code 로 분기하세요. OpenAPI 계약·공용 SDK(imsend-sdk)가 이 흐름을 감쌉니다.

PUT /api/admin/tenants/{접미사 또는 전체 ID}

없으면 만들고(201, created:true) 있으면 아무것도 바꾸지 않고 그대로 돌려줍니다(200, created:false). 본문 tenant_name, wallet_mode(SELF 하위가 직접 충전 / PARENT 상위 지갑에서 차감), usage_limit_monthly(PARENT 만, 원). 이미 있는데 wallet_mode 가 다르면 409 WALLET_MODE_MISMATCH(변경은 PATCH), 다른 소유자의 ID 면 409 TENANT_ID_TAKEN.

curl -X PUT https://imsend.kr/api/admin/tenants/site7 \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"tenant_name":"7번 기관","wallet_mode":"PARENT","usage_limit_monthly":300000}'
{"success":true,"created":true,"data":{"tenant_id":"impacsaas_site7","wallet_mode":"PARENT","usage_limit_monthly":300000,"status":"ACTIVE","billing_type":"PREPAID"}}
POST /api/admin/tenants/{tenantId}/api-keys/rotate

본문 key_name(필수, 서비스마다 고정 이름). 같은 이름의 사용 중 키를 폐기하고 새 키를 발급합니다(한 트랜잭션, 원문은 이 응답에서만). 응답을 잃었으면 다시 부르면 됩니다 — 이름당 사용 중 키는 항상 하나입니다. 키는 서버 쪽에 암호화해 보관하세요(평문 DB 저장 금지).

{"success":true,"data":{"tenant_id":"impacsaas_site7","api_key":"ims_XXXXXXXX...","key_seq":812,"key_name":"imvenue","revoked":[790]}}

공유 지갑(연동 타입 D)

wallet_mode=PARENT 하위는 자기 잔액이 없습니다. 발송 판정·차감·실패 환불은 상위 지갑에서 이루어지고, 원장에는 실제로 쓴 하위가 consumer_id 로 남습니다. 하위 월 한도를 넘으면 발송이 거절됩니다(이번 달 사용 한도 초과, 판정은 접수 시점이라 근사치). 상위가 비활성이면 하위 발송도 막힙니다(하위 잔액으로 대신 차감하지 않음). PARENT 하위는 충전 세션·충전 화면·포털 충전·포인트 적립이 모두 거절됩니다(WALLET_PARENT_MANAGED) — 충전은 상위에서 하세요. 하위 키의 /api/v1/sms/balance 는 walletMode:"PARENT" 와 상위 지갑 기준 available 을 돌려줍니다.

GET /api/admin/wallet

상위 잔액·가용액과 이번 달 하위별 사용액(children[].used = 사용 − 환불).

GET /api/admin/tenants/{tenantId}/usage?month=YYYY-MM

하위 한 곳의 월 사용액·건수·한도.

PATCH /api/admin/tenants/{tenantId} — wallet_mode·usage_limit_monthly

지갑 전환은 하위에 처리 중인 발송(결과 대기·예약·API 큐·이메일 배치)이 없을 때만 됩니다(409 IN_FLIGHT) — 이미 나간 발송의 과금·환불이 전환 전후 다른 지갑에 기록되지 않게 하기 위해서입니다. SELF→PARENT 는 하위 잔액이 0 이어야 합니다(409 BALANCE_NOT_ZERO). PARENT→SELF 는 잔액 0 에서 시작합니다.

리셀러 point/record 의 CHARGE 는 PARENT 하위에 거절됩니다(400 WALLET_PARENT_MANAGED).

②-b 하위 발신번호 신청 대행 / 조회 슈퍼관리자 키

하위 가맹점 명의의 발신번호 등록을 대신 신청하고 심사 상태를 봅니다. 승인·반려는 imsend 관리자만 합니다(발신번호 심사는 통신사 컴플라이언스 책임이라 리셀러에게 위임하지 않음). 승인(APPROVED)된 번호만 발송에 쓰입니다.

POST /api/admin/tenants/{tenantId}/sender  (multipart/form-data)
필드필수설명
phoneNumber✅발신번호. 숫자만 남겨 저장합니다. 9자리 미만이거나 발신번호 규격(고시 제2022-7호 제3조)에 맞지 않으면 400
applicantName신청자명
managerName담당자명
managerPhone담당자 연락처
certFile통신서비스 이용증명원(파일)
bizFile사업자등록증(파일)

같은 가맹점에 반려되지 않은 같은 번호가 있으면 409. 대행 신청은 명의자 본인확인을 거치지 않았으므로 verified: false(미검증)로 접수되고, 심사자가 서류로 판단합니다 — 서류를 함께 올리세요.

curl -X POST https://imsend.kr/api/admin/tenants/impacsaas_1234/sender \
  -H "X-API-Key: ims_SUPERKEY..." \
  -F "phoneNumber=0330000000" -F "applicantName=ACME지점" \
  -F "certFile=@cert.pdf" -F "bizFile=@biz.pdf"
{"success":true,"data":{"tenantId":"impacsaas_1234","phoneNumber":"0330000000","status":"PENDING","verified":false,"message":"신청이 접수되었습니다. imsend 관리자의 서류 심사 후 승인됩니다."}}
GET /api/admin/tenants/{tenantId}/sender?status=

하위 가맹점 발신번호 목록(신청 최신순). status(선택) 로 거르며 비우거나 ALL 이면 전체입니다. 상태: PENDING(심사 중) / APPROVED(승인) / REJECTED(반려). admin_memo 는 심사자 메모.

{"success":true,"data":{"tenantId":"impacsaas_1234","count":1,"senders":[{"id":77,"phone_number":"0330000000","status":"PENDING","is_default":false,"is_verified":false,"admin_memo":null,"applicant_name":"ACME지점","manager_name":null,"manager_phone":null,"applied_at":"(신청 시각)","reviewed_at":null}]}}

③ 하위 발송 슈퍼관리자 키, 차감 제어

POST /api/admin/send

자기 하위 가맹점 명의로 문자(SMS/LMS/MMS)·이메일·알림톡을 발송합니다. 채널은 channel 로 고릅니다(2026-09-23 문서 정정 — 이전 문서는 문자만 된다고 잘못 적었습니다).

공통 필드

필드필수설명
tenant_id✅발송 주체(자기 하위여야 함 — 아니면 403, 없으면 404)
channelSMS(기본) / EMAIL(E) / ALIMTALK(A·KKO). 생략하면 msg_type 값으로 판정하고, 그것도 없으면 SMS
receivers✅수신자 배열(또는 단건 receiver). 문자·알림톡은 전화번호(숫자 10자리 미만은 실패로 집계), 이메일은 주소
chargetrue(기본) / false(무료). false 는 imsend 가 허용한 리셀러(설정 admin.no_charge.allowed_owners)에게만 적용되고, 그 밖에는 조용히 과금(true)으로 처리됩니다 — 응답의 charge 로 실제 적용 값을 확인하세요

과금(charge=true)이면 발송 전에 하위 가맹점 잔액(후불은 신용한도 포함)을 확인해 부족하면 402. charge=false 가 적용되면 im_sms_log.no_charge=TRUE 로 남아 결과 수신 시 차감·큐 실패 환불을 모두 건너뜁니다.

채널별 필드·과금

채널필수 필드선택 필드과금 시점·단가응답 data
SMS
(문자)
message(또는 text)msg_type SMS/LMS/MMS(S/L/M, 생략 시 EUC-KR 90바이트 초과면 LMS). 그 밖의 값은 400. sender(또는 callback) 발신번호, adType(ad), is_resend대기열 적재 후 수신 확인(DELIVERED) 시 차감. 단가는 하위 가맹점 적용 단가batchId, tenantId, charge, totalCount, queuedCount, failCount, optoutFiltered, unitPrice, estimatedCost
EMAILsubject, content(또는 message)from(발신 주소 — 하위·상위 가맹점에 인증된 주소), from_name(fromName), adType즉시 발송하고 성공 건수 × 이메일 단가를 발송 직후 원장에 기록(선불 USE·후불 CREDIT_USED)batchId, tenantId, channel, charge, totalCount, successCount, failCount, optoutFiltered, unitPrice, totalCost (+failures[], requestId, senderVerified·senderHint). 전원 실패면 success:false(HTTP 200) + data
ALIMTALKtemplate_code(또는 templateCode) — 승인된 템플릿variables(객체, 값이 null 이면 400)대기열 적재 후 결과 수신 시 차감. 알림톡 단가, 실패 시 문자 대체(failover)가 켜져 있으면 대체 문자(LMS) 단가까지 나갈 수 있음 — 잔액 확인도 둘 중 큰 단가로 함batchId, tenantId, channel, templateCode, charge, totalCount, queuedCount, failCount, unitPrice, maxChargeIfFailover (+카카오 설정을 상위에서 물려받으면 kakaoInheritedFromTenantSeq)
  • 알림톡: 하위 가맹점 → 상위 가맹점 순으로 승인된 카카오 설정(발신프로필)을 찾습니다. 없거나 비활성이면 409. 상위 설정을 쓰면 템플릿도 상위가 공유한 것만 쓸 수 있습니다(아니면 400). 발신번호(대체 문자용)는 하위 가맹점 기준으로 정합니다.
  • 이메일: 서비스가 꺼져 있으면 503. 금칙어는 제목+본문 기준 400.
# 문자
curl -X POST https://imsend.kr/api/admin/send \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"tenant_id":"impacsaas_1234","receivers":["01000001111"],"message":"테스트"}'

# 이메일
curl -X POST https://imsend.kr/api/admin/send \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"tenant_id":"impacsaas_1234","channel":"EMAIL","receivers":["user@example.com"],"subject":"안내","content":"<p>본문</p>"}'

# 알림톡
curl -X POST https://imsend.kr/api/admin/send \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"tenant_id":"impacsaas_1234","channel":"ALIMTALK","receivers":["01000001111"],"template_code":"TPL_001","variables":{"name":"홍길동"}}'

발송 정책 (채널별)

정책SMSEMAILALIMTALK
광고 판정adType=ad 또는 본문 (광고) 시작adType=ad 또는 제목·본문 (광고) 시작—(정보성 템플릿)
광고 야간(21~08시) 차단403400(요청 시점). 발송 도중 21시가 되면 남은 광고 건은 보내지 않고 failures[] 로 돌려줍니다—
수신거부 제외(광고일 때)하위 가맹점 본인·전체(ALL·SYSTEM) 차단 + 발신자 미확인 080 거부. 조회 장애 시 503 보류(미적재·미과금)이메일 수신거부(하위 가맹점·전체). 조회 장애 시 503 보류(미발송·미과금)—
금칙어본문 400제목+본문 400—
발신 주체하위 승인번호(기본번호 우선) → 상위 승인번호 → 시스템 대표번호. 미승인 번호를 지정해도 거부하지 않고 이 순서로 대체해 발송합니다(응답에 대체 여부·실제 번호가 실리지 않습니다 — 원하는 번호로 보내려면 먼저 ②-b 로 승인받으세요)from 생략 시 기본 발신 주소하위 → 상위 카카오 설정
⚠️ 컴플라이언스(전사 §2.5)는 charge 여부와 무관하게 적용됩니다(발신번호 사전등록·광고 야간발송 금지·수신거부).

리셀러 대행 발송에는 중복 방지 키(clientRequestId)가 없습니다 — 응답을 못 받았을 때 재호출하면 중복 발송될 수 있으니, 재시도 전 /api/admin/tenants/{id}/sms/history 로 확인하세요.

발신사업자 식별코드(KISA 고시로 연 1회 변경)는 imsend가 서버에서 관리하는 값입니다. 요청 필드·응답 형식·재시도 정책은 바뀌지 않으며, 연동 측에서 조치할 사항은 없습니다.

④ 하위 포인트 충전/차감 기록·잔액 슈퍼관리자 키

POST /api/admin/tenants/{tenantId}/point/record

자기 하위 가맹점 원장에 충전/차감 기록.

CHARGE 는 청구 없는 무상 적립이라 자사 상위 계정만 쓸 수 있습니다 — 그 밖의 계정은 403 FREE_CHARGE_NOT_ALLOWED. 하위 가맹점 사용자의 실제 충전은 충전 세션(POST /api/v1/charge/session → 카드 결제)으로 받으세요.

필드필수설명
tx_type✅CHARGE / USE
amount✅양수 정수(원)
item항목(description)
ref_key권장거래 ID(멱등키)
curl -X POST https://imsend.kr/api/admin/tenants/impacsaas_1234/point/record \
  -H "X-API-Key: ims_SUPERKEY..." -H "Content-Type: application/json" \
  -d '{"tx_type":"CHARGE","amount":50000,"item":"자체플랫폼 충전","ref_key":"ord-001"}'
  • 멱등: 동일 ref_key 재요청 → 기존 결과(idempotent:true). USE 로 잔액이 음수가 되면 선불·후불 구분 없이 402(code 없음, 기록 안 함).
  • 새로 기록된 CHARGE 는 하위 가맹점 주체로 웹훅 charge.completed(source:"ADMIN_API")를 발행합니다 — 하위 구독과, include_children 인 상위 구독이 받습니다. 멱등 재요청·USE 는 발행하지 않습니다.
GET /api/admin/tenants/{tenantId}/point/balance

하위 가맹점 잔액·가용·단가.

GET /api/admin/tenants/{tenantId}/point/history?page=1&size=50

하위 가맹점 포인트 이력(원장, 최신순 페이징). 응답: {page,size,total,list:[{ledger_seq,tx_type,amount,balance_after,description,ref_key,reg_date}]}. size 최대 200.

GET /api/admin/tenants/{tenantId}/sms/history?page=1&size=50

하위 가맹점 발송 이력(최신순 페이징). 수신번호는 마스킹되어 반환(전사 §2.3). 응답 항목: log_seq, phone(마스킹), msg_type, status, unit_price, no_charge, batch_id, reg_date.

이메일 대행 발송 본문은 2026-09-14부터 전체가 보존되어 관리자 발송이력 상세에서 원문 그대로 보입니다(이전: 앞 1,000자). 위 API 응답 항목은 바뀌지 않습니다.

웹 관리자 가맹점 수명주기 (비활성 → 재활성 → 영구삭제)

REST 삭제(DELETE /api/admin/tenants/{id})는 비활성(soft)까지만 수행합니다. 재활성·영구삭제는 웹 관리자 전용입니다. 구현: TenantMgmtController. 가맹점 상세(/imSiteCtrl/tenant/detail?tenantId=...) 하단 버튼.

동작엔드포인트(웹)파라미터결과가드
비활성화POST /imSiteCtrl/tenant/deactivatetenantId, forcestatus='INACTIVE' → 발송·API 인증 즉시 차단잔액/미정산(±point_balance) 있으면 needConfirm 반환 → 2차 경고 후 force=true 재요청 시 진행
활성화POST /imSiteCtrl/tenant/activatetenantIdstatus='ACTIVE' 복원(잔액·키 미변경)비활성 상세에 활성화 버튼 노출
영구 삭제POST /imSiteCtrl/tenant/deletetenantId, confirmIdim_api_key DELETED + im_tenant DELETE(되돌릴 수 없음)3중: ①INACTIVE 상태 ②confirmId=tenant_id 일치 ③하위 가맹점 없음
차단 원리 — 모든 API 인증 쿼리가 t.status='ACTIVE' AND k.status='Y' 를 요구 → INACTIVE 전환만으로 발송·인증이 막힙니다(키 개별 폐기 불필요). 재활성 시 자동 복구.
  • 버튼: 활성 상태 → 비활성화(주황) / 비활성 상태 → 활성화(초록) + 영구 삭제(빨강).
  • 응답은 {success, [needConfirm], [balance], [status], [message]}. 세션 만료 시 액션은 X-Requested-With AJAX 분기로 명확한 오류를 반환.

가맹점 자기 포인트 API (가맹점 키) — 참고

가맹점이 자기 키로 자기 포인트를 직접 기록·조회(리셀러 경유 아님):

  • POST /api/v1/point/record (tx_type, amount, item, ref_key)
  • GET /api/v1/point/balance
  • 기존 GET /api/v1/sms/balance, GET /api/v1/sms/point-history 도 동일 키로 사용 가능.

스키마 변경 (Flyway)

마이그레이션변경
V6_35__api_key_admin_role.sqlim_api_key.role(기본 'TENANT')
V6_36__sms_log_no_charge.sqlim_sms_log.no_charge(기본 FALSE)
V6_37__tenant_parent.sqlim_tenant.parent_tenant_id (리셀러 소유 링크, 최상위는 NULL)
V6_38__api_key_remark.sqlim_api_key.remark (비고)

모두 기본값이 기존 동작과 동일(가산적). 앱 기동 시 자동 적용.

슈퍼관리자 API 기능 요약

기능메서드경로
하위 생성POST/api/admin/tenants
하위 목록GET/api/admin/tenants
하위 상세GET/api/admin/tenants/{id}
하위 수정PATCH/api/admin/tenants/{id}
하위 비활성(soft delete)DELETE/api/admin/tenants/{id}
키 발급/목록/폐기POST/GET/DELETE/api/admin/tenants/{id}/api-keys[/{keySeq}]
발신번호 신청 대행/조회POST(multipart)/GET/api/admin/tenants/{id}/sender
발송(문자·이메일·알림톡, 차감/비차감)POST/api/admin/send
포인트 충전/차감POST/api/admin/tenants/{id}/point/record
잔액 조회GET/api/admin/tenants/{id}/point/balance
포인트 이력GET/api/admin/tenants/{id}/point/history
발송 이력GET/api/admin/tenants/{id}/sms/history
비활성화 / 활성화 / 영구 삭제POST웹 전용 /imSiteCtrl/tenant/{deactivate,activate,delete}

알려진 제약 / 주의

  • 슈퍼관리자 키는 자기 하위만 — 자기 상위계정 본인 발송·포인트는 별도 가맹점 키 사용.
  • API 삭제는 비활성(soft delete); 활성화·영구삭제는 웹 관리자에서만(영구삭제는 INACTIVE 상태+tenant_id 확인+하위 없음 3중 가드).
  • ③ 발송은 문자(SMS/LMS/MMS 텍스트)·이메일·알림톡(channel). MMS 이미지·예약 발송은 없습니다(하위 가맹점 키로 /api/v1/sms/*). charge=false 는 허용된 리셀러에게만 적용.
  • 접미사는 영숫자·하이픈 1~20자, 최종 tenant_id ≤ 36자.
  • 실 PostgreSQL 검증 필수(모킹 미검출 타입오류 존재) — 배포 후 스모크 curl 권장.
  • 2026-09-24 내부: 원장 쓰기 직렬화(LedgerWriter) — 요청·응답 변경 없음.

구현: AdminApiController, TenantPointApiController, TenantMgmtController, SmsResultScheduler, QueueProcessorScheduler. 원문: docs/api/admin-tenant-api.md