curl --request POST \
--url https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"body": "Customer requested a billing review."
}
'import requests
url = "https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes"
payload = { "body": "Customer requested a billing review." }
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({body: JSON.stringify('Customer requested a billing review.')})
};
fetch('https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes', 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://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes",
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([
'body' => 'Customer requested a billing review.'
]),
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://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes"
payload := strings.NewReader("{\n \"body\": \"Customer requested a billing review.\"\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://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"body\": \"Customer requested a billing review.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes")
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 \"body\": \"Customer requested a billing review.\"\n}"
response = http.request(request)
puts response.read_body{
"note": {
"id": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"visibility": "internal",
"author": {
"type": "agent",
"id": "<string>",
"name": "<string>"
}
}
}{
"code": "BadRequest",
"message": "Invalid fields, unknown properties, or a disallowed status transition."
}{
"code": "Unauthenticated",
"message": "The API key is missing, invalid, or expired."
}{
"code": "PermissionDenied",
"message": "The API key does not authorize this tenant or operation."
}{
"code": "NotFound",
"message": "The customer Conversation or active assignee is unavailable in this tenant."
}{
"code": "Conflict",
"message": "The Conversation is merged/Closed, a lock is held, or lifecycle/realtime ownership constraints reject the operation."
}{
"code": "PayloadTooLarge",
"message": "The JSON body exceeds 32768 bytes."
}{
"code": "RateLimitExceeded",
"message": "The request exceeds the API rate limit."
}{
"code": "BadGateway",
"message": "The downstream request failed; the write may already have completed."
}{
"code": "ServiceUnavailable",
"message": "A required service is unavailable; the write may already have completed."
}{
"code": "GatewayTimeout",
"message": "The request timed out; the write may already have completed."
}Add a conversation internal note
Append one text-only internal note to a non-closed Conversation with trusted API system attribution. Notes are never sent to the customer; existing internal notifications still apply. Repeated identical requests can create multiple notes. API-key identifiers are omitted from the result. Requires a tenant API key with cxm:conversations.write or an existing broader write scope. The complete JSON body must not exceed 32768 bytes. Each invocation executes independently, with no request deduplication, version precondition, preparation step, or retry protection. A timeout may follow a successful commit. Merged and non-customer records are rejected.
curl --request POST \
--url https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"body": "Customer requested a billing review."
}
'import requests
url = "https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes"
payload = { "body": "Customer requested a billing review." }
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({body: JSON.stringify('Customer requested a billing review.')})
};
fetch('https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes', 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://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes",
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([
'body' => 'Customer requested a billing review.'
]),
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://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes"
payload := strings.NewReader("{\n \"body\": \"Customer requested a billing review.\"\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://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"body\": \"Customer requested a billing review.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://platform.crescendo.ai/api/v1/cxm/tenants/{tenantId}/conversations/{conversationId}/internal-notes")
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 \"body\": \"Customer requested a billing review.\"\n}"
response = http.request(request)
puts response.read_body{
"note": {
"id": "<string>",
"createdAt": "2023-11-07T05:31:56Z",
"visibility": "internal",
"author": {
"type": "agent",
"id": "<string>",
"name": "<string>"
}
}
}{
"code": "BadRequest",
"message": "Invalid fields, unknown properties, or a disallowed status transition."
}{
"code": "Unauthenticated",
"message": "The API key is missing, invalid, or expired."
}{
"code": "PermissionDenied",
"message": "The API key does not authorize this tenant or operation."
}{
"code": "NotFound",
"message": "The customer Conversation or active assignee is unavailable in this tenant."
}{
"code": "Conflict",
"message": "The Conversation is merged/Closed, a lock is held, or lifecycle/realtime ownership constraints reject the operation."
}{
"code": "PayloadTooLarge",
"message": "The JSON body exceeds 32768 bytes."
}{
"code": "RateLimitExceeded",
"message": "The request exceeds the API rate limit."
}{
"code": "BadGateway",
"message": "The downstream request failed; the write may already have completed."
}{
"code": "ServiceUnavailable",
"message": "A required service is unavailable; the write may already have completed."
}{
"code": "GatewayTimeout",
"message": "The request timed out; the write may already have completed."
}Authorizations
Tenant API key with cxm:conversations.write, cxm:conversations.*, cxm:write, or cxm:* scope. Keep it in a trusted backend; do not expose it in browser code.
Path Parameters
Exact tenant-authorized ID without path separators.
1 - 128Exact tenant-authorized ID without path separators.
1 - 512Body
Non-blank internal note text. The complete JSON body must also fit within 32768 bytes.
1 - 20000Response
Committed operation result; notification delivery is not implied.
Show child attributes
Show child attributes

