Conversation
History of the current thread.
Privacy and analytics
We use necessary technical cookies and, only with your consent, analytics and marketing pixels to improve the website and measure campaigns.
History of the current thread.
Searches earlier messages of the current thread only.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.search |
| MCP tool | conversation_search |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, Chat, Screens, Apps |
| Status | Available |
Input
{
query: string // ≤ 200
limit?: integer // 1–10 · default 10
}Output
{
items: Array<{
sequence: integer // -9007199254740991–9007199254740991
role: string
excerpt: string
createdAt: string
}>
}Reads one full message of the current thread by sequence, in 6,000 character pages.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.read |
| MCP tool | conversation_read |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo, Voice, Chat, Screens, Apps |
| Status | Available |
Input
{
sequence: integer // 1–9007199254740991
page?: integer // 1–9007199254740991 · default 1
}Output
{
sequence: integer // -9007199254740991–9007199254740991
role: string
text: string
hasMore: boolean
}Shows one question with 2 to 6 answer buttons in the thread; the person's pick arrives as their next message. Use it only when the next step depends on choosing between concrete options (a format, a date, one of two directions); then end your answer and wait. Stores nothing; for a missing brand fact that must be kept, use ask_brand_question.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.choice |
| MCP tool | conversation_choice |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo |
| Status | Available |
| Widget | choice |
Input
{
question: string // ≤ 300
options: string[] // ≤ 6 items
allowOther?: boolean // default true · Whether the person may write their own answer instead.
}Output
{
question: string
options: string[]
allowOther: boolean
}Asks the person to attach a file to the thread (a brand manual, a logo, a photo) with a drop zone on the card; the file arrives with their next message. Use it when a task needs a file you do not have; say why in purpose, then end your answer and wait.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.request_file |
| MCP tool | conversation_request_file |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Nödo |
| Status | Available |
| Widget | file_request |
Input
{
purpose: string // ≤ 300
accept?: "image" | "pdf" | "document" | "any" // default "any"
multiple?: boolean // default false
}Output
{
purpose: string
accept: "image" | "pdf" | "document" | "any"
multiple: boolean
}Opens an empty conversation in the brand, with Nödo (agent) or with the team (team), optionally tied to a work. internal threads are for the Nömad team only. Returns the thread; it sends no message.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.create |
| MCP tool | conversation_threads_create |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
title: string // ≤ 160
workId?: string // uuid
audience: "brand" | "internal"
kind?: "agent" | "team"
}Output
{
conversation: Record<string, unknown>
}Creates a thread and posts its first message in one step; the message becomes the title. Files uploaded under a reserved draftId move into the thread. With agent, Nödo answers in a run whose id comes back.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.start |
| MCP tool | conversation_threads_start |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
kind: "agent" | "team"
content: string // ≤ 4000
idempotencyKey: string // uuid
workId?: string // uuid
draftId?: string // uuid
attachments?: Array<{
path: string // ≤ 600
name: string // ≤ 200
mime: string // ≤ 120
size: integer // –9007199254740991
}> // ≤ 10 items
mentions?: Array<{
type: "mention"
capability: string // ≤ 80
input?: Record<string, unknown> // default {}
label: string // ≤ 160
at?: {
start: integer // 0–9007199254740991
end: integer // 0–9007199254740991
}
}> // ≤ 6 items
inserts?: unknown[] // ≤ 4 items
screen?: {
path: string
title?: string
navigation?: boolean // default true
form?: "phone" | "tablet" | "desktop"
elements?: string[] // ≤ 40 items · default []
}
}Output
{
messageId?: string | null
runId?: string | null
status?: string | null
conversation: {
id: string
brandId: string
title: string
workId: string | null
audience: string // `brand`, `internal` or `private`.
kind: string // `agent` (Nödo answers) or `team` (the people at Nömad).
state: string // `open`, `waiting_brand`, `waiting_team` or `closed`.
channel: string // `app`, `whatsapp` or `email`.
titleAuto: boolean
assignee: {
id: string
name: string
avatarUrl: string | null
}
| null
lastMessageAt: string | null
version: integer // -9007199254740991–9007199254740991
createdAt: string
updatedAt: string
}
}Posts a message in a brand thread as the caller: text, files already uploaded to the thread folder, @ mentions and «+» cards. In a Nödo chat Nödo answers in a run. The same idempotencyKey never posts twice.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.messages.send |
| MCP tool | conversation_messages_send |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
content: string // ≤ 4000
idempotencyKey: string // uuid
visibility?: "shared" | "internal"
attachments?: Array<{
path: string // ≤ 600
name: string // ≤ 200
mime: string // ≤ 120
size: integer // –9007199254740991
}> // ≤ 10 items
mentions?: Array<{
type: "mention"
capability: string // ≤ 80
input?: Record<string, unknown> // default {}
label: string // ≤ 160
at?: {
start: integer // 0–9007199254740991
end: integer // 0–9007199254740991
}
}> // ≤ 6 items
inserts?: unknown[] // ≤ 4 items
screen?: {
path: string
title?: string
navigation?: boolean // default true
form?: "phone" | "tablet" | "desktop"
elements?: string[] // ≤ 40 items · default []
}
}Output
{
messageId?: string | null
runId?: string | null
status?: string | null
}Changes the title of a thread the caller can see. Returns the updated thread.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.rename |
| MCP tool | conversation_threads_rename |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
title: string // ≤ 160
}Output
{
id: string
brandId: string
title: string
workId: string | null
audience: string // `brand`, `internal` or `private`.
kind: string // `agent` (Nödo answers) or `team` (the people at Nömad).
state: string // `open`, `waiting_brand`, `waiting_team` or `closed`.
channel: string // `app`, `whatsapp` or `email`.
titleAuto: boolean
assignee: {
id: string
name: string
avatarUrl: string | null
}
| null
lastMessageAt: string | null
version: integer // -9007199254740991–9007199254740991
createdAt: string
updatedAt: string
}Closes or reopens a team thread under the same state rule as the staff inbox. Returns the thread state.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.set_state |
| MCP tool | conversation_threads_set_state |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
state: "open" | "closed"
}Output
{
id: string
brandId: string
title: string
workId: string | null
audience: string // `brand`, `internal` or `private`.
kind: string // `agent` (Nödo answers) or `team` (the people at Nömad).
state: string // `open`, `waiting_brand`, `waiting_team` or `closed`.
channel: string // `app`, `whatsapp` or `email`.
titleAuto: boolean
assignee: {
id: string
name: string
avatarUrl: string | null
}
| null
lastMessageAt: string | null
version: integer // -9007199254740991–9007199254740991
createdAt: string
updatedAt: string
}The «Briefing with Nödo» thread of a briefing task: created the first time, the same shared thread afterwards. Fails with not_found for a task that is not a Nödo briefing.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.task_briefing.open |
| MCP tool | conversation_task_briefing_open |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
taskId: string // uuid
}Output
{
conversation: Record<string, unknown>
}A fresh draft id, so files can be uploaded before the thread exists (conversation.threads.start takes it as draftId). Stores nothing.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.drafts.reserve |
| MCP tool | conversation_drafts_reserve |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{}Output
{
draftId: string // uuid
}Removes a file uploaded for a message and taken out before sending, so storage keeps nothing nobody sent. A file a saved message already carries is never touched.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.uploads.discard |
| MCP tool | conversation_uploads_discard |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
path: string // ≤ 600
}Output
{
ok: true
}Runs one slash command in a thread and writes it with its result; no model is called. Permissions and confirm cards are those of the command capability itself. Returns the message id.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.commands.run |
| MCP tool | conversation_commands_run |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
capabilityId: string // ≤ 60
content: string // ≤ 4000
argument?: unknown
idempotencyKey: string // uuid
locale?: "es" | "en"
}Output
{
messageId: string
}Casts the caller's vote on a «+» poll in a thread they can read; only the team votes in internal notes. An empty choice takes the vote back. Returns the poll as the voter sees it.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.polls.vote |
| MCP tool | conversation_polls_vote |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
messageId: string // uuid
pollId: string
optionIds: string[] // ≤ 10 items
}Output
{
ok: true
poll: unknown
}Accepts or cancels a change Nödo prepared on a confirm card. Only the person it was prepared for; on accept the stored input runs once through the full gate again. Returns the outcome and, for undoable writes, until when it can be undone.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.confirmations.decide |
| MCP tool | conversation_confirmations_decide |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
confirmationId: string // uuid
accept: boolean
}Output
{
status: string
error: string | null
undoUntil?: string // date-time
}Approves or archives an image Nödo proposed in a gallery card. The click is the caller's explicit decision, so library.asset.review runs confirmed, through its own Kit right and brand scope.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.images.review |
| MCP tool | conversation_images_review |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
assetId: string // uuid
decision: "approve" | "archive"
}Output
{
ok: boolean
status?: "approved" | "archived"
error?: string
}Runs the action a Nödo card kind declares (CARD_ACTIONS). The capability it names runs for the caller through the full gate, so a card never does more than the person could on the page. Undeclared actions answer action_not_declared.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.cards.run |
| MCP tool | conversation_cards_run |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
kind: string // ≤ 40
action: string // ≤ 40
input: Record<string, unknown>
}Output
{
ok: boolean
data?: unknown
error?: string
}Reads a live card (LIVE_CARDS) again with the input it was built from and returns the same card, current. Only read capabilities.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.cards.refresh |
| MCP tool | conversation_cards_refresh |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
capability: string // ≤ 80
input: Record<string, unknown>
}Output
{
part?: unknown
error?: string
}Undoes a confirmed write within its window (CARD_INVERSES): only the person who confirmed it, only once, through the same brand action the Kit uses for its own undo.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.cards.undo |
| MCP tool | conversation_cards_undo |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
confirmationId: string // uuid
}Output
{
ok: boolean
error?: string
}Creates one of the caller's personal folders of Nödo chats (no folderId) or renames/recolours one of their own. Folders are private to their owner.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.folders.save |
| MCP tool | conversation_folders_save |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
folderId?: string // uuid
name: string // ≤ 60
tone?: string // ≤ 20
}Output
{
ok: boolean
data?: Record<string, unknown>
error?: string
}Deletes one of the caller's own folders (registry conversation_folder.delete); its chats go back to the list. Staff operating the brand never touch someone's folders.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.folders.delete |
| MCP tool | conversation_folders_delete |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
folderId: string // uuid
}Output
{
ok: boolean
error?: string
}Saves the order of every one of the caller's folders in the brand, as dragged. Returns how many were placed.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.folders.reorder |
| MCP tool | conversation_folders_reorder |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
folderIds: string[] // ≤ 100 items
}Output
{
ok: boolean
data?: integer // -9007199254740991–9007199254740991
error?: string
}Pins, archives for oneself, files into a folder (folderId: null takes it out) or moves the read mark. Only the caller's view changes; the thread stays as it is for everyone.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.update_view |
| MCP tool | conversation_threads_update_view |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
pinned?: boolean
archived?: boolean
folderId?: string | null
read?: boolean
}Output
{
ok: boolean
data?: Record<string, unknown>
error?: string
}The creator of a Nödo chat makes it private or shares it with the brand. Returns the audience and the thread version.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.set_audience |
| MCP tool | conversation_threads_set_audience |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
audience: "brand" | "private"
}Output
{
ok: boolean
data?: {
audience: "brand" | "private"
version: integer // -9007199254740991–9007199254740991
}
error?: string
}Deletes a Nödo chat (registry conversation.delete): its creator, or whoever holds the brand's delete right. Team threads are refused (team_thread_not_deletable); they are only archived for oneself.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.delete |
| MCP tool | conversation_threads_delete |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | Yes |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
}Output
{
ok: boolean
error?: string
}From a Nödo chat, opens a new team thread linked back to it, opened by the caller's note (a team-only summary of the chat goes first) and assigned to whoever answers the brand. Clients only: staff are the team already.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.threads.escalate |
| MCP tool | conversation_threads_escalate |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
note: string // ≤ 4000
requestKey: string // uuid
}Output
{
ok: boolean
data?: {
conversationId: string
created: boolean
}
error?: string
}Queues a Nödo draft run for a work with the brand's AI policy model. Returns the run id and queued, pending_dispatch, not_configured (AI off) or model_unavailable. The same idempotencyKey never queues twice.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.agent_runs.start |
| MCP tool | conversation_agent_runs_start |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
workId: string // uuid
taskId?: string // uuid
request: string // ≤ 4000
idempotencyKey: string // uuid
}Output
{
id: string | null
status: "queued" | "pending_dispatch" | "not_configured" | "model_unavailable"
}Asks a queued or running Nödo run to stop. Only someone who can see it: the run's thread, or the person who asked for it. A finished run answers ok without change.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.agent_runs.cancel |
| MCP tool | conversation_agent_runs_cancel |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
runId: string // uuid
}Output
{
ok: true
}Approves (stored as a brand file) or rejects the text a completed Nödo run delivered. version is the run version the person saw; a changed run answers conflict (delivery_revision_changed).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.deliveries.review |
| MCP tool | conversation_deliveries_review |
| Scope | conversation.write |
| Minimum role | Member |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
runId: string // uuid
version: integer // –9007199254740991
status: "approved" | "rejected"
}Output
{
data: Record<string, unknown>
}Sends a support ticket to Nömad as the caller: type, message, page, an optional screenshot, files uploaded to the drafted ticket folder (ticketId) and «+» cards. The person gets a confirmation and the support team the ticket.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.feedback.submit |
| MCP tool | conversation_feedback_submit |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
type: "bug" | "suggestion"
message: string // ≤ 20000
pageUrl?: string | null
userAgent?: string | null
brand?: string | null
ticketId?: string | null
imageBase64?: string | null
imageMimeType?: string | null
attachments?: unknown[] // ≤ 20 items
inserts?: unknown[] // ≤ 4 items
clientIp?: string | null
}Output
{
ok: true
}Removes a file the caller uploaded for a ticket they are still drafting; only while no ticket has that id yet.
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.feedback.discard_upload |
| MCP tool | conversation_feedback_discard_upload |
| Scope | conversation.write |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, Screens, Apps |
| Status | Available |
Input
{
ticketId: string // uuid
path: string // ≤ 600
}Output
{
ok: true
}How many of the brand's conversations the person has unread: team threads and their own Nödo chats, as the «Messages» entry counts them.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.unread_count |
| MCP tool | conversation_unread_count |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
asClient?: boolean // Read as the brand sees it (a staff viewer previewing as client). Only narrows.
}Output
{
count: integer // 0–9007199254740991
}The «Messages» list as the caller sees it: team threads and Nödo chats with their pin, folder, archive and read state, and the caller's folders. offset reads the next page of Nödo chats.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.inbox.list |
| MCP tool | conversation_inbox_list |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
offset?: integer // 0–10000
}Output
{
team: Array<{
conversation: {
id: string
brandId: string
title: string
workId: string | null
audience: string // `brand`, `internal` or `private`.
kind: string // `agent` (Nödo answers) or `team` (the people at Nömad).
state: string // `open`, `waiting_brand`, `waiting_team` or `closed`.
channel: string // `app`, `whatsapp` or `email`.
titleAuto: boolean
assignee: {
id: string
name: string
avatarUrl: string | null
}
| null
lastMessageAt: string | null
version: integer // -9007199254740991–9007199254740991
createdAt: string
updatedAt: string
}
createdBy: string | null
lastSequence: integer // -9007199254740991–9007199254740991
preview: string | null // Last shared message, one line.
previewAuthor: {
kind: string // `self`, `nodo`, `staff` or `member`.
name?: string
}
| null
unread: boolean
folderId: string | null
pinnedAt: string | null
archivedAt: string | null
}>
nodo: Array<{
conversation: {
id: string
brandId: string
title: string
workId: string | null
audience: string // `brand`, `internal` or `private`.
kind: string // `agent` (Nödo answers) or `team` (the people at Nömad).
state: string // `open`, `waiting_brand`, `waiting_team` or `closed`.
channel: string // `app`, `whatsapp` or `email`.
titleAuto: boolean
assignee: {
id: string
name: string
avatarUrl: string | null
}
| null
lastMessageAt: string | null
version: integer // -9007199254740991–9007199254740991
createdAt: string
updatedAt: string
}
createdBy: string | null
lastSequence: integer // -9007199254740991–9007199254740991
preview: string | null // Last shared message, one line.
previewAuthor: {
kind: string // `self`, `nodo`, `staff` or `member`.
name?: string
}
| null
unread: boolean
folderId: string | null
pinnedAt: string | null
archivedAt: string | null
}>
nodoNextOffset: integer | null // Offset of the next page of Nödo chats; null when every chat is here.
archivedCount: integer // -9007199254740991–9007199254740991
}What «Messages» paints first on the sync engine, read in one database snapshot: the brand's threads (the latest, plus every one the caller pinned or filed) and, from the caller's own group, their private chats, read marks, pins, archive and folders in that brand, with each group's seq. Partial: the client completes both groups with sync.bootstrap and follows them with sync.deltas.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.inbox.seed |
| MCP tool | conversation_inbox_seed |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | Screens, Apps, API |
| Status | Available |
Input
{
limit?: integer // 1–200 · Latest brand threads and private chats to include (100 by default).
}Output
{
brand: {
group: string
seq: integer // 0–9007199254740991
records: Array<{
entity: string
id: string // uuid
version: string
data: Record<string, unknown>
}>
complete: false
shapes: Record<string, integer>
}
person: {
group: string
seq: integer // 0–9007199254740991
records: Array<{
entity: string
id: string // uuid
version: string
data: Record<string, unknown>
}>
complete: false
shapes: Record<string, integer>
}
}One thread a link asks for, with the caller's state on it (pin, folder, archive, read). thread is null when the caller may not see it.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.inbox.thread |
| MCP tool | conversation_inbox_thread |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
}Output
{
thread: Record<string, unknown> | null
}The threads the caller archived for themselves («Archived»), newest first.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.inbox.archived |
| MCP tool | conversation_inbox_archived |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{}Output
{
threads: Record<string, unknown>[]
}Matches in the titles and shared message text of the threads the caller can see.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.inbox.search |
| MCP tool | conversation_inbox_search |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
query: string // ≤ 200
}Output
{
hits: Record<string, unknown>[]
}One page of messages of a thread the caller can see, with its pending confirmations. cursor pages back through history; after reads only what follows the newest sequence the caller holds.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.messages.page |
| MCP tool | conversation_messages_page |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{
conversationId: string // uuid
cursor?: string // ≤ 30
after?: string
}Output
{
items: Array<{
id: string
sequence: integer // -9007199254740991–9007199254740991
role: string // `user`, `assistant` or `system`.
content: string
attachments: Array<{
path: string
name: string
mime: string
size: number
url: string
}>
author: {
id: string
name: string
avatarUrl: string | null
isStaff: boolean
}
| null
visibility: string
channelMeta: {
via?: string
}
| null
runId: string | null
parts: unknown[]
createdAt: string
}>
confirmations: Record<string, {
status: string
mine: boolean
}>
nextCursor: string | null
activeRun: {
id: string
status: string
partialText: string | null // What Nödo has written so far.
}
| null
latestRun: {
id: string
status: string
errorCode: string | null
}
| null
version: integer // -9007199254740991–9007199254740991
}The most recent open team thread of the brand's main conversation (not a work's) the caller can see, with its first page of messages; nulls when there is none.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.team.latest |
| MCP tool | conversation_team_latest |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{}Output
{
conversationId: string | null
page: Record<string, unknown> | null
}Whether the caller may write, create internal notes, manage billing from a usage card, whether they act as the team, and their name; whether the brand's plan includes a Brand Manager (has_brand_manager), the current plan with its mark, and the plans that include one.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.access |
| MCP tool | conversation_access |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{}Output
{
canWrite: boolean
canCreateInternal: boolean
isStaff: boolean
canManageBilling: boolean
viewer: {
id: string
name: string
}
planIncludesManager: boolean // default true
plan: {
name: string
mark: string | null
}
| null // default null
managerPlans: Array<{
name: string
mark: string | null
}> // default []
}What waits for the brand or changed today, one row each with the place to act on it, plus the Store's best match for what the brand needs most, in the reader's language. It is the «Today» card above a new conversation with Nödo, and summary is the same in words. No model.
| Field | Value |
|---|---|
| Kind | Read |
| HTTP method | POST /api/v1/conversation.today |
| MCP tool | conversation_today |
| Scope | conversation.read |
| Minimum role | Viewer |
| Human confirmation | No |
| Doors | API, MCP, Screens, Apps, Nödo |
| Status | Available |
Input
{}Output
{
day: string
text: string
rows: Array<{
kind: "approval" | "result" | "calendar" | "task" | "genome" | "setup" | "connection" | "question" | "stats" | "offer"
title: string // ≤ 160
meta?: string // ≤ 200
href?: string // ≤ 300
action?: "open" | "review" | "answer" | "connect"
}> // ≤ 6 items
summary: string // ≤ 1500
}Before dictating: returns a single-use signed upload URL (2 h) for the audio (up to 12 MB; webm, m4a/mp4, mp3, wav, ogg, aac, flac) and the storagePath to pass to conversation.dictation.transcribe. PUT the bytes with their content type. Refusals: invalid_input (unsupported_audio, audio_too_large).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.dictation.prepare_upload |
| MCP tool | conversation_dictation_prepare_upload |
| Scope | conversation.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | Apps |
| Status | Available |
Input
{
mimeType: string // ≤ 100
fileSize: integer // –9007199254740991
}Output
{
storagePath: string
uploadUrl: string // uri
token: string
contentType: "audio/webm" | "audio/mp4" | "audio/mpeg" | "audio/wav" | "audio/ogg" | "audio/aac" | "audio/flac"
}Transcribes the audio uploaded with conversation.dictation.prepare_upload and deletes it. Returns { text }; the person still decides to send. brandId, when the caller reaches that brand, only attributes the cost; language is a hint and context the tail of what was dictated just before (segments). Refusals: not_found (no such upload of the caller), invalid_input (unsupported_audio, audio_too_large), unavailable (transcription_failed).
| Field | Value |
|---|---|
| Kind | Write |
| HTTP method | POST /api/v1/conversation.dictation.transcribe |
| MCP tool | conversation_dictation_transcribe |
| Scope | conversation.write |
| Minimum role | self |
| Human confirmation | No |
| Doors | Apps |
| Status | Available |
Input
{
storagePath: string // ≤ 300
brandId?: string | null
language?: string | null
context?: string | null
}Output
{
text: string
}Updated on January 1, 1970