Overview
The Message API provides access to chat conversation history between users and the AI assistant about specific documents.
Currently, the Message API only supports retrieving messages. Message creation is handled through a separate AI chat system.
Procedures
getAllByDocId
Retrieve all chat messages for a specific document. Available to document owners and collaborators.
The document ID to retrieve messages for
Returns: Array of messages with role identification
Unique message identifier
Message content parts (structure depends on AI provider format)
The message sender role:
user - Message from the user
assistant - Message from the AI
Example:
import { api } from "@/lib/api" ;
function ChatHistory ({ docId } : { docId : string }) {
const { data : messages , isLoading } = api . message . getAllByDocId . useQuery ({
docId
});
if ( isLoading ) return < div > Loading chat history ...</ div > ;
return (
< div className = "chat-history" >
{ messages ?. map (( msg ) => (
< div
key = {msg. id }
className = {msg. role === "user" ? "user-message" : "ai-message" }
>
< strong >{msg. role === "user" ? "You" : "AI" } : </ strong >
< div >{JSON.stringify(msg.parts)} </ div >
</ div >
))}
</ div >
);
}
Data Models
Message Schema
interface Message {
id : string ;
createdAt : Date ;
userId : string | null ; // null for AI messages
documentId : string ;
parts : Json | null ; // Message content in JSON format
}
Message Parts
The parts field contains the message content in a JSON structure. The exact format depends on the AI provider being used (e.g., OpenAI, Anthropic, etc.).
Common structure:
interface MessageParts {
type : "text" | "image" | "code" ;
content : string ;
metadata ?: {
sources ?: string []; // Document sections referenced
confidence ?: number ; // AI confidence score
tokens ?: number ; // Token count
};
}
Role Determination
The role field is determined by the presence of a userId:
user : Message has a userId (sent by a user)
assistant : Message has no userId (generated by AI)
const role = message . userId ? "user" : "assistant" ;
Authorization
Messages can be retrieved by:
The document owner, OR
Any collaborator (regardless of role)
This allows all team members to see the full conversation history.
// Authorization check in the API
const res = await ctx . prisma . document . findUnique ({
where: {
id: input . docId ,
OR: [
{ ownerId: ctx . session . user . id },
{
collaborators: {
some: {
userId: ctx . session . user . id ,
},
},
},
],
},
select: {
messages: true ,
},
});
Message Ordering
Messages are returned in the order they were created (oldest first), as determined by the database’s natural ordering.
For reverse chronological order, sort on the client:
const { data : messages } = api . message . getAllByDocId . useQuery ({ docId });
const sortedMessages = messages ?. sort (( a , b ) =>
b . createdAt . getTime () - a . createdAt . getTime ()
);
Use Cases
Display Chat History
function DocumentChat ({ docId } : { docId : string }) {
const { data : messages } = api . message . getAllByDocId . useQuery ({ docId });
return (
< div >
{ messages ?. map (( msg ) => (
< ChatBubble key = {msg. id } message = { msg } />
))}
</ div >
);
}
Export Conversation
function ExportChat ({ docId } : { docId : string }) {
const { data : messages } = api . message . getAllByDocId . useQuery ({ docId });
const exportToText = () => {
const text = messages
?. map ( msg => ` ${ msg . role . toUpperCase () } : ${ JSON . stringify ( msg . parts ) } ` )
. join ( ' \n\n ' );
// Download or copy to clipboard
navigator . clipboard . writeText ( text );
};
return < button onClick ={ exportToText }> Export Chat </ button > ;
}
Search Messages
function SearchMessages ({ docId , query } : { docId : string ; query : string }) {
const { data : messages } = api . message . getAllByDocId . useQuery ({ docId });
const filteredMessages = messages ?. filter ( msg =>
JSON . stringify ( msg . parts ). toLowerCase (). includes ( query . toLowerCase ())
);
return (
< div >
{ filteredMessages ?. map ( msg => (
< SearchResult key = {msg. id } message = { msg } />
))}
</ div >
);
}
Error Handling
Common errors:
UNAUTHORIZED - User doesn’t have access to the document
const { data , error } = api . message . getAllByDocId . useQuery (
{ docId },
{
onError : ( error ) => {
if ( error . data ?. code === "UNAUTHORIZED" ) {
toast . error ( "You don't have access to this document's chat" );
router . push ( "/dashboard" );
}
},
}
);
Caching Messages are cached by React Query. Use refetchInterval for real-time updates: const { data } = api . message . getAllByDocId . useQuery (
{ docId },
{ refetchInterval: 5000 } // Poll every 5 seconds
);
Pagination For documents with many messages, consider implementing pagination or infinite scroll on the client side.
Future Enhancements
Planned features for the Message API:
Create new messages via API
Delete or edit messages
Message reactions or annotations
Real-time message streaming
Message search and filtering on the server
Pagination support