Skip to content

conversation operations

The exact fields each conversation.* operation takes and returns.

Every op is POST /v1/<op> over HTTP and the MCP tool loom_<op>. The identity fields (org, namespace, agent, user_id, session_id) are filled from the token and the MCP session when a transport can; for an HTTP call with no token on a single-user server, the install's org on every op but code.* and tracker.* (which keep their plane's), and on some ops its namespace and agent; see identities and namespaces. The operations index explains role, kind and the MCP all listing. Unknown input fields are served and named in ignored_args.

conversation.append

Log one chat turn (role + content) to a thread.

POST /v1/conversation.append · MCP loom_conversation_append · role writer · idempotent

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
thread_id str required The thread to append to; it need not have been created first.
msg_id str '' optional; server derives a content hash when omitted
role Literal['user', 'assistant', 'system', 'tool'] required Who spoke: user, assistant, system or tool.
content str required The message text, stored verbatim; a user or assistant turn is also embedded for conversation.search, unless its text reads as high.
reasoning str '' The model's reasoning for this turn, stored beside content; empty = none.
user_id str '-' The end user the row belongs to; '-' for none.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
metadata dict[str, Any] (computed) Free-form JSON stored on the message; read back by get and list.
tokens int 0 Token count the caller reports for the message, stored as given; 0 = not reported.
Output field Type Meaning
msg_id str The message id: the one sent, or the content hash the server derived.
ts datetime Server time of this call; the message's timestamp when it was written.
stored bool False when recording is off or the turn was removed while embedding ran.
reason Literal['', 'duplicate', 'logging_disabled', 'deleted'] "" (written), "duplicate" (this msg_id already landed; stored=True), "logging_disabled" or "deleted" (stored=False).

conversation.compact

Summarize old turns into one embedded system turn and tombstone the originals.

POST /v1/conversation.compact · MCP loom_conversation_compact · role writer · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
thread_id str required The thread to compact.
keep_recent int 10 The newest this many messages stay verbatim; older ones are summarized, as many as fit the summarizer's prompt.
min_to_compact int 4 Skip when fewer new turns than this precede the last keep_recent; an earlier summary does not count.
Output field Type Meaning
compacted bool True when a summary was written and the originals tombstoned; reason says why not.
messages_summarized int How many older messages the summary replaced, not counting an earlier summary; 0 when not compacted.
summary_msg_id str Id of the synthetic system summary message; empty when not compacted.
tokens_in int Prompt tokens the summarizer LLM call used; 0 when no call was made.
tokens_out int Completion tokens of the summary; 0 when no call was made.
reason str Why compacted=False: nothing-to-compact, below-min-to-compact, already-compacted, summarizer-empty, embed-failed (retry once the embedder answers), summary-high-sensitivity (do not retry: the same window gets the same summary).

conversation.create

Open a thread container.

POST /v1/conversation.create · MCP loom_conversation_create · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
thread_id str required The caller's id for the thread; re-creating the same id replaces its title, metadata and run_id.
title str '' Display title shown in thread lists; empty = untitled.
metadata dict[str, Any] (computed) Free-form JSON stored on the thread row; conversation.get returns it.
run_id str '-' The agent run (task scope) the call belongs to; '-' for none.
user_id str '-' The end user the row belongs to; '-' for none.
Output field Type Meaning
thread_id str Echo of the input thread_id.
created_at datetime When this call wrote the thread row.

conversation.delete

Soft-delete one thread: its messages, thread row and embeddings no other agent holds.

POST /v1/conversation.delete · MCP loom_conversation_delete · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
thread_id str required The thread to soft-delete.
confirm_thread_id str required Must equal thread_id, or the call fails with 400 and nothing is deleted.
Output field Type Meaning
tombstoned_at datetime The tombstone time stamped on every affected row.
thread_id str Echo of the input thread_id.
rows_affected dict[str, int] {table_name: rows tombstoned}; for loom.conversation_embeddings, turns (one per turn however many embedding models hold it).
total int Sum of rows_affected across tables.

conversation.get

Fetch one thread by id: thread metadata + optional messages.

