연동 문서

brownie 에 고객 데이터를 보내는 방법

서비스 서버와 앱이 고객의 행동(이벤트) · 속성 · 수신 동의를 brownie 로 보내면, 세그먼트와 캠페인이 그 데이터로 받을 사람과 보낼 때를 정해요. 이 문서는 개념과 규칙을 설명하고, 엔드포인트별 요청 · 응답 형식은 API 레퍼런스에 있어요.

시작하기

모든 요청은 HTTPS 로 아래 호스트에 보내요. 경로는 모두 /v1/ 로 시작하고, 본문은 JSON 이에요.

https://api.brownie.im

서버에서 이벤트 한 건을 보내는 가장 짧은 예예요.

$ curl -X POST https://api.brownie.im/v1/profiles/ext:1234567/events \
    -H "Authorization: Bearer $BROWNIE_SERVER_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name": "purchase", "properties": {"productId": "P-1", "price": 39000},
         "occurredAt": "2026-10-23T10:00:00+09:00"}'

성공하면 201 과 {"success": true, "data": {"duplicatedEvent": false}} 가 돌아와요.

키

키는 두 종류이고, 어디서 부르느냐에 따라 골라 써요. 한 요청에 둘을 함께 실으면 400 EVENT_012 예요.

키헤더쓰는 곳특징
서버 키Authorization: Bearer {키}서비스 서버비공개. 이벤트 · 속성 · 연락처 · 동의 · 탈퇴 · 세그먼트 조회를 모두 할 수 있어요. 키마다 만료 시각과 허용 IP 목록(최대 20개)을 둘 수 있어요.
앱 키X-Brownie-App-Key: {키}앱 · 웹 프런트엔드앱 번들에 들어가도 되는 공개 키예요. 이벤트 · 속성 수집과 세션 · 팝업 기능만 쓸 수 있고, 연락처 · 동의 · 탈퇴는 보낼 수 없어요. 웹에서 부르면 허용한 Origin 만 받아요.
키 발급서버 키는 콘솔에서 발급해요. 원문은 발급 직후 한 번만 보이니 바로 안전한 곳에 저장하세요. 앱 키는 brownie 팀에 요청해 주세요.

키가 없거나, 폐기 · 만료됐거나, 틀리면 이유를 구분하지 않고 401 EVENT_005 를 돌려줘요. 허용 IP 밖이면 403 EVENT_009, 허용하지 않은 Origin 이면 403 EVENT_010 이에요.

고객 식별

brownie 에서 고객 한 명은 프로필이에요. 프로필은 두 가지 값으로 가리킬 수 있어요.

  • externalId — 서비스의 회원 번호예요. 서버에서 보낼 때는 대부분 이 값을 써요. 처음 보는 값이면 프로필을 새로 만들어요.
  • profileId — brownie 가 매긴 UUID 예요. 앱에서 로그인 전 익명 고객을 가리킬 때 써요.

경로에 프로필을 넣는 엔드포인트는 UUID 를 그대로 쓰거나, 회원 번호 앞에 ext: 를 붙여요. 예: /v1/profiles/ext:1234567/events. 본문에 넣는 엔드포인트는 externalId 와 profileId 중 하나만 보내요. 둘 다 오면 externalId 를 먼저 봐요(배치) — 동의 · 탈퇴처럼 정확히 하나만 받는 곳은 400 이에요.

이벤트 보내기

이벤트는 고객이 한 행동이에요. 세그먼트 조건과 캠페인의 반응 조건이 이 이벤트를 써요. 보내는 길은 둘이에요.

  • 한 건씩 — POST /v1/profiles/{profileId}/events. 서버 키 전용이고, 거절되면 상태 코드로 알려 줘요.
  • 묶어서 — POST /v1/batches. 서버 키와 앱 키 모두 써요. 이벤트 · 속성 · 푸시 토큰을 한 요청에 묶고, 202 응답의 rejected 목록이 항목별 거절 이유를 알려 줘요. 나머지 항목은 그대로 저장돼요.

발생 시각 occurredAt

ISO-8601 로 보내요. 오프셋이 없으면 한국 시간으로 읽어요. 받을 수 있는 범위는 전월 1일 00:00부터 지금 + 24시간까지예요. 더 오래됐거나 미래면 그 이벤트를 거절해요.

중복 제거 clientEventId

재전송에 대비해 이벤트마다 고유한 값(64자 이하)을 실어 주세요. clientEventId 와 occurredAt 이 둘 다 같은 이벤트가 다시 오면 저장하지 않고 중복으로 알려 줘요. 서버 키와 앱 키로 보낸 이벤트끼리는 서로 중복으로 보지 않아요. 비워 두면 중복 검사를 하지 않아요.

구매 purchase

이름이 purchase 인 이벤트를 서버 키로 보내면 구매 기록이 따로 남아요. properties 에 productId(문자열)와 price(숫자)가 있어야 하고, currency(기본 KRW)와 quantity(기본 1)는 선택이에요.

속성 · 연락처

배치의 attributes 항목으로 프로필 속성을 정해요. externalId · profileId · phone · email 을 뺀 나머지 키가 모두 속성이에요.

{"attributes": [
  {"externalId": "1234567", "phone": "010-1234-5678", "grade": "VIP", "lastCourseName": null}
]}
  • 값을 null 로 보내면 그 속성을 지워요.
  • 값을 더하거나 빼는 연산은 지원하지 않아요. 계산한 값을 그대로 보내 주세요.
  • phone · email 은 메시지를 보낼 연락처예요. 서버 키로만 보낼 수 있어요. 전화번호는 010-1234-5678 · +821012345678 같은 형식을 받아 국제 형식으로 맞춰요.

