선정 (계약서 작성 건너뛰기)
지정한 크리에이터를 제안·크레딧 잠금·계약 체결까지 한 번에 끝내고 제출 단계로 세웁니다.
선정 (계약서 작성 건너뛰기)
지정한 크리에이터를 제안 → 크레딧 잠금 → 계약 체결까지 한 번에 처리합니다. 계약서 열람·정보입력·OTP 인증·서명은 통째로 건너뛰고 서명 완료 상태를 직접 만듭니다. 크리에이터마다 검수 상태(10가지)를 지정하면 선정 직후 그 상태까지 밀어 넣습니다.
OTP 인증은 실제 문자가 발송되고 과금됩니다. 반복 테스트에서 이 경로를 태울 수 없어 상태를 직접 만드는 방식을 택했습니다. 대신 크리에이터가 입력했어야 할 계약 내용과 서류, 서명 PDF는 더미로 채웁니다.
상태를 지정하지 않은 크리에이터는 호출이 끝나면 곧바로 아래 단계에 서 있습니다. 상태를 지정했다면 검수 상태 지정을 보세요.
| 캠페인 | 다음 단계 |
|---|---|
2회검수 (doubleReview = true) | 스크립트 제출 |
1회검수 (doubleReview = false) | 영상 제출 |
HTTP 요청
POST /ai/test-setup/campaign/{collabNo}/select
Content-Type: application/jsonPath Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
collabNo | Integer | 예 | 캠페인 번호 |
Request Body
{
"creators": [
{ "influenceNo": 2436, "state": "SCRIPT_ADMIN_WAITING" },
{ "influenceNo": 6289, "state": "VIDEO_REVISION_WAITING" },
{ "influenceNo": 6281 }
],
"unitPrice": 100000,
"skipDelivery": false,
"enableAdminGate": true,
"paidScope": "PARTIAL"
}상태 지정 없이 선정만 할 때는 기존처럼 influenceNos 를 써도 됩니다: { "influenceNos": [6282, 6281] }
| 필드 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
creators | Array | 둘 중 하나 | — | 선정할 크리에이터. influenceNo(필수) + state(선택, 아래 8가지). state 를 비우면 선정만 합니다 |
influenceNos | Array<Integer> | 둘 중 하나 | — | 상태 지정 없이 선정만 할 인플루언서 번호. creators 와 함께 쓸 수 없습니다 |
unitPrice | Long | 아니오 | 100000 | 협업 단가. 노출가·제안가로 함께 들어가고 크레딧 잠금 금액의 기준이 됩니다 |
skipDelivery | Boolean | 아니오 | false | true 면 배송 완료 처리를 건너뜁니다 |
enableAdminGate | Boolean | 아니오 | true | 상태를 지정했을 때만 씁니다. 관리자 검수를 거치는 상태에 필요한 캠페인 게이트(스크립트/영상)가 꺼져 있으면 켭니다. false 면 게이트를 건드리지 않고 그 크리에이터를 실패로 돌립니다 |
paidScope | String | 아니오 | PARTIAL | 상태를 지정했을 때 넣는 유료 피드백의 검수 범위 (PARTIAL / FULL) |
처리 순서
크리에이터 한 명당 아래를 순서대로 수행합니다.
- 신청 생성 — 이미 있으면 재사용. 없으면
SELF_APPLIED로 새로 만듭니다. 크리에이터 SNS 링크를 같은 SNS 타입의 과거 신청에서 찾아 넣고, 없으면 프리셋을 씁니다 - 기업 노출 + 단가 —
adminVisible = true, 노출가·제안가 설정, 추천사 프리셋 주입 - 제안 + 크레딧 잠금 —
PROPOSAL로 바꾸고 캠페인 예산에서 잠급니다. 이미 잠겨 있으면 건너뜁니다 - 계약 체결 —
ApplicationContract를SIGNED상태로 직접 생성하고, 단가 수락 뒤 계약서 빈 칸을 더미로 채웁니다(아래 표) - 선정 확정 + 검수 라운드 —
SELECTED로 바꾸고ContentReview생성 (2회검수 = 1차, 1회검수 = 2차) - 일정 트리거 발화 — 아래 표 참고
마지막으로 캠페인 단계를 CONTENT_CREATION 으로 넘기고 신청자 목록을 숨깁니다(운영 서명 흐름과 동일). 여기까지가 한 트랜잭션으로 커밋되고, 상태를 지정한 크리에이터는 그 뒤에 아래 검수 상태 지정이 이어집니다.
검수 상태 지정 (선택)
creators[].state 를 주면 선정이 끝난 직후 그 크리에이터를 지정한 검수 상태까지 밀어 넣습니다. 상태 컬럼을 직접 쓰지 않고 운영 서비스를 순서대로 호출하므로 일정·승인 로그 같은 곁가지까지 운영과 같은 모양으로 남습니다.
상태를 지정하면 운영 흐름을 그대로 타므로 알림(메일·슬랙)과 AI 검수(Gemini 실호출)가 운영과 똑같이 나갑니다. AI 결과는 Python 이 비동기로 채우므로 응답 직후에는 비어 있고, 영상은 규정검수를 함께 돌면 1건당 약 5분 걸립니다.
state | 화면 표기 | 라운드 |
|---|---|---|
SCRIPT_SUBMISSION | 스크립트 - 제출 대기 (선정 직후 그대로) | 1차 |
SCRIPT_BUSINESS_WAITING | 스크립트 - 기업 검수 대기 | 1차 |
SCRIPT_ADMIN_WAITING | 스크립트 - 관리자 검수 대기 | 1차 |
SCRIPT_REVISION_PRODUCING | 스크립트 - 수정본 제작 중 | 1차 |
SCRIPT_REVISION_WAITING | 스크립트 - 수정본 검수 대기 | 1차 |
VIDEO_SUBMISSION | 영상 - 제출 대기 (스크립트 승인까지) | 2차 |
VIDEO_BUSINESS_WAITING | 영상 - 기업 검수 대기 | 2차 |
VIDEO_ADMIN_WAITING | 영상 - 관리자 검수 대기 | 2차 |
VIDEO_REVISION_PRODUCING | 영상 - 수정본 제작 중 | 2차 |
VIDEO_REVISION_WAITING | 영상 - 수정본 검수 대기 | 2차 |
스크립트 상태는 2회검수(1차 SCRIPT_VIDEO) 캠페인에서만 됩니다. SCRIPT_SUBMISSION 은 상태를 비운 것과 결과가 같지만 드롭다운에서 고를 수 있게 둔 값입니다. 한 캠페인에 한 크리에이터는 신청 1건이라, 10가지를 한 캠페인에 모두 만들려면 크리에이터 10명이 필요합니다.
진행 경로
크리에이터 한 명은 아래 단계를 목표 상태에서 멈출 때까지 차례로 밟습니다.
| 단계 | 호출하는 운영 서비스 | 여기서 멈추면 |
|---|---|---|
| (없음) | — | 제출 대기 |
| ① 제출 | ContentSubmissionService.batchSubmit — 1차는 스크립트 3장면, 2차는 캠페인 2차 제출물 구성(영상·캡션·해시태그 등) | 기업 검수 대기 |
| ② 기업 피드백 | FeedbackApprovalService.submitBulk — 피드백 4건 | 관리자 검수 대기 |
| ③ 관리자 승인 | FeedbackApprovalService.decide — 전 피드백 승인 | 수정본 제작 중 |
| ④ 재제출 | batchSubmit — 기존 제출물 id를 실어 다시 제출 | 수정본 검수 대기 |
영상 상태는 이 앞에 1차 스크립트를 제출하고 피드백 없이 승인(FeedbackApprovalService.approve)하는 단계가 붙습니다. 그러면 2차 라운드가 자동으로 생기고, 거기서 ①부터 진행합니다. VIDEO_SUBMISSION 은 2차 라운드가 열린 직후에서 멈춥니다(1회검수 캠페인이면 선정 직후 그대로).
단계마다 검수 라운드(TB_CONTENT_REVIEW)는 아래 모양이 됩니다. 응답의 reviewState.matchesExpected 가 이 표와의 일치 여부입니다.
| 목표 상태 | status | admin_approval_status | awaiting_revision | current_revision_no |
|---|---|---|---|---|
| 제출 대기 | PENDING | NONE | 0 | 1 |
| 기업 검수 대기 | REVIEWING | NONE | 0 | 1 |
| 관리자 검수 대기 | REVIEWING | PENDING | 0 | 1 |
| 수정본 제작 중 | REJECTED | APPROVED | 1 | 1 |
| 수정본 검수 대기 | REVIEWING | APPROVED | 0 | 2 |
수정본 검수 대기의 admin_approval_status 가 APPROVED 인 것은 정상입니다. 재제출은 이 값을 리셋하지 않아 직전 차수의 승인값이 남습니다.
넣는 피드백
관리자 검수 대기 이후 상태에는 한 라운드에 피드백 4건이 들어갑니다. 스크립트는 장면마다 돌려 가며, 영상 라운드는 영상 제출물에 모아 답니다.
| 피드백 | feedback_type | billing_type | paid_scope | 분류 / 항목 |
|---|---|---|---|---|
| 자유 | FREE | FREE | — | — |
| 가이드라인 미반영 | GUIDELINE_UNREFLECTED | FREE | — | 기본 공통 항목 / 자막 필수 확인 |
| 가이드라인 외 추가 | GUIDELINE_EXTRA | FREE | — | 자막 |
| 유료 | FREE | PAID | paidScope 값 | — / 재촬영 |
관리자 검수 게이트
관리자 검수 대기와 수정본 두 상태는 기업 피드백이 관리자 검수 게이트에 걸려야 만들어집니다. 게이트가 꺼진 채 피드백을 넣으면 관리자를 건너뛰고 바로 크리에이터에게 릴리스돼 상태가 조용히 틀어집니다.
- 스크립트 게이트는 캠페인 설정
admin_feedback_approval_script_enabled와 Flagsmithscript_admin_gate_enabled가 둘 다 켜져야 걸립니다. Flagsmith 가 꺼져 있으면 스크립트 관리자 검수 이후 상태는 실패로 돌립니다(코드로 켤 수 없습니다). - 영상 게이트는 캠페인 설정
admin_feedback_approval_enabled하나로 걸립니다. enableAdminGate: true가 캠페인 게이트를 켜면 그 캠페인의 다른 신청에도 적용됩니다. 응답reviewStateSummary.gatesEnabled에 켠 게이트가 남습니다.
이미 진행된 신청
같은 크리에이터가 이 캠페인에 이미 신청해 있으면 선정은 그 신청을 재사용합니다. 그런데 그 신청의 시작 라운드가 이미 제출된 상태라면 되돌리지 않고 그 크리에이터의 상태 세팅만 실패로 돌립니다. 다른 사람의 테스트일 수 있어서입니다. 다시 만들려면 새 캠페인에서 선정하세요.
일정 트리거를 실제로 발화시키는 이유
마감이 trigger_type 없이 임시값(placeholder)으로 남으면, 어드민·크리에이터 화면이 계약자 전원을 연체로 표시합니다. 그래서 상태는 직접 만들되 일정 트리거만은 운영과 같은 서비스를 호출합니다.
| 캠페인 | 발화하는 트리거 | 제출 마감 |
|---|---|---|
| 비배송형 | TRIGGER_1B_CONTRACT_GUIDELINE | 서명 + 3일(2회검수) / +4일(1회검수) |
| 국내 배송형 | TRIGGER_1A_DELIVERY_INPUT | 배송완료 + 3일(2회검수) / +4일(1회검수) |
| 해외 배송형 (2회검수) | TRIGGER_1A_DELIVERY_ESTIMATE | 운송장 등록 + 배송영업일 4일 + 3일 |
| 해외 배송형 (1회검수) | TRIGGER_1B_CONTRACT_GUIDELINE | 서명 + 9일 (운송장 트리거 미사용) |
배송형에서는 운송장 더미와 배송 완료 시각까지 넣습니다. 택배사는 국내면 kr.cjlogistics, 해외면 OTHER 로 들어갑니다.
skipDelivery: true 를 주면 배송 처리를 건너뛰어 마감이 임시값으로 남습니다. 운송장 입력을 광고주 화면에서 직접 테스트하고 싶을 때 쓰세요. 이 상태에서는 화면에 연체로 보이는 것이 정상입니다.
응답 (200 OK)
{
"status": 200,
"code": null,
"message": "선정 완료",
"data": {
"collabNo": 3201,
"doubleReview": true,
"nextStage": "SCRIPT_SUBMISSION",
"campaignSubStep": "CONTENT_CREATION",
"selectedCount": 2,
"selected": [
{
"applicationId": 88123,
"influenceNo": 6282,
"influenceName": "테스트크리에이터",
"unitPrice": 100000,
"lockedAmount": 100000,
"contractId": 4412,
"contractStatus": "SIGNED",
"contractPaperwork": {
"contractId": 4412,
"contractInfoFilled": true,
"variablesFilled": true,
"documentsAdded": 2,
"pdfCreated": true,
"signedPdfUrl": "https://glowb-input.s3.ap-southeast-1.amazonaws.com/contract/pdf/..."
},
"reviewRound": 1,
"deliveryCompleted": true,
"reviewState": {
"state": "SCRIPT_ADMIN_WAITING",
"reached": true,
"reviewId": 9001,
"feedbackIds": [5101, 5102, 5103, 5104],
"review": {
"status": "REVIEWING",
"adminApprovalStatus": "PENDING",
"awaitingRevision": false,
"currentRevisionNo": 1
},
"matchesExpected": true
}
}
],
"failed": [],
"reviewStateSummary": {
"requested": 1,
"reached": 1,
"scriptAdminGateFlag": true,
"gatesEnabled": ["SCRIPT"],
"note": "AI 검수 결과는 Python 이 비동기로 채운다(영상 1건 약 5분, 규정검수 병행 시)."
}
}
}| 필드 | 설명 |
|---|---|
nextStage | SCRIPT_SUBMISSION(2회검수) 또는 CONTENT_SUBMISSION(1회검수) |
lockedAmount | 실제로 잠긴 금액. 만원 단위 올림이 적용됩니다 |
reviewRound | 생성된 검수 라운드 (1 = 스크립트, 2 = 영상) |
contractPaperwork | 계약서 더미 채움 결과. 재호출이면 이미 채워져 있어 전부 false / 0 입니다. PDF 생성이 실패해도 선정은 그대로 진행되고 pdfError 에 사유가 남습니다 |
deliveryCompleted | 배송 완료까지 세팅했는지 여부 |
failed[] | 인플루언서를 못 찾은 건. { influenceNo, reason } |
selected[].reviewState | 상태를 지정한 크리에이터에만 붙습니다. 성공이면 reached: true 와 reviewId(영상 상태면 scriptReviewId 도), feedbackIds, 다시 읽은 review, matchesExpected. 실패면 reached: false 와 reason, 실패 전에 커밋된 마지막 단계 reachedStep |
reviewStateSummary | 상태를 지정했을 때만 붙습니다. 요청·도달 건수, 이번 호출에서 켠 게이트 gatesEnabled |
존재하지 않는 인플루언서가 섞여 있어도 전체가 실패하지 않고 해당 건만 failed 로 빠집니다. 검수 상태 세팅이 한 명에서 실패해도 선정과 다른 크리에이터의 상태 세팅은 그대로 진행되고, 실패한 사람의 앞 단계는 되돌려지지 않습니다.
재호출 안전성
같은 크리에이터로 다시 호출해도 터지지 않습니다. 신청·계약·배송·매칭 레코드는 있으면 갱신하고, 예산은 이미 잠겨 있으면 건너뜁니다.
에러 응답
에러도 HTTP 는 200 이고, 판정은 body 의 status 필드로 합니다.
body status | message | 상황 |
|---|---|---|
404 | influenceNos 또는 creators 는 필수입니다. | 둘 다 비었거나 없음 |
404 | influenceNos 와 creators 는 함께 쓸 수 없습니다. | 둘 다 보냄 |
404 | 같은 인플루언서가 두 번 들어 있습니다. | 같은 번호 중복 |
404 | 캠페인을 찾을 수 없습니다: {collabNo} | 캠페인 없음 |
SNS 링크는 반드시 채웁니다. 비어 있으면 추천사 재생성과 벡터화가 SNS 링크 없음 으로 통째로 스킵됩니다. 과거 신청에 링크가 없는 크리에이터는 채널별 프리셋이 들어갑니다(인스타 glow.b_kor — 글로우비 자사 계정 / 틱톡 twice_tiktok_official). 기존 신청을 재사용할 때도 링크가 비어 있으면 채워 넣습니다.
계약서 더미
서명 흐름을 건너뛰면 크리에이터가 입력하는 칸이 전부 비어, 크리에이터 화면에 서류 제출 모달이 계속 뜨고 "계약서 보기"도 비어 있습니다. 그래서 아래를 더미로 채웁니다. 사람이 이미 입력하거나 올린 값은 덮어쓰지 않습니다.
| 항목 | 채우는 값 |
|---|---|
| 서명자·대표자·예금주 | 크리에이터 이름 |
| 은행 / 계좌번호 | 테스트은행 / 000-0000-0000-00 |
| 주민등록번호 | 9001011000000 |
| 주소 | 서울특별시 강남구 테스트로 1 (자동생성 더미 주소) |
| 계약일 | 호출한 날 |
| 계약서 변수 | 2차활용기간(캠페인 값), 광고대금(수락 단가) |
서류 (TB_CONTRACT_DOCUMENT) | IDENTIFICATION, BANK_ACCOUNT_COPY 두 행 (test 에 올라가 있는 스크린샷 재사용) |
| 서명 PDF | 더미 서명 이미지로 실제 계약서 PDF를 생성해 test S3에 업로드 |
정산정보(TB_CONTRACT_PAYMENT_INFO)는 해외 계약 전용이라 만들지 않습니다. 이미 선정된 신청도 같은 크리에이터로 다시 선정하면 빈 칸만 채워집니다.
이 API가 만들지 않는 것
- 배송지(
TB_DELIVERY_ADDRESS) — 배송 레코드만 완료 처리합니다 - 알림·메일·문자 — 선정만 할 때는 일절 발송하지 않습니다. 검수 상태를 지정하면 운영 흐름대로 나갑니다