POST /v1/conversation.get · MCP loom_conversation_get · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required "" or "-" reads every agent's turns in the thread (a turn two agents both logged once), for a caller that has only its id, as conversation.search reads "" and "-"; any other value reads that one agent's.
thread_id str required The thread to fetch.
include_messages bool False Default off: keeps the metadata lookup to two point reads.
limit int 100 At most this many messages when include_messages is set (1-1000).
offset int 0 Messages to skip from the end order counts from.
order Literal['asc', 'desc'] 'asc' Which end of the thread the page is counted from.
Output field Type Meaning
found bool False when the thread has neither a thread row nor any live message.
thread_id str Echo of the input thread_id.
title str Title from conversation.create or update; empty when none was set.
metadata dict[str, Any] Thread metadata JSON; {} when none was set, apart from the titler's title_refused mark.
run_id str The agent run (task scope) the call belongs to; '-' for none.
user_id str The end user the row belongs to; '-' for none.
agent str The thread's owner (the agent of its thread row); for a thread built by appends only, the latest writer among the messages read, so empty without include_messages.
created_at datetime \| None When the thread row was first written; None for a thread built by appends only.
updated_at datetime \| None When the thread row last changed; None for a thread built by appends only.
msg_count int Live (not tombstoned) messages in the thread, returned even without messages.
messages list[_ConversationEntry] Populated only when include_messages=True; simple limit/offset slicing (cursor-paginated walks belong on conversation.list).
next_cursor str \| None With order="asc", the conversation.list cursor for the turns after this page; None when the page reaches the end, or the order is desc.

conversation.list

Enumerate stored conversation threads for an agent / namespace.

POST /v1/conversation.list · MCP loom_conversation_list · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required "" or "-" reads every agent's turns in the thread (a turn two agents both logged once), for a caller that has only its id, as conversation.search reads "" and "-"; any other value reads that one agent's.
thread_id str required The thread to page through, oldest message first.
run_id str \| None None optional filter; None or "-" = every run
limit int 100 At most this many messages per page (1-1000).
cursor str \| None None opaque (ts, msg_id)
Output field Type Meaning
rows list[_ConversationEntry] The page of messages in chronological order.
next_cursor str \| None Pass as cursor for the next page; None when this is the last page.

conversation.list_saved_messages

Page through the messages a user saved, newest first, with no query: the first view of a "Saved" panel before anything is typed.

POST /v1/conversation.list_saved_messages · MCP loom_conversation_list_saved_messages · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str required Only this user's saved messages are listed.
conversation_id str \| None None Optional: only the messages saved from one conversation.
limit int 50 At most this many messages per page (1-200).
cursor str \| None None Opaque; pass the previous page's next_cursor.
fields list[str] \| None None The output fields the caller reads (a whitelist resolved against the row model; an unknown name is a 422); unset, every field.
Output field Type Meaning
rows list[_SavedMessageHit] The page, most recently saved first.
next_cursor str \| None Pass as cursor for the next page; None when this is the last page.

conversation.save_message

Save one chat message a user picked for search, whether or not the namespace records transcripts.

POST /v1/conversation.save_message · MCP loom_conversation_save_message · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
conversation_id str required The client's conversation id; this remains the thread id in Loom.
message_id str required The client's stable message id.
role Literal['user', 'assistant'] required Only selectable user and assistant messages are embedded.
content str required The message text; ignored when the transcript already holds the message.
user_id str required The owner is a hard filter on the paired search operation.
Output field Type Meaning
conversation_id str
message_id str
indexed bool True when the turn is saved and has a vector the saved search ranks: a new write, a re-save (finishing what an interrupted save left behind), and a transcript turn that is now flagged saved.
reason Literal['', 'duplicate', 'owner_mismatch', 'deleted', 'unsupported_role', 'embed_failed', 'high_sensitivity'] "duplicate": already saved, nothing changed. "owner_mismatch": the stored turn belongs to another user. "deleted": the stored turn was tombstoned. "unsupported_role": the stored turn is a system or tool turn, which is not embedded.…
ts datetime The stored turn's timestamp; the time of the refusal when one is refused.

conversation.saved_status

Which of a conversation's messages a user has saved, in one call: a chat client draws every message's saved state when it opens the conversation.

POST /v1/conversation.saved_status · MCP loom_conversation_saved_status · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str required The end user the row belongs to; '-' for none.
conversation_id str required The conversation the messages belong to; message ids are unique within it.
message_ids list[str] required The messages to check (1-500).
Output field Type Meaning
saved list[str] The subset of message_ids this user has saved, in the order asked.

conversation.search

Rank conversation messages by embedding similarity to query.

