채팅 발신 메시지 전송
에이전트를 실행하지 않고 채팅 메시지 한 건을 만든 뒤, 채널이 지원하면 그 채널로 보냅니다. origin=human은 Supabase JWT와 X-Vox-Organization-Id 헤더가 필요하며, 호출자가 활성 상담사이면서 이 채팅을 지금 응대 중이어야 합니다. origin=system은 워크스페이스 API 키나 내부 관리자 인증이 필요합니다.
curl --request POST \
--url https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"origin": "human",
"client_idempotency_key": "<string>",
"text": "<string>",
"images": [
{
"type": "file_key",
"file_key": "file_abc123"
}
],
"navertalk": {
"text_content": {
"text": "<string>",
"quick_reply": {
"button_list": [
{
"type": "TEXT",
"data": {
"title": "<string>",
"code": "<string>"
}
}
]
}
},
"composite_content": null
}
}
'import requests
url = "https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages"
payload = {
"origin": "human",
"client_idempotency_key": "<string>",
"text": "<string>",
"images": [
{
"type": "file_key",
"file_key": "file_abc123"
}
],
"navertalk": {
"text_content": {
"text": "<string>",
"quick_reply": { "button_list": [
{
"type": "TEXT",
"data": {
"title": "<string>",
"code": "<string>"
}
}
] }
},
"composite_content": None
}
}
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({
origin: 'human',
client_idempotency_key: '<string>',
text: '<string>',
images: [{type: 'file_key', file_key: 'file_abc123'}],
navertalk: {
text_content: {
text: '<string>',
quick_reply: {button_list: [{type: 'TEXT', data: {title: '<string>', code: '<string>'}}]}
},
composite_content: null
}
})
};
fetch('https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages', 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/chats/{chat_id}/outbound-messages",
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([
'origin' => 'human',
'client_idempotency_key' => '<string>',
'text' => '<string>',
'images' => [
[
'type' => 'file_key',
'file_key' => 'file_abc123'
]
],
'navertalk' => [
'text_content' => [
'text' => '<string>',
'quick_reply' => [
'button_list' => [
[
'type' => 'TEXT',
'data' => [
'title' => '<string>',
'code' => '<string>'
]
]
]
]
],
'composite_content' => null
]
]),
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/chats/{chat_id}/outbound-messages"
payload := strings.NewReader("{\n \"origin\": \"human\",\n \"client_idempotency_key\": \"<string>\",\n \"text\": \"<string>\",\n \"images\": [\n {\n \"type\": \"file_key\",\n \"file_key\": \"file_abc123\"\n }\n ],\n \"navertalk\": {\n \"text_content\": {\n \"text\": \"<string>\",\n \"quick_reply\": {\n \"button_list\": [\n {\n \"type\": \"TEXT\",\n \"data\": {\n \"title\": \"<string>\",\n \"code\": \"<string>\"\n }\n }\n ]\n }\n },\n \"composite_content\": null\n }\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/chats/{chat_id}/outbound-messages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"origin\": \"human\",\n \"client_idempotency_key\": \"<string>\",\n \"text\": \"<string>\",\n \"images\": [\n {\n \"type\": \"file_key\",\n \"file_key\": \"file_abc123\"\n }\n ],\n \"navertalk\": {\n \"text_content\": {\n \"text\": \"<string>\",\n \"quick_reply\": {\n \"button_list\": [\n {\n \"type\": \"TEXT\",\n \"data\": {\n \"title\": \"<string>\",\n \"code\": \"<string>\"\n }\n }\n ]\n }\n },\n \"composite_content\": null\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages")
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 \"origin\": \"human\",\n \"client_idempotency_key\": \"<string>\",\n \"text\": \"<string>\",\n \"images\": [\n {\n \"type\": \"file_key\",\n \"file_key\": \"file_abc123\"\n }\n ],\n \"navertalk\": {\n \"text_content\": {\n \"text\": \"<string>\",\n \"quick_reply\": {\n \"button_list\": [\n {\n \"type\": \"TEXT\",\n \"data\": {\n \"title\": \"<string>\",\n \"code\": \"<string>\"\n }\n }\n ]\n }\n },\n \"composite_content\": null\n }\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"chat_id": "<string>",
"content": "<string>",
"delivery": {
"channel": "api",
"status": "stored",
"provider_message_ids": [
"<string>"
],
"error_code": "<string>",
"error_message": "<string>"
},
"role": "assistant",
"attachments": [
{
"file_key": "<string>",
"mime_type": "<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": "CHAT_NOT_FOUND",
"message": "채팅을 찾을 수 없습니다.",
"details": {
"chat_id": "22222222-2222-4222-8222-222222222222"
}
}
}{
"error": {
"code": "CONFLICT",
"message": "The requested operation conflicts with current state.",
"details": {
"current_status": "draft"
}
}
}{
"error": {
"code": "PAYLOAD_TOO_LARGE",
"message": "요청 본문이 허용 크기를 초과했습니다.",
"details": {
"content_length": 11534336,
"max_bytes": 10485760
}
}
}{
"error": {
"code": "CONVERTED_FILE_TOO_LARGE",
"message": "Converted image size exceeds the 1 MB limit.",
"details": {
"converted_size_bytes": 1258291,
"max_bytes": 1048576
}
}
}{
"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> 형식으로 보냅니다.
Headers
에이전트가 지금 처리 중인 입력 ID입니다. 네이버톡톡 채팅에 origin=system으로 보낼 때 필요하고 그 밖의 요청에서는 쓰지 않으며, 에이전트 도구에서는 시스템 변수 {{chat_turn_id}}를 넣습니다.
1 <= x <= 9223372036854776000Supabase JWT로 인증할 때 필요한 워크스페이스(organization_id) UUID입니다. origin=human은 이 인증 방식만 허용합니다.
Path Parameters
메시지를 보낼 채팅 ID입니다.
Body
이 엔드포인트의 요청 데이터입니다.
메시지를 만든 주체입니다. human은 Supabase JWT와 X-Vox-Organization-Id 헤더가 필요하고 이 채팅을 지금 응대 중인 활성 상담사만 쓸 수 있으며, system은 워크스페이스 API 키나 내부 관리자 인증으로만 쓸 수 있습니다.
human, system, tool "human"
클라이언트가 만든 메시지 멱등 키입니다. responder-notice:로 시작하거나 :responder-notice:가 들어간 키는 내부 전용이라 쓸 수 없습니다.
1 - 128보낼 메시지 텍스트입니다. navertalk을 쓰지 않으면 text와 images 중 하나 이상이 필요합니다.
1선택 이미지 입력입니다. 최대 3장까지 지원합니다.
3- ChatMessageFileKeyImageInput
- ChatMessageBase64ImageInput
- ChatMessageUrlImageInput
Show child attributes
Show child attributes
네이버톡톡 전용 메시지입니다. 퀵 버튼을 붙인 텍스트나 카드(최대 10개)를 보낼 때 쓰며, text·images와 함께 지정할 수 없습니다.
- NavertalkContentDto
- NavertalkContentDto
Show child attributes
Show child attributes
Response
성공 응답
저장된 메시지 ID입니다.
채팅 ID입니다.
저장된 메시지 텍스트입니다.
채널 발신 결과입니다.
Show child attributes
Show child attributes
저장된 메시지의 역할입니다. 항상 assistant입니다.
"assistant"저장된 이미지 첨부 목록입니다.
Show child attributes
Show child attributes
Was this page helpful?
curl --request POST \
--url https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"origin": "human",
"client_idempotency_key": "<string>",
"text": "<string>",
"images": [
{
"type": "file_key",
"file_key": "file_abc123"
}
],
"navertalk": {
"text_content": {
"text": "<string>",
"quick_reply": {
"button_list": [
{
"type": "TEXT",
"data": {
"title": "<string>",
"code": "<string>"
}
}
]
}
},
"composite_content": null
}
}
'import requests
url = "https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages"
payload = {
"origin": "human",
"client_idempotency_key": "<string>",
"text": "<string>",
"images": [
{
"type": "file_key",
"file_key": "file_abc123"
}
],
"navertalk": {
"text_content": {
"text": "<string>",
"quick_reply": { "button_list": [
{
"type": "TEXT",
"data": {
"title": "<string>",
"code": "<string>"
}
}
] }
},
"composite_content": None
}
}
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({
origin: 'human',
client_idempotency_key: '<string>',
text: '<string>',
images: [{type: 'file_key', file_key: 'file_abc123'}],
navertalk: {
text_content: {
text: '<string>',
quick_reply: {button_list: [{type: 'TEXT', data: {title: '<string>', code: '<string>'}}]}
},
composite_content: null
}
})
};
fetch('https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages', 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/chats/{chat_id}/outbound-messages",
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([
'origin' => 'human',
'client_idempotency_key' => '<string>',
'text' => '<string>',
'images' => [
[
'type' => 'file_key',
'file_key' => 'file_abc123'
]
],
'navertalk' => [
'text_content' => [
'text' => '<string>',
'quick_reply' => [
'button_list' => [
[
'type' => 'TEXT',
'data' => [
'title' => '<string>',
'code' => '<string>'
]
]
]
]
],
'composite_content' => null
]
]),
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/chats/{chat_id}/outbound-messages"
payload := strings.NewReader("{\n \"origin\": \"human\",\n \"client_idempotency_key\": \"<string>\",\n \"text\": \"<string>\",\n \"images\": [\n {\n \"type\": \"file_key\",\n \"file_key\": \"file_abc123\"\n }\n ],\n \"navertalk\": {\n \"text_content\": {\n \"text\": \"<string>\",\n \"quick_reply\": {\n \"button_list\": [\n {\n \"type\": \"TEXT\",\n \"data\": {\n \"title\": \"<string>\",\n \"code\": \"<string>\"\n }\n }\n ]\n }\n },\n \"composite_content\": null\n }\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/chats/{chat_id}/outbound-messages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"origin\": \"human\",\n \"client_idempotency_key\": \"<string>\",\n \"text\": \"<string>\",\n \"images\": [\n {\n \"type\": \"file_key\",\n \"file_key\": \"file_abc123\"\n }\n ],\n \"navertalk\": {\n \"text_content\": {\n \"text\": \"<string>\",\n \"quick_reply\": {\n \"button_list\": [\n {\n \"type\": \"TEXT\",\n \"data\": {\n \"title\": \"<string>\",\n \"code\": \"<string>\"\n }\n }\n ]\n }\n },\n \"composite_content\": null\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://client-api.tryvox.co/v3/chats/{chat_id}/outbound-messages")
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 \"origin\": \"human\",\n \"client_idempotency_key\": \"<string>\",\n \"text\": \"<string>\",\n \"images\": [\n {\n \"type\": \"file_key\",\n \"file_key\": \"file_abc123\"\n }\n ],\n \"navertalk\": {\n \"text_content\": {\n \"text\": \"<string>\",\n \"quick_reply\": {\n \"button_list\": [\n {\n \"type\": \"TEXT\",\n \"data\": {\n \"title\": \"<string>\",\n \"code\": \"<string>\"\n }\n }\n ]\n }\n },\n \"composite_content\": null\n }\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"chat_id": "<string>",
"content": "<string>",
"delivery": {
"channel": "api",
"status": "stored",
"provider_message_ids": [
"<string>"
],
"error_code": "<string>",
"error_message": "<string>"
},
"role": "assistant",
"attachments": [
{
"file_key": "<string>",
"mime_type": "<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": "CHAT_NOT_FOUND",
"message": "채팅을 찾을 수 없습니다.",
"details": {
"chat_id": "22222222-2222-4222-8222-222222222222"
}
}
}{
"error": {
"code": "CONFLICT",
"message": "The requested operation conflicts with current state.",
"details": {
"current_status": "draft"
}
}
}{
"error": {
"code": "PAYLOAD_TOO_LARGE",
"message": "요청 본문이 허용 크기를 초과했습니다.",
"details": {
"content_length": 11534336,
"max_bytes": 10485760
}
}
}{
"error": {
"code": "CONVERTED_FILE_TOO_LARGE",
"message": "Converted image size exceeds the 1 MB limit.",
"details": {
"converted_size_bytes": 1258291,
"max_bytes": 1048576
}
}
}{
"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": {}
}
}