Glowb Dev Docs
SaaS API

기업용 콘텐츠 검수 API

기업 대시보드에서 사용하는 콘텐츠 검수 관련 API

기업용 콘텐츠 검수 API

기업 대시보드에서 사용하는 콘텐츠 검수 관련 API입니다.

Base URL: /ai/business/contents

인증 정보

항목
인증 필요
인증 방식JWT Bearer Token

엔드포인트 목록

메서드경로설명인증
PUT/ai/business/contents/guideline/{collabNo}가이드라인 저장/생성필요
PUT/ai/business/contents/guideline/empty/{collabNo}가이드라인 빈 템플릿 요청필요
GET/ai/business/contents/review/{reviewId}/detail검수 라운드 상세 조회필요
POST/ai/business/contents/{itemId}/feedbacks피드백 추가/검수 완료 (개별)필요
POST/ai/business/contents/review/{reviewId}/feedbacks/bulk벌크 피드백 추가/검수 완료필요
POST/ai/business/contents/upload-date업로드일 선택필요
POST/ai/business/contents/review-request검수 추가 요청필요
POST/ai/business/contents/final-submissions/bulk벌크 최종 제출물 조회필요
POST/ai/business/contents/final-submission/{applicationId}/re-request최종제출물 재요청필요

API 상세

가이드라인 저장/생성 (Upsert)

가이드라인 데이터를 저장합니다. 없으면 생성, 있으면 병합합니다.

수정 제한 조건

  • 가이드라인이 이미 존재하는 경우, 모집 시작 전에만 수정 가능합니다.
  • 모집이 시작된 이후(CREATOR_RECRUIT 단계 이후)에는 수정이 불가능합니다.
  • 수정 가능 단계: CAMPAIGN_REVIEW, CAMPAIGN_PAYMENT, CAMPAIGN_GUIDELINE

초안 검수 vs 완성본 저장

  • isFirst=true: 초안 검수 모드. 가이드라인 상태가 DRAFT로 설정되며, hasGuideline=false 유지
  • isFirst=false (기본값): 완성본 저장 모드. 가이드라인 상태가 COMPLETED로 설정되며, hasGuideline=true
  • 완성본 저장 시에만 캠페인 단계가 CREATOR_RECRUIT로 변경됩니다 (모집 시작)

HTTP 요청

PUT /ai/business/contents/guideline/{collabNo}?isFirst=false
Authorization: Bearer {access_token}
Content-Type: application/json

Path Parameters

파라미터타입필수설명
collabNoint캠페인 번호

Query Parameters

파라미터타입필수기본값설명
isFirstboolean아니오falsetrue: 초안 검수 (DRAFT), false: 완성본 저장 (COMPLETED)

Request Body

가이드라인은 4단계 Wizard 구조로 되어 있습니다. 각 단계별로 부분 저장이 가능하며, 기존 데이터와 병합됩니다.

{
  "contentTypes": ["VIDEO", "CAPTION", "HASHTAG"],
  "basicInfo": {
    "productUrl": "https://example.com/product",
    "contentConcepts": [
      {
        "code": "STORY_TELLING",
        "referenceUrl": "https://instagram.com/reel/xxx",
        "referenceDescription": "이 영상처럼 자연스러운 썰풀기 스타일로"
      }
    ],
    "keywords": [
      { "text": "여름선크림", "isRequired": true }
    ],
    "titleExample": "여름 필수템! 촉촉한 선크림 추천"
  },
  "contentDetailInfo": {
    "categoryCode": "BEAUTY",
    "basicShots": [
      {
        "code": "TEXTURE_SHOT",
        "appealPoint": "크리미한 제형과 빠른 흡수력을 보여주세요",
        "subOption": null
      }
    ],
    "additionalOptions": [
      { "code": "BEFORE_AFTER", "additionalPrice": 50000, "inputText": null }
    ],
    "etcOptions": []
  },
  "marketingInfo": {
    "requiredPoints": ["SPF50+ PA++++ 자외선 차단", "촉촉한 수분감"],
    "optionalPoints": ["비건 인증"],
    "videoMarketingOptions": {
      "promotion": {
        "referenceUrl": "https://example.com/promo",
        "description": "6월 한정 20% 할인 진행 중"
      },
      "brandAccountTag": {
        "accountName": "@glowb_beauty",
        "tagMethods": ["PERSON_TAG", "CAPTION_TAG"],
        "saveAsDefault": true
      },
      "sponsorLabel": {
        "accountName": "@glowb_official"
      },
      "collaborator": {
        "accountName": "@glowb_collab",
        "tagMethods": null,
        "saveAsDefault": false
      },
      "useAutoDm": false,
      "productLinkShare": {
        "enabled": true,
        "shareMethods": ["dm", "profileLink"],
        "shareLink": "https://example.com/product",
        "shareDuration": 2,
        "shareDurationUnit": "week",
        "priceIncrease": 50000
      }
    },
    "hashtags": ["#글로브뷰티", "#여름선크림"]
  },
  "detailPageInfo": {
    "brandLogo": {
      "imageUrl": "https://s3.../brand-logo.png",
      "saveAsDefault": true
    },
    "thumbnailUrl": "https://s3.../thumbnail.jpg",
    "detailImageUrls": ["https://s3.../detail-1.jpg", "https://s3.../detail-2.jpg"]
  }
}