POST /v1/conversation.search · MCP loom_conversation_search · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required "*" or "-" searches every agent's turns; any other value, that one agent's.
query str required Text matched against message content, by embedding similarity, or by substring when the embedder is unavailable.
thread_id str \| None None optional: scope to one thread
run_id str \| None None optional: filter by run_id; None or "-" = every run
role Literal['user', 'assistant', 'system', 'tool'] \| None None Filter by speaker; system turns other than compaction summaries, and tool turns, are not embedded, so they match only on the substring fallback.
user_id str \| None None Optional owner filter; None or "-" = every user.
top_k int 20 At most this many hits (1-200).
namespace_scope Literal['this', 'all_mine'] 'all_mine' "all_mine" also reads the caller's own namespaces, up to memory_search_fanout_max_namespaces, as knowledge.search does.
Output field Type Meaning
hits list[_ConversationHit] Matching messages, most similar first and empty when none clears the similarity floor; newest first, with no floor, on the substring fallback.
namespaces_skipped list[str] Namespaces of the caller an all_mine search did not read because it stops at memory_search_fanout_max_namespaces; address one by name to search it.

conversation.search_saved_messages

Hybrid search over only the messages one user saved with conversation.save_message, not the whole transcript.

POST /v1/conversation.search_saved_messages · MCP loom_conversation_search_saved_messages · role viewer · read · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
user_id str required The end user the row belongs to; '-' for none.
query str required
conversation_id str \| None None
role Literal['user', 'assistant'] \| None None
top_k int 20
search_mode Literal['hybrid', 'semantic', 'lexical'] 'hybrid' hybrid: the vector leg and a BM25 leg over each message's whole text, fused by reciprocal rank; semantic: vector only; lexical: BM25 only.
lexical_weight float 1.0 Weight of the BM25 leg in the fusion (hybrid mode); 1.0 = equal weight.
Output field Type Meaning
hits list[_SavedMessageHit]

conversation.set_logging

Toggle whether this namespace records conversation transcripts.

POST /v1/conversation.set_logging · MCP loom_conversation_set_logging · role admin · idempotent · audited · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
enabled bool required True records transcripts in this namespace; False makes append store nothing.
Output field Type Meaning
namespace str The namespace the op reads or writes.
enabled bool The logging switch as it now stands.

conversation.threads

Recent conversation threads of a namespace, newest first.

POST /v1/conversation.threads · MCP loom_conversation_threads · role viewer · read

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str '*' "*" or "-" lists every agent's threads
window_days int 30 Only threads with a live message in the last this many days (1-365).
limit int 30 At most this many threads (1-200).
offset int 0 Threads to skip before the page (0-100,000); default 0, the first page.
fields list[str] \| None None The output fields the caller reads (a whitelist resolved against the row model; an unknown name is a 422); unset, every field.
Output field Type Meaning
threads list[ConversationThreadSummary] Threads, most recently active first, each with its count and last message excerpt.

conversation.unsave_message

Withdraw one message from a user's saved-message search.

POST /v1/conversation.unsave_message · MCP loom_conversation_unsave_message · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
conversation_id str required The client's conversation id, as given to conversation.save_message.
message_id str required The client's message id, as given to conversation.save_message.
user_id str required Only this user's saved row is withdrawn.
Output field Type Meaning
conversation_id str
message_id str
found bool False when no saved row of this user exists: never saved, already withdrawn, or saved by another user (which is not confirmed).
removed bool True when the turn itself was removed: the namespace does not record transcripts, so the row existed only because of the save.
ts datetime

conversation.update

Update one thread's title/metadata/run_id.

POST /v1/conversation.update · MCP loom_conversation_update · role writer · idempotent · not listed under the MCP all toolset

Input field Type Default Meaning
org str required The tenant (first element of the identity tuple).
namespace str required The namespace the op reads or writes.
agent str required The writing or reading agent; '-' or '*' means any agent where the op allows it.
thread_id str required The thread to update; an unknown thread returns updated=False.
title str \| None None Handler raises 400 if all three are absent (no-op signals a caller bug).
metadata dict[str, Any] \| None None New metadata JSON, replacing the old object whole; None leaves it unchanged.
run_id str \| None None The thread's new run; None or "-" leaves it unchanged, "" detaches it from its run.
Output field Type Meaning
updated bool False when the thread has no thread row (never created); nothing is written then.
thread_id str Echo of the input thread_id.
updated_at datetime \| None The new updated_at of the thread; None when nothing was updated.