SaaS API제출 링크 (매직 링크)
POST /ai/submit-link/{token}/complete/review
1·2차 검수 제출 완료 콜백 (무인증)
1·2차 검수 제출 완료 콜백
GCS 직접 업로드가 끝난 뒤 호출하여 1차·2차 검수 제출을 처리합니다. 내부적으로 기존 일괄 제출 로직을 그대로 호출하므로, 상태변경·피드백 해결·알림이 인앱 제출과 동일하게 발생합니다.
처리 순서
- 토큰 검증 +
reviewId가 토큰의 신청에 속하는지 확인 (다른 신청 제출 방지) - 비디오 항목은 트랜스코딩 트리거 (인앱 업로드와 동일한 호출)
- 기존 일괄 제출(batchSubmit) 호출 → 저장 · 제출 · 피드백 해결 · 알림
- 제출 성공 → 토큰 1회용 소진 (재제출/2차는 새 링크 필요)
HTTP 요청
POST /ai/submit-link/{token}/complete/review
Content-Type: application/jsonPath Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
token | string | 예 | 제출 링크 토큰 |
Request Body
{
"reviewId": 50,
"items": [
{
"itemId": 1,
"itemType": "VIDEO",
"filePath": "https://storage.googleapis.com/glowb-input/campaign/content/link/100/...mp4",
"comment": null
},
{
"itemId": 3,
"itemType": "CAPTION",
"editorState": "{\"root\":{\"children\":[]}}"
}
]
}| 필드명 | 타입 | 필수 | 설명 |
|---|---|---|---|
reviewId | long | 예 | 제출할 검수 라운드 ID (컨텍스트에서 조회) |
items | array | 예 | 제출물 목록 (기존 일괄 제출과 동일 구조) |
items[].itemId | long | 조건부 | 제출물 ID (기존 항목 수정 시 필수) |
items[].itemType | string | 조건부 | 제출물 타입 (신규 시 필수) |
items[].scriptIndex | integer | 아니오 | 장면 번호(0부터). SCRIPT_VIDEO/SCRIPT_BLOG 다중 장면용. 생략 시 서버가 자동 부여(첫 항목=0). 그 외 타입은 무시 |
items[].filePath | string | 조건부 | GCS URL (publicUrl) — VIDEO/PHOTO 필수 |
items[].editorState | string | 아니오 | 에디터 상태 JSON을 문자열로 직렬화(stringify) 한 값 (SCRIPT_VIDEO/SCRIPT_BLOG/CAPTION/HASHTAG). ⚠️ 객체 {}로 보내면 JSON parse error로 실패 — 반드시 문자열 |
items[].comment | string | 아니오 | 코멘트 |
다중 장면 스크립트 (Multi-Scene)
SCRIPT_VIDEO는 장면별로 여러 항목을 보낼 수 있습니다. 각 장면을 scriptIndex로 구분하면 서버가 장면별 레코드로 저장합니다(같은 reviewId·같은 scriptIndex로 재제출하면 해당 장면을 덮어쓰기=upsert). 단일 장면이면 scriptIndex를 생략해도 됩니다(자동 0).
SCRIPT_VIDEO 항목의 editorState는 장면 구성을 위해 videoScene/subtitle/narration 3개 필드를 담은 JSON을 문자열로 직렬화해 보냅니다(각 필드 선택, 기존 단순 Lexical {"root":...} 형식도 그대로 호환).
{
"reviewId": 50,
"items": [
{
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 0,
"editorState": "{\"videoScene\":{\"root\":{...}},\"subtitle\":{\"root\":{...}},\"narration\":{\"root\":{...}}}"
},
{
"itemType": "SCRIPT_VIDEO",
"scriptIndex": 1,
"editorState": "{\"videoScene\":{\"root\":{...}},\"subtitle\":{\"root\":{...}},\"narration\":{\"root\":{...}}}"
}
]
}editorState는 항상 stringify된 문자열입니다. 위 예시의 videoScene/subtitle/narration 객체도 통째로 JSON.stringify(...) 한 문자열로 보내야 합니다(객체째 전송 시 파싱 실패). 백엔드는 editorState를 그대로 저장하고 v4 AI 스크립트 검수에 장면별로 전달합니다.
응답
성공 응답 (200 OK) — 기존 일괄 제출과 동일한 BatchResponse
{
"status": 200,
"code": null,
"message": "제출 완료",
"data": {
"reviewId": 50,
"items": [ { "id": 1, "itemType": "VIDEO", "isSubmitted": true } ],
"resolvedFeedbackCount": 2
}
}에러 응답
| 상황 | 상태 코드 | 메시지 |
|---|---|---|
| 토큰 만료/사용됨 | 400 | 만료되었거나 사용할 수 없는 링크입니다. |
| 검수 라운드 없음 | 400 | 검수 라운드를 찾을 수 없습니다. |
| 다른 신청의 라운드 | 400 | 이 링크로 제출할 수 없는 검수 라운드입니다. |
| VIDEO/PHOTO 파일 없음 | 400 | 영상/사진 파일을 업로드해주세요. |