Request Body 필드 상세 설명

전체 필드 상세는 가이드라인 조회 API 문서를 참고하세요.

응답

성공 응답 (200 OK) - 완성본 저장 (isFirst=false)

{
  "status": 200,
  "code": null,
  "message": "가이드라인이 저장되었습니다.",
  "data": {
    "collabNo": 123,
    "isFirst": false,
    "guidelineStatus": "COMPLETED",
    "message": "가이드라인이 저장되었습니다."
  }
}

성공 응답 (200 OK) - 초안 검수 (isFirst=true)

{
  "status": 200,
  "code": null,
  "message": "가이드라인이 저장되었습니다.",
  "data": {
    "collabNo": 123,
    "isFirst": true,
    "guidelineStatus": "DRAFT",
    "message": "가이드라인이 저장되었습니다."
  }
}

Response 스키마

필드타입설명
collabNoint캠페인 번호
isFirstboolean초안 검수 여부
guidelineStatusstring가이드라인 상태 (REQUESTED: 요청됨, DRAFT: 초안, COMPLETED: 완성)
messagestring결과 메시지

에러 응답 (400 Bad Request) - 모집 시작 후 수정 시도

{
  "status": 400,
  "code": "GUIDELINE_MODIFICATION_NOT_ALLOWED",
  "message": "모집이 시작된 이후에는 가이드라인을 수정할 수 없습니다.",
  "data": null
}

가이드라인 빈 템플릿 요청

관리자에게 빈 가이드라인 템플릿 작성을 요청합니다. 가이드라인 상태가 REQUESTED로 설정되고, 캠페인 진행상태가 CAMPAIGN_GUIDELINE 단계로 변경됩니다.

상태 변경

  • guidelineStatusREQUESTED (빈 템플릿 요청됨)
  • campaignSubStepCAMPAIGN_GUIDELINE (가이드라인 작성 단계)

HTTP 요청

PUT /ai/business/contents/guideline/empty/{collabNo}
Authorization: Bearer {access_token}

Path Parameters

파라미터타입필수설명
collabNoint캠페인 번호

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "가이드라인 빈템플릿 요청이 완료 되었습니다.",
  "data": {
    "collabNo": 123,
    "guidelineStatus": "REQUESTED"
  }
}

Response 스키마

필드타입설명
collabNoint캠페인 번호
guidelineStatusstring가이드라인 상태 (REQUESTED: 빈 템플릿 요청됨)

검수 라운드 상세 조회

검수 라운드의 모든 제출물과 각 제출물의 히스토리, 피드백을 포함하여 조회합니다.

HTTP 요청

GET /ai/business/contents/review/{reviewId}/detail
Authorization: Bearer {access_token}

Path Parameters

파라미터타입필수설명
reviewIdlong검수 라운드 ID

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "검수 라운드 상세 조회가 완료되었습니다.",
  "data": {
    "reviewId": 1,
    "applicationId": 100,
    "reviewRound": 1,
    "status": "REVIEWING",
    "uploadApproved": false,
    "feedbackDeadline": "2024-01-20T10:30:00",
    "createdAt": "2024-01-15T10:30:00",
    "creatorName": "홍길동",
    "creatorEmail": "creator@example.com",
    "submissionItems": [
      {
        "id": 1,
        "itemType": "VIDEO",
        "filePath": "https://s3.../video.mp4",
        "editorState": null,
        "comment": null,
        "status": "REVIEWING",
        "isSubmitted": true,
        "submittedAt": "2024-01-16T14:00:00",
        "uploadedAt": "2024-01-16T14:00:00",
        "currentVersion": 2,
        "createdAt": "2024-01-15T10:30:00",
        "histories": [...],
        "feedbacks": [...]
      }
    ],
    "unreflectedFeedbackIds": [1, 2, 3]
  }
}

