Glowb Dev Docs
Admin APIAdmin Dashboard API

POST /ai/admin/dashboard/{campaignNo}/price-negotiations

협업 단가 전송

협업 단가 전송 (벌크)

선택된 크리에이터들에게 단가 제안을 전송합니다.

  • CampaignApplication이 없으면 자동 생성(ADMIN_ADDED)되고, PriceNegotiation이 PENDING 상태로 생성됩니다.
  • 각 크리에이터별로 독립적으로 처리되며, 일부 실패 시에도 나머지는 정상 처리됩니다.

중복 방지 정책

동일 크리에이터에게 중복 전송을 방지하기 위해 다음 조건을 확인합니다:

  • PENDING 상태: 이미 협상 진행 중이면 재요청 불가
  • ACCEPT 상태: 이미 수락된 협상이 있으면 재요청 불가
  • REJECT 상태: 거절된 경우에만 재협상 가능

HTTP 요청

POST /ai/admin/dashboard/{campaignNo}/price-negotiations
Authorization: Bearer {access_token}
Content-Type: application/json

Path Parameters

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

Request Body

필드타입필수설명
itemsArray예단가 제안 목록
items[].applicationIdLong조건부기존 신청 ID (있으면 우선 사용)
items[].influenceNoInteger조건부인플루언서 번호 (applicationId 없을 때 필수)
items[].proposedPriceLong조건부제안 단가 (캠페인 통화). priceMode 를 보내면 무시됨
priceModeString아니오제안가 산정 방식. 보내면 서버가 크리에이터별로 계산 (아래 표)
customPriceKrwLong조건부priceMode=CUSTOM 일 때 관리자 입력 금액 (원)

priceMode — 서버 계산

제안가는 크리에이터가 보는 캠페인 통화 기준입니다. 원화로만 있는 값(랭킹가·직접 입력)은 외화 캠페인(currency 가 KRW 가 아닌 캠페인)에서 캠페인 환율(외화 1단위당 원)로 나눠 바꿉니다. 끝자리는 통화와 관계없이 소수점만 버립니다 (기존 프론트 계산과 같은 규칙이라 국내 결과는 동일).

priceMode국내(KRW)외화(USD·JPY 등)재료 없을 때
RANK랭킹가 그대로랭킹가(원) ÷ 환율. 미국 틱톡은 USD 랭크가를 그대로 사용NO_RANK_PRICE, 환율 없으면 RATE_NOT_SET
DESIRED희망가 그대로희망가 그대로 (환율 불필요)NO_DESIRED_PRICE
DESIRED_MINUS_10희망가 × 0.9희망가 × 0.9 (환율 불필요)NO_DESIRED_PRICE
DESIRED_MINUS_20희망가 × 0.8희망가 × 0.8 (환율 불필요)NO_DESIRED_PRICE
CUSTOMcustomPriceKrw 그대로customPriceKrw ÷ 환율INVALID_PRICE, 환율 없으면 RATE_NOT_SET
  • 재료가 없는 크리에이터는 다른 값으로 대체하지 않고 failedItems 로 돌려주며, 나머지는 정상 전송합니다.
  • 계산 결과가 0 이면 INVALID_PRICE 로 실패합니다 (예: 1,000원 ÷ 1,380).
  • priceMode 를 생략하면 기존처럼 items[].proposedPrice 를 그대로 저장합니다.
  • 실제 저장된 금액은 응답 sentPrices 로 확인하고, 후속 알림·화면 갱신은 이 값을 사용하세요.

요청 예시 (priceMode)

{
  "priceMode": "DESIRED_MINUS_10",
  "items": [
    { "applicationId": 123 },
    { "applicationId": 124 }
  ]
}

요청 예시 (기존 방식)

{
  "items": [
    {
      "applicationId": 123,
      "proposedPrice": 500000
    },
    {
      "influenceNo": 456,
      "proposedPrice": 300000
    }
  ]
}

응답

성공 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "3명에게 단가 제안이 전송되었습니다.",
  "data": {
    "success": true,
    "successApplicationIds": [123, 124, 125],
    "sentPrices": { "123": 49, "124": 45, "125": 90 },
    "phoneNotFoundApplicationIds": [125],

    "failedItems": [],
    "message": "3명에게 단가 제안이 전송되었습니다. (전화번호 없음: 1명)"
  }
}

일부 실패 응답 (200 OK)

{
  "status": 200,
  "code": null,
  "message": "2명 성공, 1명 실패",
  "data": {
    "success": false,
    "successApplicationIds": [123, 124],
    "phoneNotFoundApplicationIds": [],
    "failedItems": [
      {
        "applicationId": 125,
        "influenceNo": null,
        "failureCode": "CREATE_FAILED",
        "failureMessage": "이미 협상 진행 중입니다. 크리에이터의 응답을 기다려주세요."
      }
    ],
    "message": "2명 성공, 1명 실패"
  }
}

에러 응답 예시

중복 협상 (PENDING 상태)

failedItems에 포함되는 형태:

{
  "failureCode": "CREATE_FAILED",
  "failureMessage": "이미 협상 진행 중입니다. 크리에이터의 응답을 기다려주세요."
}

수락된 협상 재시도 (ACCEPT 상태)

failedItems에 포함되는 형태:

{
  "failureCode": "CREATE_FAILED",
  "failureMessage": "이미 수락된 협상이 있습니다. 새로운 협상을 시작할 수 없습니다."
}

존재하지 않는 신청

failedItems에 포함되는 형태:

{
  "failureCode": "APPLICATION_NOT_FOUND",
  "failureMessage": "존재하지 않는 신청입니다."
}

캠페인 불일치

failedItems에 포함되는 형태:

{
  "failureCode": "CAMPAIGN_MISMATCH",
  "failureMessage": "캠페인이 일치하지 않습니다."
}

priceMode 계산 실패

failureCode사유
NO_DESIRED_PRICE희망가가 없음 (불러오기로 추가한 크리에이터는 희망가를 먼저 저장)
NO_RANK_PRICE랭킹가가 없거나 0
RATE_NOT_SET외화 캠페인인데 환율 미설정 (RANK·CUSTOM)
INVALID_PRICE직접 입력 금액이 없거나 계산 결과가 0

캠페인에서 제외된 크리에이터

크리에이터가 가격 협상 응답 시 캠페인 참여를 거절(decline)하면 신청이 제외 처리(ELIMINATED)됩니다. 이 상태의 신청에는 단가를 재전송할 수 없습니다. 다시 초대하려면 관리자가 먼저 선정 상태를 복구해야 합니다.

failedItems에 포함되는 형태:

{
  "failureCode": "ELIMINATED",
  "failureMessage": "캠페인에서 제외된 크리에이터입니다. 선정 상태를 먼저 복구해야 단가를 전송할 수 있습니다."
}

응답 필드 설명

필드타입설명
successBoolean전체 성공 여부 (모든 항목 성공 시 true)
successApplicationIdsArray<Long>성공한 신청 ID 목록
sentPricesObject성공한 신청 ID → 실제 저장된 제안가 (캠페인 통화)
phoneNotFoundApplicationIdsArray<Long>전화번호가 없는 신청 ID 목록 (SMS 발송 불가)
failedItemsArray실패한 항목 목록
failedItems[].applicationIdLong실패한 신청 ID
failedItems[].influenceNoInteger실패한 인플루언서 번호
failedItems[].failureCodeString실패 코드
failedItems[].failureMessageString실패 사유

API 테스트

On this page