Notes Live
Internal notes are private commentary on a conversation, visible only to agents in scope. They live alongside visitor and agent messages in the timeline, but the visitor never sees them. Used for context, escalation handoffs, and audit trail.
Notes are their own database table, not a message with a special kind. They don't appear in the Messages list at all.
List notes on a conversation
Not paginated. Returns every note on the conversation, newest first.
Post a note
Append-only: every call adds a new note row, authored by the caller. This is the endpoint for free-form, one-comment-per-agent collaboration notes.
| Field | Type | Description |
|---|---|---|
| bodyrequired | string | Note body, 1 to 10,000 characters. |
There is no mentions field and no @-mention parsing. A note is plain text; mentioning a teammate does not notify them.
{ "body": "Customer is a Series A founder; escalate to lead." }Code samples
curl -X POST 'https://app.message.com/api/v1/conversations/:id/notes' \
-H 'Authorization: Bearer YOUR_WORKSPACE_JWT' \
-H 'Content-Type: application/json' \
-d '{"body":"Customer is a Series A founder"}'const res = await fetch(`https://app.message.com/api/v1/conversations/${conversationId}/notes`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({ body: 'Customer is a Series A founder' })
});
const { note } = await res.json();import requests
r = requests.post(
f"https://app.message.com/api/v1/conversations/{conversation_id}/notes",
headers={"Authorization": f"Bearer {token}"},
json={"body": "Customer is a Series A founder"},
)
note = r.json()["note"]require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/conversations/#{conversation_id}/notes")
req = Net::HTTP::Post.new(uri, {"Authorization" => "Bearer #{token}", "Content-Type" => "application/json"})
req.body = { body: "Customer is a Series A founder" }.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
note = JSON.parse(res.body)["note"]<?php
$ctx = stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer $token\r\nContent-Type: application/json",
"content" => json_encode(["body" => "Customer is a Series A founder"])
],
]);
$note = json_decode(file_get_contents("https://app.message.com/api/v1/conversations/{$conversationId}/notes", false, $ctx), true)["note"];{
"notes": [
{
"id": "uuid",
"conversationId": "uuid",
"visitorId": "uuid",
"authorAgentId": "uuid",
"authorName": "Alex Lee",
"authorAvatarUrl": null,
"body": "Customer is a Series A founder; escalate to lead.",
"createdAt": "2026-05-13T15:30:00Z",
"updatedAt": "2026-05-13T15:30:00Z"
}
]
}Your working note (upsert)
Not a general note editor. This is a single-textarea "working memory" surface, built for the call view: it always targets the calling agent's own most recent note on this conversation, not a note chosen by ID.
- Empty or omitted
body: deletes the agent's existing note on this conversation, if any. - Non-empty
body, agent already has a note here: updates it in place. - Non-empty
body, agent has no note yet: creates one.
There is no PATCH by note ID, and no editedAt field: an updated note just carries a newer updatedAt.
Delete a note
Hard delete, by note ID. Author or admin only. No soft-delete, no [note removed] tombstone: the row is gone.
Errors
| Code | When |
|---|---|
400 invalid_body | Empty body on POST, or body too long (10K chars max). |
404 not_found | Conversation or note does not exist, or is outside the agent's department scope. |
Common pitfalls
- Leaking customer-sensitive info in notes. Notes are visible to every agent in scope. Avoid full credit-card numbers, SSNs, full passwords.
- Expecting @-mentions to notify anyone. They don't. There is no mention parsing or notification on this resource.
- Calling PATCH expecting to edit a specific note by ID. It doesn't take a note ID at all; it always targets the caller's own latest note on the conversation. Use POST for a new, separate note.