Response 스키마

필드타입설명
reviewIdlong검수 라운드 ID
applicationIdlongCampaignApplication ID
reviewRoundint검수 라운드 (1: 1차, 2: 2차)
statusstring검수 상태
uploadApprovedboolean업로드 승인 여부
feedbackDeadlinedatetime피드백 마감일
createdAtdatetime생성일시
creatorNamestring크리에이터명
creatorEmailstring크리에이터 이메일
submissionItemsarray제출물 목록 (히스토리, 피드백 포함)
unreflectedFeedbackIdsarray<long>반영되지 않은 피드백 ID 목록 (nullable)

unreflectedFeedbackIds 필드

기업이 "피드백 미반영" 검수 추가 요청 시 지정한 피드백 ID 목록입니다.

  • 값이 있으면: 해당 피드백들을 하이라이트 표시하여 크리에이터가 미반영한 피드백 확인 가능
  • 값이 null이면: 검수 추가 요청이 없거나 OUTSIDE_GUIDELINE 유형인 경우

피드백 추가 / 검수 완료

제출물에 피드백을 추가하거나 검수를 완료 처리합니다.

  • 피드백 내용이 비어있고 파일도 없으면 → 검수 완료 (APPROVED)
  • 피드백 내용이나 파일이 있으면 → 재제출 요청 (REJECTED) + 피드백 마감일 설정 (4일 후)

HTTP 요청

POST /ai/business/contents/{itemId}/feedbacks
Authorization: Bearer {access_token}
Content-Type: multipart/form-data

Path Parameters

파라미터타입필수설명
itemIdlong제출물 ID

Request Parts

파트타입필수설명
requestFeedbackDto.CreateRequest피드백 요청 JSON
filesfile[]아니오첨부 파일 (이미지/영상)

request JSON 예시

{
  "content": "영상 중간 부분 재촬영 필요합니다.",
  "mediaFeedbacks": [
    {
      "comment": "이 부분 조명이 어둡습니다",
      "startTime": 15,
      "endTime": 20
    }
  ]
}

벌크 피드백 추가 / 검수 완료

검수 라운드의 모든 아이템에 대해 피드백 추가 또는 검수 완료를 일괄 처리합니다.

기존 개별 API의 문제점 해결

기존 개별 API(POST /{itemId}/feedbacks)는 아이템마다 검수 횟수(count)가 증가하여, 여러 아이템을 순차적으로 처리할 때 "검수 횟수 초과" 오류가 발생할 수 있습니다.

벌크 API는 모든 아이템을 한 번에 처리하고, 검수 횟수는 라운드 단위로 1번만 증가합니다.

처리 규칙

  • 각 아이템별로 feedback이 null이고 파일이 없으면 → 승인 (APPROVED)
  • feedback 내용이나 파일이 있으면 → 재제출 요청 (REJECTED)
  • 검수 횟수(count)는 전체 처리 후 1번만 증가
  • 피드백 마감일은 가장 마지막 피드백 기준으로 4일 후 설정

HTTP 요청

POST /ai/business/contents/review/{reviewId}/feedbacks/bulk
Authorization: Bearer {access_token}
Content-Type: multipart/form-data

Path Parameters

파라미터타입필수설명
reviewIdlong검수 라운드 ID

Request Parts

파트타입필수설명
requestBulkFeedbackDto.Request벌크 피드백 요청 JSON
filesfile[]아니오첨부 파일 (모든 아이템 파일을 배열로 전달)

request JSON 구조

{
  "items": [
    {
      "itemId": 244,
      "feedback": null,
      "highlightedText": null,
      "selection": null,
      "fileIndices": [],
      "mediaList": []
    },
    {
      "itemId": 245,
      "feedback": null,
      "highlightedText": null,
      "selection": null,
      "fileIndices": [],
      "mediaList": []
    },
    {
      "itemId": 246,
      "feedback": "피드백 내용입니다. 이 부분을 수정해주세요.",
      "highlightedText": "수정 필요한 텍스트",
      "selection": "{\"anchor\":{},\"focus\":{}}",
      "fileIndices": [0, 1],
      "mediaList": [
        {
          "mediaType": "IMAGE",
          "startTime": null,
          "endTime": null,
          "comment": "이 부분 참고"
        },
        {
          "mediaType": "VIDEO",
          "startTime": 10.5,
          "endTime": 15.0,
          "comment": "이 구간 재촬영 필요"
        }
      ]
    }
  ]
}

