다중 계약 웹사이트 리드 수집
이 문서는 사이트 백엔드가 리드 데이터, 방문자의 연락처 세부 정보 및 마케팅 UTM 메트릭을 하나의 요청으로 ReactiveCRM에 전송하는 단일 API 계약을 설명합니다.
1. 목적
사이트 통합자는 연락처, 전화번호, 이메일, 리드 및 마케팅 이벤트를 별도로 생성할 필요가 없습니다. 하나의 요청이 원자적으로 다음을 생성해야 합니다:
- 연락처;
- 연락처의 기본 전화번호;
- 제공된 경우 연락처의 기본 이메일;
- 리드;
- 연락처와 리드 간의 연결;
- 마케팅 이벤트 및 UTM 속성.
어느 단계라도 실패하면 전체 요청이 롤백되고 부분적으로 생성된 데이터는 저장되지 않습니다.
2. 엔드포인트
POST /api/site/leads
Authorization: Bearer <CRM_ACCESS_TOKEN>
Content-Type: application/json
토큰은 사이트 백엔드 또는 서버리스 함수에서만 전달되어야 합니다. CRM 토큰을 브라우저 JavaScript에 절대 배치하지 마세요.
테넌트는 인증 토큰에서 결정됩니다. 제공된 모든 UUID는 이 테넌트 내에서 검증됩니다.
3. 전체 JSON 계약
{
"externalId": "site-form-01J7K9A2M4Y5T6",
"lead": {
"leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47",
"quality": "WARM",
"message": "CRM 구현에 대한 상담을 원합니다"
},
"contact": {
"firstName": "이반",
"lastName": "이바노프",
"phone": "+79991234567",
"email": "ivan@example.com"
},
"marketing": {
"occurredAt": "2026-09-06T18:00:00Z",
"visitorId": "visitor-8b7f",
"sessionId": "session-31ac",
"source": "website",
"channel": "paid",
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "summer-consulting",
"utmContent": "banner-a",
"utmTerm": "crm consultation",
"gclid": "EAIaIQobChMI-example",
"fbclid": null,
"landingUrl": "https://example.com/consultation",
"referrer": "https://www.google.com/"
}
}
4. 요청 필드
4.1. 루트 필드
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
externalId | 문자열 | 권장 | 멱등성을 위한 양식 제출의 안정적인 고유 ID |
lead | 객체 | 예 | 생성 중인 리드의 데이터 |
contact | 객체 | 예 | 연락처 담당자 데이터 |
marketing | 객체 | 아니오 | 마케팅 방문의 UTM 메트릭 및 기술 데이터 |
externalId는 첫 번째 전송 시도 전에 생성됩니다. 시간 초과 또는 네트워크 오류 후 요청을 반복할 때 동일한 값을 사용하세요.
4.2. lead 객체
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
leadSourceId | uuid | 예 | 현재 테넌트의 리드 소스 ID |
quality | 문자열 | 아니오 | 초기 평가: HOT, WARM, COOL 또는 COLD |
message | 문자열 | 아니오 | 방문자의 메시지 또는 제출에 대한 설명 |
사이트가 사전 평가를 수행하지 않는 경우 quality 필드를 보내지 않는 것이 좋습니다.
4.3. contact 객체
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
firstName | 문자열 | 예 | 연락처 담당자의 이름 |
lastName | 문자열 | 아니오 | 연락처 담당자의 성 |
phone | 문자열 | 예 | 기본 전화번호; E.164 형식 권장 |
email | 문자열(이메일) | 아니오 | 연락처 담당자의 기본 이메일 |
제출을 처리하는 데 필요한 최소 데이터는 firstName과 phone입니다.
4.4. marketing 객체
| 필드 | 유형 | 필수 | 설명 |
|---|---|---|---|
occurredAt | 날짜-시간 | 아니오 | 양식 제출 시간; 생략 시 CRM 시간 사용 |
visitorId | 문자열 | 아니오 | 익명 방문자 ID |
sessionId | 문자열 | 아니오 | 사이트 세션 ID |
source | 문자열 | 아니오 | 이벤트 소스, 기본값은 website |
channel | 문자열 | 아니오 | 채널: 예: paid, organic, social, email, direct |
utmSource | 문자열 | 아니오 | utm_source 값 |
utmMedium | 문자열 | 아니오 | utm_medium 값 |
utmCampaign | 문자열 | 아니오 | utm_campaign 값 |
utmContent | 문자열 | 아니오 | utm_content 값 |
utmTerm | 문자열 | 아니오 | utm_term 값 |
gclid | 문자열 | 아니오 | Google 클릭 ID |
fbclid | 문자열 | 아니오 | Meta/Facebook 클릭 ID |
landingUrl | 문자열 | 아니오 | 랜딩 페이지 URL |
referrer | 문자열 | 아니오 | 이전 페이지 URL |
개인 데이터는 마케팅 객체에서 중복되어서는 안 됩니다. 이름, 전화번호, 이메일 및 메시지 텍스트는 contact 및 lead에만 전달됩니다.
5. 최소 요청
{
"externalId": "site-form-550e8400-e29b-41d4-a716-446655440000",
"lead": {
"leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47"
},
"contact": {
"firstName": "이반",
"phone": "+79991234567"
}
}
6. 성공 응답
첫 번째 요청 — 201 Created
{
"leadId": "22222222-2222-2222-2222-222222222222",
"contactId": "33333333-3333-3333-3333-333333333333",
"phoneId": "44444444-4444-4444-4444-444444444444",
"emailId": "55555555-5555-5555-5555-555555555555",
"marketingEventId": "66666666-6666-6666-6666-666666666666",
"duplicate": false,
"createdAt": "2026-09-06T18:00:01Z"
}
이메일 또는 마케팅 데이터가 제공되지 않으면 해당 ID는 null로 반환됩니다.
동일한 externalId로 반복 — 200 OK
{
"leadId": "22222222-2222-2222-2222-222222222222",
"contactId": "33333333-3333-3333-3333-333333333333",
"phoneId": "44444444-4444-4444-4444-444444444444",
"emailId": "55555555-5555-5555-5555-555555555555",
"marketingEventId": "66666666-6666-6666-6666-666666666666",
"duplicate": true,
"createdAt": "2026-09-06T18:00:01Z"
}
반복 요청은 두 번째 리드나 연락처를 생성해서는 안 됩니다.
7. cURL 예제
curl -X POST "$CRM_BASE_URL/api/site/leads" \
-H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalId": "site-form-01J7K9A2M4Y5T6",
"lead": {
"leadSourceId": "7b33b18f-55bd-48a5-a8f0-e450a56dde47",
"quality": "WARM",
"message": "전화 주세요"
},
"contact": {
"firstName": "이반",
"lastName": "이바노프",
"phone": "+79991234567",
"email": "ivan@example.com"
},
"marketing": {
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "crm-demo",
"landingUrl": "https://example.com/demo"
}
}'
8. TypeScript 인터페이스
type LeadQuality = 'HOT' | 'WARM' | 'COOL' | 'COLD';
interface SiteLeadRequest {
externalId?: string;
lead: {
leadSourceId: string;
quality?: LeadQuality;
message?: string;
};
contact: {
firstName: string;
lastName?: string;
phone: string;
email?: string;
};
marketing?: {
occurredAt?: string;
visitorId?: string;
sessionId?: string;
source?: string;
channel?: string;
utmSource?: string;
utmMedium?: string;
utmCampaign?: string;
utmContent?: string;
utmTerm?: string;
gclid?: string;
fbclid?: string;
landingUrl?: string;
referrer?: string;
};
}
interface SiteLeadResponse {
leadId: string;
contactId: string;
phoneId: string;
emailId: string | null;
marketingEventId: string | null;
duplicate: boolean;
createdAt: string;
}
9. 서버 측 예제
export async function sendLeadToCrm(payload: SiteLeadRequest): Promise<SiteLeadResponse> {
const response = await fetch(`${process.env.CRM_BASE_URL}/api/site/leads`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRM_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const body = await response.text();
throw new Error(`ReactiveCRM returned ${response.status}: ${body}`);
}
return response.json() as Promise<SiteLeadResponse>;
}
10. 오류
| HTTP | 원인 | 통합자 조치 |
|---|---|---|
400 | 형식 오류, 필수 필드 누락, 잘못된 이메일/전화번호/quality | 데이터 수정; 변경 없이 자동 재시도 금지 |
401 | 토큰 누락 또는 유효하지 않음 | 통합 토큰 갱신 |
403 | 테넌트 또는 작업에 대한 액세스 권한 없음 | 통합 사용자 및 역할 확인 |
404 | 현재 테넌트에서 leadSourceId를 찾을 수 없음 | 사이트 설정에서 소스 ID 업데이트 |
409 | externalId가 이미 호환되지 않는 요청에 연결됨 | ID 생성 및 통합 로그 확인 |
5xx | 일시적인 CRM 오류 | 동일한 externalId로 동일한 요청 재시도 |
검증 오류 예시:
{
"status": 400,
"error": "Bad Request",
"message": "contact.phone must not be blank",
"path": "/api/site/leads"
}
11. 멱등성
externalId는 테넌트 및website소스 내에서 고유해야 합니다.- 첫 번째 요청 전에
externalId를 생성하고 사이트 제출에 저장하세요. - 시간 초과 시 동일한 본문과 동일한
externalId로 요청을 재시도하세요. - 단일 제출을 재시도할 때 새
externalId를 생성하지 마세요. duplicate: true인 경우 제출이 이미 처리되었으며 성공적으로 전달된 것으로 간주됩니다.
12. 사이트 개발자용 미니 가이드
- 첫 방문 시 UTM 태그,
gclid,fbclid, 랜딩 URL 및 리퍼러를 저장하세요. - 양식이 제출되면 안정적인
externalId를 생성하세요. - 양식을 사이트 백엔드로 전송하세요.
- 백엔드가 CRM 토큰을 추가하고
POST /api/site/leads를 호출합니다. 201또는200과duplicate: true인 경우 제출이 전달된 것으로 간주합니다.- 시간 초과 또는
5xx발생 시 동일한externalId로 요청을 재시도하세요. - CRM 토큰을 브라우저에서 직접 전송하지 마세요.
13. CRM 관리자가 얻게 되는 것
성공적인 요청 후 CRM에는 다음 리드가 포함됩니다:
leadSourceId소스가 설정됨;- 방문자의 메시지를 보관함;
- 사이트가 제공한 경우 초기 품질 평가를 가짐;
- 이름과 기본 전화번호가 있는 연락처에 연결됨;
- 제공된 경우 기본 이메일에 연결됨;
- 분석을 위한 UTM 메트릭 및 광고 식별자를 보관함.
이 세트는 관리자가 제출을 확인하고, 연락처에 전화하고, 리드를 평가하기에 충분합니다.
Reactive CRM