POST /ai/business/contents/submission-reminder
콘텐츠 제출 독촉 이메일 발송
콘텐츠 제출 독촉 이메일 발송
광고주가 진행표에서 콘텐츠 제출이 지연된 크리에이터에게 독촉 이메일을 발송합니다.
동작 구조
- 메일 렌더링과 실제 발송은 node-server가 담당합니다 (React Email 템플릿, 다국어 문구, Resend)
- 발송 이력과 횟수 제한도 node-server가 관리합니다 (
TB_REMINDER_EMAIL_LOG) - 이 API는 인증된 진입점 역할을 하고 node-server로 위임합니다
- 발송자는 요청 본문이 아니라 인증 토큰에서 확인합니다
HTTP 요청
POST /ai/business/contents/submission-reminder
Authorization: Bearer {access_token}
Content-Type: application/json권한
| 권한 | 접근 |
|---|---|
ROLE_BUSINESS | 가능 |
ROLE_AGENCY | 가능 (에이전시 본인은 ROLE_BUSINESS도 함께 부여됨) |
ROLE_AGENCY_CLIENT | 가능 (고객사 화이트리스트 Bucket 1에 등록) |
ROLE_ADMIN | 가능 |
Request Body
{
"applicationId": 19630,
"campaignId": "1584",
"campaignName": "테스트 캠페인",
"email": "creator@example.com",
"creatorName": "홍길동",
"delayDays": 3,
"reminderPhase": "DRAFT_SUBMISSION",
"dashboardUrl": "https://biz.glowb.io/ko/corp/dashboard/1584"
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
applicationId | long | 예 | 캠페인 신청 ID |
campaignId | string | 예 | 캠페인 번호 |
campaignName | string | 아니오 | 비어 있으면 서버가 캠페인에서 채웁니다 |
email | string | 예 | 수신 크리에이터 이메일 |
creatorName | string | 아니오 | 크리에이터 이름 |
delayDays | int | 아니오 | 지연 일수. 메일 본문에 노출되며 기본값은 1입니다 |
reminderPhase | string | 예 | 독촉 단계 (아래 표 참고) |
dashboardUrl | string | 아니오 | 메일 내 대시보드 바로가기 URL |
memberId는 받지 않습니다. 발송자는 인증 토큰의 로그인 ID를 사용합니다.
독촉 단계 (reminderPhase)
| 값 | 설명 |
|---|---|
DRAFT_SUBMISSION | 초안/스크립트 제출 단계 |
VIDEO_PRODUCTION | 제작물 제출 단계 |
FINAL_SUBMISSION | 최종 제출물 단계 |
발송 횟수 제한은 단계별로 따로 계산됩니다.
독촉 대상 판정 — 호출 전에 확인할 것
이 API는 호출되면 보냅니다. 마감이 지났는지, 크리에이터에게 제출 의무가 시작됐는지는 검사하지 않습니다. 누구에게 보낼지는 화면에서 판정해야 하고, 그 판정에 필요한 값은 이미 다른 API가 내려주고 있습니다.
반드시 트리거 확정 여부를 먼저 보세요
제출 마감일은 선정 시점에 임시(placeholder) 값으로 먼저 깔립니다. 배송완료·계약 서명완료 같은
트리거가 발동해야 실제 일자로 확정됩니다(ScheduleTriggerType 참고).
날짜만 보고 판정하면, 아직 제출 의무가 시작되지도 않은 크리에이터가 "지연"으로 잡힙니다. 임시 마감은 이미 지나 있는 경우가 대부분이라 실제로 그렇게 됩니다.
| 확인 위치 | 필드 | 호출 횟수 |
|---|---|---|
진행표 GET /ai/progress-table/item/{collabId} | items[].submissionTimeline.script.deadlines.submissionDeadlineConfirmed(2차 검수면 .content) | 캠페인당 1회 |
검수 상세 GET /ai/business/contents/review/{reviewId}/detail | hasTriggered | 검수 라운드당 1회 |
두 값은 같은 사실을 가리킵니다. 같은 TB_APPLICATION_SCHEDULE row의 trigger_type이 채워졌는지를 읽으며,
1차 검수는 SCRIPT_SUBMISSION_DEADLINE, 2차 검수는 CONTENT_SUBMISSION_DEADLINE을 봅니다.
이름만 다르니 편한 쪽을 쓰면 됩니다.
여러 명을 한 화면에서 판정한다면 진행표 쪽을 쓰세요
진행표는 캠페인당 1회 호출로 모든 크리에이터의 값을 함께 내려줍니다. 검수 상세는 행마다 호출해야 하므로, 목록 화면에서 같은 목적으로 쓰면 호출 수가 인원수만큼 늘어납니다.
캠페인 일정(TB_CAMPAIGN_SCHEDULE)을 지연 판정에 쓰지 마세요
캠페인 일정은 캠페인 전체의 계획값이라 크리에이터 개인의 마감과 다릅니다.
특히 DRAFT_SUBMISSION 단계는 이름과 달리 실제 의미가 제품 배송이고,
VIDEO_PRODUCTION은 회사측 검수 구간입니다. 개인 마감의 대체값으로 쓰면 안 됩니다.
이미 제출했거나 검수 중이면 제외
최종 제출이 끝났거나 최신 검수가 REVIEWING이면 독촉 대상이 아닙니다.
이 API는 그 상태도 검사하지 않으므로 화면에서 걸러야 합니다.
응답
성공 응답 (200 OK)
{
"status": 200,
"code": null,
"message": "독촉 이메일이 발송되었습니다.",
"data": {
"success": true,
"error": null,
"sentCount": 1,
"maxCount": 3
}
}발송이 거부된 경우 (200 OK)
발송 거부도 200으로 내려오며 success: false와 사유 코드가 담깁니다.
{
"status": 200,
"code": null,
"message": "독촉 이메일이 발송되지 않았습니다.",
"data": {
"success": false,
"error": "ALREADY_SENT_TODAY",
"sentCount": null,
"maxCount": null
}
}Response 스키마
| 필드 | 타입 | 설명 |
|---|---|---|
success | boolean | 발송 성공 여부 |
error | string | 실패 사유 코드. 성공 시 null |
sentCount | int | 해당 단계에서 지금까지 발송된 횟수 (nullable) |
maxCount | int | 단계별 발송 한도 (nullable) |
실패 사유 코드
| 코드 | 설명 |
|---|---|
ALREADY_SENT_TODAY | 오늘 이미 같은 단계로 발송했습니다 (KST 기준 하루 1회) |
MAX_REMINDERS_REACHED | 해당 단계의 발송 한도(3회)를 모두 사용했습니다 |
INVALID_REQUEST | 필수 필드가 누락되었습니다 |
NODE_API_NOT_CONFIGURED | 서버에 node-server 주소가 설정되지 않았습니다 |
SEND_FAILED | node-server 호출에 실패했습니다 |
발송 이력이 node-server에 단일 기록되므로, 이 API로 보낸 건과 기존 경로로 보낸 건의 횟수가 합산되어 계산됩니다. 한도가 이중으로 집계되지 않습니다.
발송 횟수 조회
POST /ai/business/contents/submission-reminder/counts
Authorization: Bearer {access_token}
Content-Type: application/json{ "applicationIds": [19630, 19585] }응답 (200 OK)
{
"status": 200,
"code": null,
"message": "독촉 발송 횟수 조회가 완료되었습니다.",
"data": [
{
"applicationId": 19630,
"reminderPhase": "DRAFT_SUBMISSION",
"count": 1,
"sentToday": true
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
applicationId | long | 신청 번호 |
reminderPhase | string | 독촉 단계 |
count | int | 해당 단계에서 지금까지 발송된 횟수 |
sentToday | boolean | 오늘(KST) 발송 여부 |
발송 이력이 없는 신청은 결과에 포함되지 않습니다. 조회에 실패해도 빈 배열을 돌려주며 오류를 던지지 않습니다.
횟수 조회는 반드시 이 API로 하세요
발송과 조회가 서로 다른 경로를 타면 다른 환경의 이력을 읽게 됩니다. 실제로 발송은 test node-server에 기록되고 조회는 prod node-server를 읽어, 독촉을 보냈는데도 화면에 횟수와 "오늘 발송함" 표기가 뜨지 않는 일이 있었습니다.
이 API는 발송과 같은 진입점을 쓰므로 항상 같은 곳을 봅니다.