본문으로
Knobase

POST /v1/offers/decide

유일한 V1 오퍼 결정 엔드포인트입니다. 표기된 오퍼 또는 성공적인 no-fill을 반환합니다.

현재 턴에 표기된 추적 오퍼를 반환할지 결정합니다.

인증

Bearer serving key가 필요합니다. 설정 토큰은 거절됩니다. 앱은 키에서 확인됩니다.

속도 제한

클라이언트 IP당 분당 60회. HTTP 429와 `{ ok: false, error: "rate_limited" }`.

요청 헤더

헤더
AuthorizationBearer $KNOBASE_API_KEY
Content-Typeapplication/json

요청 본문

필드타입필수설명
messagestring (max 4000)아니오Current user turn only. Processed transiently and not persisted. Do not send a transcript.
session.tokenstring (max 128)아니오Ephemeral host session token for frequency. Not a user id.
session.digest.intent_type"purchase" | "compare" | "browse" | "low" | "unknown"아니오Typed intent. No free text.
session.digest.category_idsstring[] (max 20, each max 64)아니오Allowlisted category identifiers only.
session.digest.entity_idsstring[] (max 20, each max 64)아니오Allowlisted entity identifiers only.
session.digest.excluded_entity_idsstring[] (max 20, each max 64)아니오Allowlisted exclusions. Unknown ids are dropped.
session.digest.budget_bucket"unknown" | "low" | "mid" | "high"아니오Coarse budget band. No currency or amounts.
session.digest.safety"clear" | "unknown" | "blocked"아니오`unknown` or `blocked` results in safe no-fill.
session.digest.generated_atstring (max 40)아니오ISO timestamp. Digests older than 30 minutes are ignored.
localestring (2–16)아니오Serving is live for `ko*` and `en*` locales only.
countrystring (2–8)아니오ISO-style country hint. Not a precise location.
audience.age_band"under_13" | "13_17" | "18_plus" | "unknown"아니오Never infer age. Serving requires `18_plus`.
audience.contextual_offer_consentboolean아니오Serving requires `true`. Omitted or false is no-fill.
native_surface.clickablebooleanSelf-attestation that an existing separate clickable surface is available. Not production trust.
native_surface.disclosure_capablebooleanSelf-attestation that disclosure and advertiser identity can be shown. False is CAPABILITY_UNMET.

제약

  • message는 현재 사용자 턴만입니다. 전체 기록을 보내지 마세요.
  • 신원, 이메일, 전화, 광고 ID, 정밀 위치, 건강, 금융, 생체, 교차 앱 식별 데이터를 보내지 마세요.
  • 현재 메시지는 일시 처리되며 저장되지 않습니다.
  • typed digest는 선택이며 자유 텍스트가 없습니다.
  • 동의, 나이, 안전이 불명이면 안전한 no-fill입니다.
  • serving은 audience.age_band = 18_plus와 contextual_offer_consent = true가 필요합니다.
  • native_surface.clickable과 disclosure_capable는 필수 자가 증명 불리언입니다. 하나라도 false이면 CAPABILITY_UNMET입니다.
  • 알 수 없는 최상위 키는 검증 실패입니다. envelope는 strict입니다.
  • 타임아웃, 네트워크 오류, 잘못된 응답, serve:false는 no-render로 다루세요.

본문 어디서든 거절되는 키: email, phone, tel, user_id, userid, userId, name, full_name, fullname, lat, lng, latitude, longitude, address, ip, conversation, conversation_history, messages, raw_message, app_id, placement, display_style, surface_id, capabilities.

요청 예제

curl -sS -X POST "https://knobase.com/v1/offers/decide" \
  -H "Authorization: Bearer $KNOBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"시니어 반려견에게 줄 부드러운 사료를 찾고 있어요","locale":"ko","country":"KR","audience":{"age_band":"18_plus","contextual_offer_consent":true},"native_surface":{"clickable":true,"disclosure_capable":true}}'

오퍼 반환 응답

HTTP 200. disclosure, advertiser, link는 필수 상업 무결성 필드입니다. title, description, image_url, cta는 선택 힌트이며 특정 UI를 만들라는 지시가 아닙니다. offer_id는 이벤트의 offer_snapshot_id로 쓰는 스냅샷 id입니다.

필드타입필수설명
servetrueAn eligible offer may be used on an existing surface.
request_idstringRequest identifier.
decision_idstringDecision identifier (`dec_…`).
offer_idstringOffer snapshot id (`snp_…`). Send this as `offer_snapshot_id` on events.
expires_atstring | nullISO expiry when present.
disclosurestringRequired sponsorship label. Must be visible.
advertiserstringRequired advertiser identity. Must be visible.
linkstringKnobase tracked URL. Never replace with a merchant URL.
titlestring아니오Optional presentation hint. Not a UI instruction.
descriptionstring아니오Optional presentation hint. Not a UI instruction.
image_urlstring아니오Optional presentation hint. Not a UI instruction.
ctastring아니오Optional presentation hint. Not a UI instruction.
reason_codesstring[]Includes `MATCHED` when an offer is served.
{
  "serve": true,
  "request_id": "req_01",
  "decision_id": "dec_01",
  "offer_id": "snp_01EXAMPLEOFFER",
  "expires_at": "2026-09-10T12:30:00.000Z",
  "disclosure": "광고",
  "advertiser": "Example Pet",
  "link": "https://knobase.com/r/tok_example",
  "title": "시니어 반려견을 위한 부드러운 식사 옵션",
  "description": "제품 정보와 원재료를 확인해보세요.",
  "cta": "제품 보기",
  "reason_codes": [
    "MATCHED"
  ]
}

No-fill 응답

serve:false인 HTTP 200은 성공입니다. 유기적 답변을 유지하세요. 사용자를 막는 재시도는 하지 마세요.

필드타입필수설명
servefalseDo not render an offer.
request_idstringRequest identifier.
decision_idstringDecision identifier (`dec_…`).
reason_codesstring[]Why no offer was returned.
retry_after_secondsnumber아니오Present on some no-fill outcomes, including timeout.
{
  "serve": false,
  "request_id": "req_02",
  "decision_id": "dec_02",
  "reason_codes": [
    "NO_ELIGIBLE_OFFER"
  ],
  "retry_after_seconds": 0
}

사유 코드

코드의미
POLICY_BLOCKAudience, safety, or policy did not allow a serve.
LOW_INTENTIntent was too weak to serve.
NO_ELIGIBLE_OFFERNo matching eligible offer.
BUDGET_EXHAUSTEDCampaign or account budget is exhausted.
TIMEOUTHandler budget exceeded. Fail open.
LOCALE_NOT_LIVELocale is not in the live serving set.
FREQUENCY_CAPPEDSession frequency cap reached.
MATCHEDAn eligible offer was returned.
INVALID_ENVELOPERequest failed schema or unknown-field validation.
FORBIDDEN_IDENTITY_FIELDA forbidden identity or placement field was present.
CAPABILITY_UNMETnative_surface.clickable or disclosure_capable was false.

오류

HTTP본문상황
401{ "ok": false, "error": "…" }없거나 잘못된 키, 폐기된 키
400{ "ok": false, "error": "INVALID_ENVELOPE" | "FORBIDDEN_IDENTITY_FIELD" }잘못된 또는 금지된 본문
429{ "ok": false, "error": "rate_limited" }IP당 분당 60회 초과
200serve:false, TIMEOUT처리 예산 초과. fail-open

문서 버전 v1 · 구현된 퍼블리셔 API 기준