Merge sessions
curl --request POST \
--url http://localhost:8080/chat/sessions/{session_id}/merge \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"sources": [
{
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"idempotencyKey": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "http://localhost:8080/chat/sessions/{session_id}/merge"
payload = {
"sources": [{ "sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a" }],
"idempotencyKey": "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({
sources: [{sessionId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'}],
idempotencyKey: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('http://localhost:8080/chat/sessions/{session_id}/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_PORT => "8080",
CURLOPT_URL => "http://localhost:8080/chat/sessions/{session_id}/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([
'sources' => [
[
'sessionId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]
],
'idempotencyKey' => '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 := "http://localhost:8080/chat/sessions/{session_id}/merge"
payload := strings.NewReader("{\n \"sources\": [\n {\n \"sessionId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n ],\n \"idempotencyKey\": \"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("http://localhost:8080/chat/sessions/{session_id}/merge")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"sources\": [\n {\n \"sessionId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n ],\n \"idempotencyKey\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:8080/chat/sessions/{session_id}/merge")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sources\": [\n {\n \"sessionId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n ],\n \"idempotencyKey\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"mergeId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"primarySessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"sources": [
{
"id": "<string>",
"ticket_number": 123,
"main_status": "closed_resolved"
}
]
}{
"statusCode": 123,
"message": "<string>",
"error": "<string>"
}Sessions
Merge
Permanently merge source sessions into a primary while keeping original histories separate. Requires merge-sessions:write and OpenCX ticketing.
POST
/
chat
/
sessions
/
{session_id}
/
merge
Merge sessions
curl --request POST \
--url http://localhost:8080/chat/sessions/{session_id}/merge \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"sources": [
{
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"idempotencyKey": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "http://localhost:8080/chat/sessions/{session_id}/merge"
payload = {
"sources": [{ "sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a" }],
"idempotencyKey": "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({
sources: [{sessionId: '3c90c3cc-0d44-4b50-8888-8dd25736052a'}],
idempotencyKey: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('http://localhost:8080/chat/sessions/{session_id}/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_PORT => "8080",
CURLOPT_URL => "http://localhost:8080/chat/sessions/{session_id}/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([
'sources' => [
[
'sessionId' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]
],
'idempotencyKey' => '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 := "http://localhost:8080/chat/sessions/{session_id}/merge"
payload := strings.NewReader("{\n \"sources\": [\n {\n \"sessionId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n ],\n \"idempotencyKey\": \"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("http://localhost:8080/chat/sessions/{session_id}/merge")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"sources\": [\n {\n \"sessionId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n ],\n \"idempotencyKey\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://localhost:8080/chat/sessions/{session_id}/merge")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sources\": [\n {\n \"sessionId\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n }\n ],\n \"idempotencyKey\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"mergeId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"primarySessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"sources": [
{
"id": "<string>",
"ticket_number": 123,
"main_status": "closed_resolved"
}
]
}{
"statusCode": 123,
"message": "<string>",
"error": "<string>"
}Merging is irreversible. Sources are permanently closed and cannot be reopened or replied to. Continue the merged work through the primary session’s channel and recipients, including when contacts differ.
merge-sessions:write. The organization’s ticketing system must be open. API callers execute directly; there is no acknowledgment field.
Choose closed_resolved or closed_unresolved for each source. Every source receives closed_by_merge and becomes a subsession of the primary. The primary retains its status, assignee, contact, channel, fields, and tags. Internal merge entries link the original histories; messages are not interleaved chronologically.
{
"idempotencyKey": "00000000-0000-4000-8000-000000000001",
"sources": [
{ "sessionId": "ce38e481-5ea1-46ca-a6d1-ab897b73b53a", "status": "closed_resolved" },
{ "sessionId": "8150934d-1812-4481-b17e-15a99fdd433a", "status": "closed_unresolved" }
]
}
idempotencyKey for each merge. If a request times out or its response is lost, retry with the same primary, sources, closure choices, and key. A key reused for a different operation is rejected. Eligibility failures close none of the selected sources.
Closure workflows are dispatched only by the request that newly completes the merge. A retry returns the saved merge without dispatching them again. If dispatch is interrupted after the merge is saved, retrying does not recover missed workflows.
All sessions must belong to the same organization. Self-merges, repeated sources, circular hierarchies, previously merged sources, AI-owned sessions, and active calls are rejected. A primary that has received sources may later be merged into another primary.
Messages addressed to a specific source, such as replies to its original email thread, stay there and mirror internally into the final primary without reopening either session. WhatsApp, Messenger, Instagram, and SMS keep normal routing to an open session or a new session; incoming messages are not redirected by the merge. Redaction preserves merge links while hiding sensitive content. See Merge sessions for dashboard warnings, history viewing, and reporting behavior.
Finish pending replies and avoid simultaneous changes to the selected sessions before merging. A merge does not wait for operations already in progress; an in-flight reply or status change may complete after the merge. Concurrent merges or parent changes can interfere with one another.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Pattern:
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Body
application/json
Response
Default Response
Pattern:
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Pattern:
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Show child attributes
Show child attributes
Was this page helpful?