개발자 가이드
방문자 식별 API
로그인한 사용자를 코드로 식별해 이메일 입력 요청을 건너뛰고, 대화를 연락처에 자동으로 연결하세요.
언제 쓰나요
웹사이트에 로그인 사용자가 있다면(SaaS 앱, 대시보드, 회원 포털 등) 이미 누구인지 알고 있어요. identify 명령으로 이메일을 Sonny에 넘기면 사용자가 다시 입력할 필요가 없어요.
- 로그인한 사용자는 이메일 입력 요청을 완전히 건너뛰어요
- 채팅 대화를 연락처 기록에 자동으로 연결해요
- 받은편지함에서 방문자의 이름과 이메일을 바로 확인할 수 있어요
- 인증된 신원으로 브라우저와 기기를 바꿔도 기록이 이어져요
기기 간 연속성
인증된 방문자 신원
기본 이메일 식별은 편리하지만 브라우저는 어떤 이메일이든 주장할 수 있어요. 인증된 신원은 서버가 서명한 짧은 수명의 JWT를 더해요. 그러면 Sonny가 고객의 연락처를 웹사이트 채팅 기록의 소유자로 안전하게 사용할 수 있어, 다른 브라우저나 기기에서도 같은 대화가 나타나요.
꺼짐
기본값이에요. 기존 삽입 코드나 방문자에게 바뀌는 것이 없고, 기록은 브라우저의 방문자 ID에 묶여 있어요.
선택적 인증 (권장)
유효한 JWT가 있으면 기기 간 기록을 쓸 수 있어요. JWT가 없는 방문자는 기존의 익명 또는 이메일 식별 방식을 그대로 써요.
신원 확인에 증명 필요
익명 채팅은 계속 작동하지만, identify와 사용자 지정 속성 호출에는 유효한 서명된 JWT가 필요해요.
인증 모드가 필수 채팅 전 필드를 선택 사항으로 바꾸지는 않아요. 각 모드가 필수 이메일을 어떻게 충족하는지는 필수 채팅 전 정보를 참고하세요.
1. 서명 시크릿 생성
채널 → 내 채널 → 실시간 채팅을 열고 보안 방문자 신원을 찾아 모드를 고른 뒤 시크릿을 생성하세요. 원문은 한 번만 표시돼요. 백엔드의 시크릿 관리자에 SONNY_IDENTITY_SECRET으로 저장하세요. 서명 시크릿을 브라우저 코드, 삽입 스니펫, 공개 환경 변수, 소스 저장소에 절대 넣지 마세요.
2. 백엔드에서 짧은 수명의 JWT 발급
HS256으로 서명하세요. Sonny는 user_id, email, iat, exp를 요구해요. user_id는 이메일 주소가 아니라 내 데이터베이스의 고정 ID여야 해요. 토큰 수명은 최대 24시간이며, 15분이 적당한 기본값이에요. 시계는 최대 60초까지 차이가 나도 돼요.
merchantName, subdomain, plan, platform, role 같은 신뢰할 수 있는 고객 지원 정보를 위해 서버가 서명한 traits 객체도 넣을 수 있어요. 특성 값은 문자열, 유한한 숫자, 불리언, null이어야 해요. Sonny는 이를 웹사이트 범위의 인증된 연락처 신원에 저장해요. 브라우저가 보낸 속성은 미인증 상태로 남고 서명된 특성으로 승격되지 않아요.
import { SignJWT } from 'jose';
export async function createSonnyIdentityToken(user) {
const secret = new TextEncoder().encode(process.env.SONNY_IDENTITY_SECRET);
return new SignJWT({
user_id: String(user.id),
email: user.email,
traits: {
merchantName: user.merchant.name,
subdomain: user.merchant.subdomain,
plan: user.merchant.plan,
platform: user.merchant.platform,
role: user.role
}
})
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('15m')
.sign(secret);
}require 'jwt'
def sonny_identity_token(user)
now = Time.now.to_i
payload = {
user_id: user.id.to_s,
email: user.email,
traits: {
merchantName: user.merchant.name,
subdomain: user.merchant.subdomain,
plan: user.merchant.plan,
platform: user.merchant.platform,
role: user.role
},
iat: now,
exp: now + 15 * 60
}
JWT.encode(payload, ENV.fetch('SONNY_IDENTITY_SECRET'), 'HS256')
endimport os
from datetime import datetime, timedelta, timezone
import jwt
def sonny_identity_token(user):
now = datetime.now(timezone.utc)
return jwt.encode({
"user_id": str(user.id),
"email": user.email,
"traits": {
"merchantName": user.merchant.name,
"subdomain": user.merchant.subdomain,
"plan": user.merchant.plan,
"platform": user.merchant.platform,
"role": user.role,
},
"iat": now,
"exp": now + timedelta(minutes=15),
}, os.environ["SONNY_IDENTITY_SECRET"], algorithm="HS256")use Firebase\JWT\JWT;
function sonnyIdentityToken($user): string {
$now = time();
return JWT::encode([
'user_id' => (string) $user->id,
'email' => $user->email,
'traits' => [
'merchantName' => $user->merchant->name,
'subdomain' => $user->merchant->subdomain,
'plan' => $user->merchant->plan,
'platform' => $user->merchant->platform,
'role' => $user->role,
],
'iat' => $now,
'exp' => $now + (15 * 60),
], $_ENV['SONNY_IDENTITY_SECRET'], 'HS256');
}3. 위젯에는 JWT만 전달
일반 애플리케이션 세션으로 보호되는 엔드포인트에서 JWT를 반환하세요. init 전후에 identify를 호출하세요. 위젯은 JWT를 메모리에만 보관하며 localStorage, 쿠키, URL에 저장하지 않아요.
// Fetch a short-lived JWT from your authenticated backend.
// Your signing secret never reaches this code.
const { userJwt } = await fetch('/api/sonny-identity').then((res) => res.json());
sonny('identify', { userJwt });
sonny('init', { siteId: 'YOUR_SITE_ID' });
// Sonny asks for a fresh token after expiry or an emergency key change.
window.addEventListener('sonny:identity-required', async () => {
const { userJwt } = await fetch('/api/sonny-identity').then((res) => res.json());
sonny('identify', { userJwt });
});갱신, 로그아웃, 교체를 안전하게
- 갱신: JWT가 만료되기 전에 갱신하세요(15분짜리 토큰이라면 약 10분 후). 그다음
sonny('identify', { userJwt })을 호출하세요. 만료나 서명 키 변경에 대비해sonny:identity-required도 수신하세요. - 로그아웃:
sonny('reset')을 호출하세요. 메모리의 JWT를 버리고, 브라우저의 위젯 세션을 지우고, 새 익명 방문자로 시작해요. - 계정 전환: 사용자 B의 JWT를 가져와
sonny('identify', { userJwt })을 호출하기 전에 사용자 A에 대해sonny('reset')을 호출하세요. 한 사용자의 토큰을 다른 로그인 세션으로 넘기지 마세요. - 정기 교체: Sonny는 이전 시크릿을 24시간 동안 함께 허용하므로 모든 백엔드 인스턴스를 업데이트할 시간이 있어요. 이 겹치는 기간이 끝날 때까지는 다음 정기 교체를 할 수 없어요.
- 시크릿 유출이 의심될 때: 즉시 바꾸기를 선택하세요. 이전 서명 키 두 개가 한꺼번에 새 요청 인증을 멈추고, 연결된 위젯은 호스트 페이지에 새 JWT를 요청해요.
- 인증 끄기: Sonny가 현재와 이전 서명 시크릿을 폐기하고 인증된 위젯의 연결을 끊어요. 인증을 다시 켜기 전에 새 시크릿을 생성하세요.
신원 충돌은 자동으로 병합되지 않아요
고정 사용자 ID와 이메일이 서로 다른 Sonny 연락처를 가리키면, 고객 기록을 몰래 합치지 않고 인증이 충돌을 반환해요. 토큰을 고치거나 Sonny에서 연락처를 병합한 뒤 다시 시도하세요.
예전에 서명 없는 이메일로만 연결된 채팅은 자동으로 인증된 기록으로 취급되지 않아요. 그래서 브라우저가 보낸 이메일로 다른 고객의 대화에 접근할 수 없어요.
도움말 센터 로그인
기존 고객 로그인을 호스팅 또는 사용자 지정 도메인 도움말 페이지에 연결하세요. 위젯과 같은 웹사이트 서명 시크릿과 짧은 수명의 JWT를 쓰세요. 기본 이메일 식별과 Sonny 직원 로그인으로는 인증된 독자 접근 권한을 얻을 수 없어요.
- 이 채널에 대해 위의 인증된 방문자 신원 설정을 마치세요. 채널 → 내 채널 → 실시간 채팅에서 선택적 인증 또는 신원 확인에 증명 필요를 켜세요.
- 앱에
{ "userJwt": "SIGNED_TOKEN" }를 반환하는 인증된 엔드포인트를 구현하세요. 로그인한 고객과 신뢰할 수 있는 특성으로 서버에서 토큰을 발급하고, 브라우저가 요청한 플랜이나 역할은 절대 받아들이지 마세요. - 채널 → 내 채널 → 도움말 센터 → 도움말 센터 접근 권한에서 인증된 고객과 선택적으로 고객 대상 그룹을 고르세요. 고객 로그인 URL에 앱의 로그인 페이지를 입력하고 접근 권한 저장을 선택하세요.
- 고객이 로그인한 뒤 아래 교환을 실행하세요. YOUR_SLUG는 도움말 센터 주소로 바꾸세요. 로그인 URL만으로는 부족하며, 앱이 이 교환을 완료해야 해요.
// Run after your application authenticates the customer.
// This endpoint must use the server's session and trusted customer traits.
const response = await fetch('/api/sonny-identity');
if (!response.ok) throw new Error('Unable to sign in to the help center');
const { userJwt } = await response.json();
const signIn = new URL('https://www.usesonny.com/help/YOUR_SLUG/auth');
signIn.searchParams.set('token', userJwt);
signIn.searchParams.set('return', '/help/YOUR_SLUG');
window.location.assign(signIn);사용자 지정 도메인이라면 signIn을 https://help.example.com/auth로 설정하고 /나 /articles/getting-started로 돌아가게 하세요. 호스팅 페이지의 반환 경로는 /help/YOUR_SLUG로 시작해요. 반환 대상은 같은 호스트여야 해요.
교환은 안전한 HTTP-only 쿠키를 설정하고 리디렉션해 URL에서 토큰을 제거해요. 독자 세션은 JWT와 함께, 최대 24시간 후 만료돼요. 짧은 수명의 토큰을 쓰고 교환 URL을 로그에 남기지 마세요. JWT는 이 로그인 교환에만 쓰고, 공유하는 아티클 링크에는 넣지 마세요.
로그인과 접근 테스트
- 로그아웃 상태로 비공개 센터를 방문하세요. 로그인 버튼을 따라 앱에 로그인한 뒤, 같은 호스팅 또는 사용자 지정 도메인의 도움말 센터로 돌아오는지 확인하세요.
- 허용된 아티클 하나와 거부된 아티클 하나를 둘러보기, 검색, 직접 URL로 확인하세요. 센터 대상 그룹에 속하지 않은 고객은 접근 안내 메시지를 보고, 거부된 아티클은 찾을 수 없음을 반환해요.
- 도움말 센터에서 로그아웃을 쓰고 비공개 콘텐츠가 사라지는지 확인하세요. 특성이 다른 고객으로 반복하세요.
갱신과 로그아웃
위젯은 현재 JWT를 자동으로 보내요. Sonny가 요청하면 갱신하고, 고객이 로그아웃하면 sonny('reset')을 호출하세요. 위젯 신원과 도움말 센터 쿠키는 별개라서, 위젯을 초기화해도 호스팅 도움말 센터 세션은 로그아웃되지 않아요.
호스팅 페이지에서 로그아웃하려면 /help/YOUR_SLUG/auth/logout으로 이동하고, 사용자 지정 도메인에서는 /auth/logout을 쓰세요. 두 세션을 모두 끝내야 한다면 앱의 로그아웃 흐름에 이 동작을 연결하세요. 잘못되었거나 만료된 자격 증명은 방문자 권한을 가져요. 신원 인증이 꺼져 있으면 모든 독자가 방문자 권한을 가져요.
고객 특성이 바뀌면 새 JWT를 발급해 위젯에서 다시 식별하고, 호스팅 페이지는 로그인 교환을 다시 하세요. 기존 독자 쿠키는 만료될 때까지 이전에 서명된 특성을 유지해요. 대상 그룹 규칙 변경은 다음 요청부터 적용돼요.
대상 그룹 만들기, 접근 미리보기, 제한 문제 해결이메일 식별은 인증이 아니에요
브라우저가 보내는 이메일 방식은 고객 지원 맥락을 풍부하게 해 주지만 방문자를 인증하지는 않아요. 방문자는 자기 페이지에서 JavaScript를 확인하고 실행할 수 있으므로, 이메일, 이름, 사용자 지정 속성으로는 다른 고객의 Sonny 기록에 접근할 수 없어요. 인증된 신원에는 아래 설명한 서버 서명 JWT가 필요해요. 내 제품에서의 작업 권한은 로그인된 애플리케이션 안에서 관리하세요.
방문자 인증이 켜져 있으면 받은편지함의 인증됨 배지는 서버가 서명한 JWT로 대화가 연결됐다는 뜻이에요. 미인증 배지는 서버가 서명한 신원으로 대화가 인증되지 않았다는 뜻이에요. 표시된 이름이나 이메일은 방문자가 제공한 참고 정보일 뿐 신원 증명이 아니에요.
코드 예시
가장 간단한 호출이에요. 사용자의 이메일만 넘기세요.
// Identify a logged-in user
sonny('identify', {
email: 'jane@example.com'
});상담원이 받은편지함에서 볼 수 있도록 사용자 이름도 넘기세요.
// Identify with full name
sonny('identify', {
email: 'jane@example.com',
name: 'Jane Smith'
});식별할 때 유용한 고객 지원 정보를 추가하고, 나중에 업데이트하거나, 페이지가 열려 있는 동안 바뀌는 값을 감시하세요.
// Identify with support context
sonny('identify', {
email: 'jane@example.com',
name: 'Jane Smith',
attributes: {
plan: 'starter',
seats: 5,
trial: true
}
});
// Update only the values that changed
sonny('setAttributes', {
plan: 'growth',
seats: 8,
trial: null // Clears this property
});
// Keep a changing value in sync (checks every 10 seconds)
sonny('watchAttributes', () => ({
monthly_usage: window.currentUsage
}), { interval: 10000 });비동기 스니펫을 사용한 전체 예시예요. identify는 init 전에 호출해도 돼요. 신원이 대기열에 들어갔다가 위젯이 연결되자마자 전송돼요.
<!-- Sonny widget snippet -->
<script>
(function(w,d,s,o,f,js,fjs){
w['Sonny']=o;w[o]=w[o]||function(){
(w[o].q=w[o].q||[]).push(arguments)};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
})(window,document,'script','sonny','https://www.usesonny.com/widget.js');
// Identify before init — identity is queued and sent on connect
sonny('identify', {
email: 'jane@example.com',
name: 'Jane Smith'
});
sonny('init', { siteId: 'YOUR_SITE_ID' });
</script>사용자가 로그아웃하면 reset을 호출해 신원을 지우고 새 세션을 시작하세요.
// Reset on logout — clears identity and starts a fresh session
sonny('reset');작동 방식
- 01
위젯 로드 및 연결
위젯이 Sonny에 연결되면서 저장된 신원을 함께 보내요.
- 02
신원을 서버로 전송
Sonny가 워크스페이스에서 그 이메일의 연락처를 찾고, 없으면 새로 만들어요.
- 03
대화 연결
현재 브라우저 방문자 세션의 연결되지 않은 대화가 식별된 연락처에 연결돼요. 식별만으로는 브라우저나 기기 간 기록이 병합되지 않아요.
- 04
이메일 입력 요청 생략
방문자가 이미 식별됐으므로 위젯 안의 이메일 입력 요청이 나타나지 않아요. 사용자를 방해하지 않아요.
사용자 지정 속성 규칙
- 호출당 속성은 최대 50개까지 보내세요.
- 값은 문자열, 숫자, 불리언, null일 수 있어요. 문자열은 최대 1000자예요. 저장된 값을 지우려면 null이나 빈 문자열을 넘기세요.
- 키는 문자로 시작하고 문자, 숫자, 밑줄만 포함해야 하며 최대 64자예요.
- 다음 키는 예약되어 무시돼요:
email, name, id, phone, createdAt, updatedAt.
API 레퍼런스
sonny('identify', { email, name?, attributes? })- 현재 방문자를 식별해요. 이메일과 선택적으로 이름을 설정하고, 이메일 입력 요청을 건너뛰고, 신원을 서버로 보내요. init 전후 언제든 호출할 수 있어요.
emailstring방문자의 이메일 주소namestring?방문자의 표시 이름attributesobject?연락처에 붙일 사용자 지정 속성(키와 값 규칙은 위젯 설정 가이드 참고)
sonny('identify', { userJwt, name?, attributes? })- 서버가 생성한 JWT로 현재 로그인한 고객을 안전하게 식별해요. 토큰은 메모리에만 머물며 인증된 요청이나 소켓 페이로드로만 전송돼요.
userJwtstring인증된 백엔드에서 새로 발급한 HS256 JWTnamestring?방문자의 표시 이름attributesobject?인증된 연락처에 붙일 사용자 지정 속성
sonny('setAttributes', { ... })- 식별된 방문자의 사용자 지정 속성을 업데이트해요. 바뀐 값만 전송돼요. 방문자가 아직 식별되지 않았다면 업데이트는 대기했다가 identify가 실행된 뒤 전송돼요.
attributesobject설정할 키-값 쌍. 속성을 지우려면 값으로 null을 넘기세요.
sonny('watchAttributes', getter, { interval? })- 타이머로 getter 함수를 호출하고 바뀐 속성을 자동으로 동기화해요. 페이지가 열려 있는 동안 플랜이나 사용량 같은 값이 바뀔 때 유용해요.
getterfunction현재 속성 객체를 반환하는 함수intervalnumber?확인 주기(밀리초). 기본값 10000, 최솟값 2000.
sonny('reset')- 현재 브라우저의 위젯 ID, 이메일, 이름, 로컬 대화 기록을 지우고 새 방문자 세션을 시작해요. Sonny의 연락처나 저장된 속성은 삭제하지 않아요. 로그아웃할 때 쓰세요.
도움이 필요하신가요?
위젯 설정 가이드를 확인하거나 저희 팀에 문의하세요.
관련 문서
- 위젯 설정
스크립트 태그 하나로 웹사이트에 Sonny 실시간 채팅 위젯을 설치하세요.
- 위젯 맞춤 설정
색상, 로고, 메시지, 동작으로 위젯을 브랜드에 맞추세요.
- 연락처
Sonny에서 연락처가 어떻게 생성, 관리, 태그 지정, 병합되는지 알아보세요.
- 채팅 기록
위젯이 재방문자를 어떻게 기억하고 이전 메시지를 다시 불러오는지 알아보세요.