ItemFeedback 필드 설명

필드타입필수설명
itemIdlong제출물 아이템 ID
feedbackstring아니오피드백 내용 (null이면 승인 처리)
highlightedTextstring아니오하이라이트된 텍스트
selectionstring아니오선택 영역 정보 (JSON)
fileIndicesint[]아니오files 배열에서의 인덱스 목록
mediaListMediaMeta[]아니오각 파일에 대한 메타데이터

MediaMeta 필드 설명

필드타입필수설명
mediaTypestring아니오미디어 타입 (IMAGE, VIDEO)
startTimedouble아니오영상 시작 시간 (초)
endTimedouble아니오영상 끝 시간 (초)
commentstring아니오미디어에 대한 코멘트

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "3건의 제출물이 승인되었습니다.",
  "data": {
    "success": true,
    "approvedCount": 3,
    "rejectedCount": 0,
    "processedItemIds": [244, 245, 246],
    "message": "3건의 제출물이 승인되었습니다."
  }
}

피드백 포함 응답

{
  "status": 200,
  "code": null,
  "message": "2건 승인, 1건 피드백 추가가 완료되었습니다.",
  "data": {
    "success": true,
    "approvedCount": 2,
    "rejectedCount": 1,
    "processedItemIds": [244, 245, 246],
    "message": "2건 승인, 1건 피드백 추가가 완료되었습니다."
  }
}

에러 응답 (404) - 검수 횟수 초과

{
  "status": 404,
  "code": "INVALID_DATA",
  "message": "검수 횟수를 초과했습니다. (현재: 1/1) 검수 추가 요청이 필요합니다.",
  "data": null
}

검수 추가 요청

검수 횟수를 초과한 경우 추가 검수를 요청합니다.

검수 추가 요청 유형

  • FEEDBACK_NOT_REFLECTED: 피드백/가이드라인이 반영되지 않음 (크레딧 차감 없음)
  • OUTSIDE_GUIDELINE: 가이드라인 외 추가 요청 (50,000 크레딧 차감)

HTTP 요청

POST /ai/business/contents/review-request
Authorization: Bearer {access_token}
Content-Type: application/json

Request Body

{
  "reviewId": 123,
  "requestType": "FEEDBACK_NOT_REFLECTED",
  "unreflectedFeedbackIds": [1, 2, 3],
  "requestContent": "캡션에 필수 키워드가 누락되었습니다."
}

Request Body 필드 설명

필드타입필수설명
reviewIdlong검수 라운드 ID
requestTypestring요청 유형 (FEEDBACK_NOT_REFLECTED, OUTSIDE_GUIDELINE)
unreflectedFeedbackIdsarray<long>아니오반영되지 않은 피드백 ID 목록 (FEEDBACK_NOT_REFLECTED 시 필요)
requestContentstring아니오추가 요청 내용

상태 변경 처리

검수 추가 요청 시 다음 상태가 자동으로 변경됩니다:

  1. ContentReview

    • maxFeedbackCount +1 증가
    • statusPENDING (다시 피드백 가능 상태)
  2. unreflectedFeedbackIds에 해당하는 피드백 (FEEDBACK_NOT_REFLECTED 유형)

    • resolvedfalse (미해결 상태로 초기화)
    • checkedByCreatorfalse (체크 상태 초기화)
  3. 해당 피드백이 속한 제출물

    • statusREJECTED (재제출 필요 상태)

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "검수 추가 요청이 등록되었습니다. (피드백 횟수: 2회)",
  "data": {
    "id": 1,
    "reviewId": 123,
    "requestType": "FEEDBACK_NOT_REFLECTED",
    "creditDeducted": 0,
    "message": "검수 추가 요청이 등록되었습니다. (피드백 횟수: 2회)",
    "createdAt": "2024-01-20T10:30:00"
  }
}

OUTSIDE_GUIDELINE 유형 응답