광고성 메시지는 고객의 동의를 확인한 뒤에만 나가요. 동의가 바뀔 때마다 서버에서 POST /v1/consent-events 로 알려 주세요(서버 키 전용).

kind의미
ADVERTISING광고성 정보 수신. 광고 푸시는 이 동의만 봐요.
MOBILE_MESSAGE문자 · 카카오 메시지 수신. 광고 문자 · 알림톡은 이 동의와 ADVERTISING 이 모두 있어야 해요.
EMAIL_MESSAGE이메일 수신. 광고 이메일은 이 동의와 ADVERTISING 이 모두 있어야 해요.
NIGHT_ADVERTISING야간(21:00~08:00) 광고 수신
MARKETING_PRIVACY마케팅 목적 개인정보 활용
  • value 는 true(동의) 또는 false(거부 · 철회)예요. 받은 적 없는 동의는 거부로 봐요.
  • changeId 는 변경 한 건마다 고유한 값이에요. 같은 값을 다시 보내면 처음 결과를 그대로 돌려주고, 같은 값에 내용이 다르면 409 예요. 예: 1234567:MOBILE_MESSAGE:20261015100000123
  • 이미 받은 변경보다 이른 시각의 변경은 이력에만 남고 현재 동의는 바뀌지 않아요(applied: false). 순서가 뒤바뀌어 도착해도 안전해요.

앱 연동 흐름

앱은 앱 키로 아래 순서를 따라요.

  1. 세션 시작 — 앱을 열 때 POST /v1/sessions 를 불러요. 처음이면 기기 ID 와 익명 프로필을 발급해 줘요. 응답의 deviceId 를 저장해 두고, 이후 모든 요청에 X-Brownie-Device-Id 로 실어요. 응답의 config 는 전송 주기 · 재시도 간격 같은 앱 설정이에요.
  2. 이벤트 · 속성 수집 — POST /v1/batches 로 묶어 보내요. 대상을 적지 않으면 그 기기의 프로필에 쌓여요.
  3. 로그인 — 고객이 로그인하면 PUT /v1/devices/{deviceId}/profile 에 회원 번호를 보내요. 익명으로 쌓인 기록이 회원 프로필로 이어져요. 응답의 result 는 PROMOTED · MERGED · RELINKED · UNCHANGED 중 하나예요.
  4. 팝업 — 세션 · 배치 응답의 bundle 에 띄울 팝업이 들어 있어요. 띄우면 POST /v1/in-app-messages 로 노출을, 누르거나 닫으면 POST /v1/in-app-messages/{messageSendId}/events 로 반응(CLICK · DISMISS · SNOOZE)을 알려요.
신원 토큰워크스페이스 설정에 따라, 앱이 로그인한 회원의 프로필에 쓸 때 그 회원임을 증명하는 신원 토큰(JWT)을 X-Brownie-Identity 헤더에 실어야 할 수 있어요. 토큰이 없거나 다른 회원의 것이면 401 EVENT_011 이에요.

회원 탈퇴

회원이 탈퇴하면 서버에서 POST /v1/profile-deletions 를 불러요. 부르는 즉시 발송 · 세그먼트 · 수집 대상에서 빠지고, 24시간 뒤 행동 기록 · 연락처 · 속성 · 동의가 지워져요. 되돌릴 수 없어요. 동의 철회를 먼저 보낸 뒤에 부르세요 — 지운 프로필의 동의는 바꿀 수 없어요.

한도

항목한도넘으면
요청 본문1MB413 EVENT_006
배치의 이벤트 · 푸시 토큰각 100건400 GLOBAL_002
배치의 속성 항목75건400 GLOBAL_002
한 요청이 가리키는 서로 다른 프로필75명400 EVENT_004
이벤트 이름 · 속성 키200자그 이벤트 거절
이벤트 속성최상위 키 100개, 중첩 3단계, 이름 포함 16KB그 이벤트 거절

같은 곳에서 짧은 시간에 아주 많은 요청을 보내면 차단될 수 있어요. 가능하면 배치로 묶어 보내 주세요.

응답과 오류

성공 응답은 data 를 감싼 형식이에요. 204 는 본문이 없어요.

{ "success": true, "data": { ... } }

오류는 감싸지 않고 이 형식이에요. code 로 분기하고, message 는 사람이 읽는 설명이에요.

{ "code": "EVENT_005", "message": "호출자를 확인할 수 없습니다.", "response": null }
코드HTTP뜻할 일
GLOBAL_002400요청 값 · 형식 오류message 의 필드를 고쳐요
EVENT_005401쓸 수 없는 키키 · 만료를 확인해요
EVENT_007 · EVENT_013403키 종류가 맞지 않는 경로서버 키 · 앱 키를 바꿔요
EVENT_008400이벤트를 저장할 수 없음response.reason 을 봐요
EVENT_016409동시에 바뀜다시 보내요
EVENT_001 · EVENT_017503일시적인 실패Retry-After 초 뒤 같은 요청을 다시 보내요

요청에 X-Request-Id(영숫자 · ._-, 64자 이하)를 실으면 응답에 같은 값이 돌아와요. 없으면 서버가 만들어 줘요. 문의할 때 이 값을 알려 주시면 빨리 찾을 수 있어요. 전체 오류 코드는 API 레퍼런스의 엔드포인트별 응답에 있어요.