중복 고객 병합
인증된 워크스페이스의 중복 고객을 대표 고객으로 병합합니다. 중복 고객의 식별자, 통화, 상담사 통화, 채팅, 발신·수신 SMS, 메모리, 대기 중인 메모리 이벤트, 상담사 메모(답글·반응 포함), 상태·담당자 이력을 대표 고객으로 옮기고 중복 고객을 영구 삭제합니다. 충돌 시 대표 고객의 프로필을 우선하지만, 이름·메모가 비어 있거나 속성 값이 없으면 중복 고객의 값으로 채웁니다. 병합 후 메모리 저장 한도를 다시 적용합니다. main_customer_id와 duplicate_customer_id는 달라야 하며, 같으면 400 INVALID_MERGE를 반환합니다. 두 고객 모두 워크스페이스에 존재해야 하며, 아니면 404 CUSTOMER_NOT_FOUND를 반환합니다. 전체 작업은 하나의 트랜잭션으로 실행하며 실패하면 모두 되돌립니다.
curl --request POST \
--url https://client-api.tryvox.co/v3/customers/merge \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"main_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"duplicate_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://client-api.tryvox.co/v3/customers/merge"
payload = {
"main_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"duplicate_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
main_customer_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
duplicate_customer_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://client-api.tryvox.co/v3/customers/merge', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://client-api.tryvox.co/v3/customers/merge",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'main_customer_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'duplicate_customer_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://client-api.tryvox.co/v3/customers/merge"
payload := strings.NewReader("{\n \"main_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"duplicate_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://client-api.tryvox.co/v3/customers/merge")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"main_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"duplicate_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://client-api.tryvox.co/v3/customers/merge")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"main_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"duplicate_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"attributes": {},
"created_at": 123,
"updated_at": 123,
"identifiers": [
{
"id": "<string>",
"identifier_type": "<string>",
"identifier_value": "<string>",
"created_at": 123
}
],
"metadata": {},
"interaction_summary": {
"interaction_count": 123,
"first_interaction_at": 123,
"last_interaction_at": 123,
"last_interaction_id": "<string>",
"last_interaction_type": "<string>"
},
"name": "<string>",
"note": "<string>"
}{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": {
"field": "name",
"reason": "must not be blank"
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication is required.",
"details": {}
}
}{
"error": {
"code": "FORBIDDEN",
"message": "Permission denied.",
"details": {}
}
}{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found.",
"details": {
"resource": "agent"
}
}
}{
"error": {
"code": "CONFLICT",
"message": "The requested operation conflicts with current state.",
"details": {
"current_status": "draft"
}
}
}{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests.",
"details": {
"limit": 10,
"window_seconds": 1,
"scope": "user"
}
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error.",
"details": {}
}
}{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable.",
"details": {}
}
}Authorizations
워크스페이스 API 키를 Authorization: Bearer <token> 형식으로 보냅니다.
Body
병합할 기준 고객과 중복 고객의 UUID입니다.
Response
성공 응답
식별자 검색, 생성, 수정, 병합 경로에서 반환하는 고객 응답입니다.
메모리는 고객 단건 조회가 반환하는 CustomerDetailResponse에만 있습니다.
고객 UUID입니다.
자동 추출한 고객 속성 값입니다. 이름에 해당하는 정의가 없어도 저장된 값을 유지합니다.
리소스 생성 시각입니다. 밀리초 단위 unix timestamp입니다.
리소스 수정 시각입니다. 밀리초 단위 unix timestamp입니다.
고객 신원 식별자 목록입니다.
Show child attributes
Show child attributes
고객 메타데이터입니다.
통화, 채팅, SMS 인터랙션의 집계 요약입니다.
Show child attributes
Show child attributes
고객 이름입니다.
고객 메모입니다.
Was this page helpful?
curl --request POST \
--url https://client-api.tryvox.co/v3/customers/merge \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"main_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"duplicate_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://client-api.tryvox.co/v3/customers/merge"
payload = {
"main_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"duplicate_customer_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
main_customer_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
duplicate_customer_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://client-api.tryvox.co/v3/customers/merge', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://client-api.tryvox.co/v3/customers/merge",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'main_customer_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'duplicate_customer_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://client-api.tryvox.co/v3/customers/merge"
payload := strings.NewReader("{\n \"main_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"duplicate_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://client-api.tryvox.co/v3/customers/merge")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"main_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"duplicate_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://client-api.tryvox.co/v3/customers/merge")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"main_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"duplicate_customer_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"attributes": {},
"created_at": 123,
"updated_at": 123,
"identifiers": [
{
"id": "<string>",
"identifier_type": "<string>",
"identifier_value": "<string>",
"created_at": 123
}
],
"metadata": {},
"interaction_summary": {
"interaction_count": 123,
"first_interaction_at": 123,
"last_interaction_at": 123,
"last_interaction_id": "<string>",
"last_interaction_type": "<string>"
},
"name": "<string>",
"note": "<string>"
}{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": {
"field": "name",
"reason": "must not be blank"
}
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication is required.",
"details": {}
}
}{
"error": {
"code": "FORBIDDEN",
"message": "Permission denied.",
"details": {}
}
}{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Resource not found.",
"details": {
"resource": "agent"
}
}
}{
"error": {
"code": "CONFLICT",
"message": "The requested operation conflicts with current state.",
"details": {
"current_status": "draft"
}
}
}{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests.",
"details": {
"limit": 10,
"window_seconds": 1,
"scope": "user"
}
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "Internal server error.",
"details": {}
}
}{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "Service temporarily unavailable.",
"details": {}
}
}