{
  "status": 200,
  "code": null,
  "message": "검수 추가 요청이 등록되었습니다. (크레딧 차감: 50,000원, 피드백 횟수: 2회)",
  "data": {
    "id": 2,
    "reviewId": 123,
    "requestType": "OUTSIDE_GUIDELINE",
    "creditDeducted": 50000,
    "message": "검수 추가 요청이 등록되었습니다. (크레딧 차감: 50,000원, 피드백 횟수: 2회)",
    "createdAt": "2024-01-20T10:30:00"
  }
}

업로드일 선택

최종 업로드 일정을 선택합니다.

HTTP 요청

POST /ai/business/contents/upload-date
Authorization: Bearer {access_token}
Content-Type: application/json

Request Body

{
  "applicationId": 123,
  "selectionType": "OPTIMAL",
  "uploadDate": "2024-02-01"
}

selectionType 옵션

타입설명필수 필드
OPTIMAL3일 이내 최적 날짜에 게시uploadDate
CANDIDATE기업이 후보 지정 (관리자에게 이메일 전송)candidateDates

CANDIDATE 타입 예시

{
  "applicationId": 123,
  "selectionType": "CANDIDATE",
  "candidateDates": ["2024-02-01", "2024-02-03", "2024-02-05"]
}

벌크 최종 제출물 조회

여러 CampaignApplication ID에 대한 최종 제출물을 벌크로 조회합니다.

HTTP 요청

POST /ai/business/contents/final-submissions/bulk
Authorization: Bearer {access_token}
Content-Type: application/json

Request Body

{
  "applicationIds": [1, 2, 3, 4, 5]
}

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "벌크 최종 제출물 조회가 완료되었습니다.",
  "data": {
    "submissions": [
      {
        "applicationId": 1,
        "creatorName": "인플루언서A",
        "uploadLink": "https://instagram.com/p/...",
        "partnershipCode": "ABC123",
        "files": [
          {
            "fileUrl": "https://...",
            "fileName": "final.mp4",
            "fileType": "VIDEO"
          }
        ]
      }
    ]
  }
}

최종제출물 재요청

크리에이터에게 최종제출물 항목을 선택하여 재요청합니다.

재요청 특징

  • 여러 번 재요청 가능 (새 재요청은 기존 재요청을 덮어씀)
  • 크리에이터가 재제출해도 재요청 이력은 유지됨

HTTP 요청

POST /ai/business/contents/final-submission/{applicationId}/re-request
Authorization: Bearer {access_token}
Content-Type: application/json

Path Parameters

파라미터타입필수설명
applicationIdlong캠페인 신청 ID

Request Body

{
  "types": ["CONTENT_LINK", "CLEAN_FILE"]
}

재요청 타입 (FinalSubmissionType)

타입설명
CONTENT_LINK업로드한 콘텐츠 링크
PARTNERSHIP_CODE파트너십 코드
CLEAN_FILE클린본 파일
FINAL_FILE최종본 파일

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "재요청이 등록되었습니다.",
  "data": {
    "id": 1,
    "applicationId": 123,
    "contentLink": "https://instagram.com/p/...",
    "partnershipCode": "ABC123",
    "cleanFilePath": null,
    "finalFilePath": null,
    "isSubmitted": true,
    "submittedAt": "2024-01-15T10:30:00",
    "requiredTypes": ["CONTENT_LINK", "PARTNERSHIP_CODE", "CLEAN_FILE", "FINAL_FILE"],
    "hasReRequest": true,
    "reRequestTypes": ["CONTENT_LINK", "CLEAN_FILE"],
    "reRequestedAt": "2024-01-20T14:00:00"
  }
}

Response 스키마 (재요청 관련 필드)

필드타입설명
hasReRequestboolean재요청 존재 여부
reRequestTypesarray<string>재요청 항목 목록
reRequestedAtdatetime재요청 일시

사용 예시

피드백 추가

curl -X POST https://api.glowb.io/ai/business/contents/1/feedbacks \
  -H "Authorization: Bearer {token}" \
  -F 'request={"content":"재촬영 필요","mediaFeedbacks":[{"comment":"조명 문제","startTime":15,"endTime":20}]}' \
  -F "files=@reference.jpg"

업로드일 선택

curl -X POST https://api.glowb.io/ai/business/contents/upload-date \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "applicationId": 123,
    "selectionType": "OPTIMAL",
    "uploadDate": "2024-02-01"
  }'

최종제출물 재요청

curl -X POST https://api.glowb.io/ai/business/contents/final-submission/123/re-request \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "types": ["CONTENT_LINK", "CLEAN_FILE"]
  }'

API 테스트

On this page