This document provides a comprehensive reference for all tRPC API endpoints available in the CityU Vet Sim application. All endpoints are type-safe and can be accessed via the tRPC client.
All endpoints are either:
publicProcedure)protectedProcedure)Protected endpoints automatically have access to ctx.session.user containing the authenticated user's information.
Router Path: api.elevenlabs.*
Endpoints for managing ElevenLabs conversational AI integration.
getAgentIdType: Query
Authentication: Public
Description: Returns the ElevenLabs agent ID configured in environment variables.
Input: None
Output:
string // The ElevenLabs agent ID
Example:
const agentId = await api.elevenlabs.getAgentId.useQuery();
getCustomPromptType: Query
Authentication: Public
Description: Generates a custom system prompt for a specific skill and case combination. This prompt is used to configure the AI pet owner's behavior and persona.
Input:
{
skillId: number; // The ID of the communication skill being practiced
caseId: string; // The ID of the veterinary case scenario
}
Output:
string // The generated custom prompt
Example:
const { data: prompt } = api.elevenlabs.getCustomPrompt.useQuery({
skillId: 1,
caseId: "case-123"
});
Errors:
NOT_FOUND: If the skill or case with the provided IDs cannot be foundINTERNAL_SERVER_ERROR: If prompt generation failsgetConversationTokenType: Query
Authentication: Public
Description: Generates a conversation token required for WebRTC connection with private ElevenLabs agents that have authentication enabled.
Input: None
Output:
string // The conversation token
Example:
const token = await api.elevenlabs.getConversationToken.useQuery();
Errors:
INTERNAL_SERVER_ERROR: If the token request to ElevenLabs failsgetSignedUrlType: Query
Authentication: Public
Description: Generates a signed URL for WebSocket connection with ElevenLabs, required for private agents with authentication enabled.
Input: None
Output:
string // The signed URL for WebSocket connection
Example:
const signedUrl = await api.elevenlabs.getSignedUrl.useQuery();
Errors:
INTERNAL_SERVER_ERROR: If the signed URL request to ElevenLabs failsRouter Path: api.voice.*
Endpoints for voice-based conversations using OpenAI's audio capabilities.
converseType: Mutation
Authentication: Protected
Description: Processes voice input and generates an AI response with both text and audio output. This endpoint handles speech-to-text transcription and text-to-speech generation in parallel.
Input:
{
audioBase64: string; // Base64-encoded WebM audio data (from MediaRecorder)
history?: Array<{ // Optional conversation history
role: "user" | "assistant";
content: string;
}>;
skillId?: number; // Optional skill ID for prompt generation
caseId?: string; // Optional case ID for prompt generation
}
Output:
{
userText: string; // Transcribed user speech (from Whisper)
assistantText: string; // AI's textual response
assistantAudioBase64: string; // AI's audio response (WAV, base64-encoded)
}
Example:
const converse = api.voice.converse.useMutation();
const result = await converse.mutateAsync({
audioBase64: "data:audio/webm;base64,...",
history: [
{ role: "user", content: "Hello" },
{ role: "assistant", content: "Hi there!" }
],
skillId: 1,
caseId: "case-123"
});
Process:
skillId and caseId are providedgpt-4o-mini-transcribegpt-4o-mini-audio-previewErrors:
INTERNAL_SERVER_ERROR: If audio processing or API calls failRouter Path: api.evaluation.*
Endpoints for evaluating student performance and generating feedback.
evaluateType: Mutation
Authentication: Protected
Description: Analyzes a student's conversation transcript and generates personalized feedback based on the practiced skill, case scenario, and student's personality profile.
Input:
{
conversation: Array<{
role: "user" | "assistant"; // "user" = student, "assistant" = AI pet owner
content: string;
}>;
skillId?: number; // Optional skill ID for context-specific evaluation
caseId?: string; // Optional case ID for case-specific evaluation
}
Output:
{
feedback: string; // Generated feedback text
}
Example:
const evaluate = api.evaluation.evaluate.useMutation();
const result = await evaluate.mutateAsync({
conversation: [
{ role: "user", content: "Hello, how can I help you today?" },
{ role: "assistant", content: "Hi, my dog has been limping..." }
],
skillId: 1,
caseId: "case-123"
});
Process:
gpt-5-mini modelErrors:
BAD_REQUEST: If conversation is empty or lacks meaningful contentINTERNAL_SERVER_ERROR: If feedback generation failsNote: Feedback is tailored to the student's personality profile (four-letter code: [IO][SF][XV][LE]) to provide more relevant and effective guidance.
Router Path: api.user.*
Endpoints for managing user profile and settings.
setPersonalityType: Mutation
Authentication: Protected
Description: Updates the authenticated user's personality profile. The personality code follows a four-letter format representing different personality dimensions.
Input:
{
personality: string; // Four-letter code: [IO][SF][XV][LE]
// I/O: Inner/Outer World
// S/F: Structure/Flow
// X/V: Experience/Envisioning
// L/E: Logic/Emotion
}
Output:
{
success: boolean;
}
Example:
const setPersonality = api.user.setPersonality.useMutation();
await setPersonality.mutateAsync({
personality: "ISXL" // Inner, Structure, Experience, Logic
});
Validation:
/^[IO][SF][XV][LE]$/Errors:
getPersonalityType: Query
Authentication: Protected
Description: Retrieves the authenticated user's personality profile.
Input: None
Output:
{
personality: string | null; // The user's personality code, or null if not set
} | undefined
Example:
const { data: userPersonality } = api.user.getPersonality.useQuery();
// Returns: { personality: "ISXL" } or { personality: null }
Router Path: api.notes.*
Endpoints for managing simulation notes associated with specific skill-case combinations.
getNotesType: Query
Authentication: Protected
Description: Retrieves saved notes for a specific skill and case combination for the authenticated user.
Input:
{
skillId: string; // The ID of the communication skill
caseId: string; // The ID of the veterinary case
}
Output:
string // The saved notes text, or empty string if no notes exist
Example:
const { data: notes } = api.notes.getNotes.useQuery({
skillId: "1",
caseId: "case-123"
});
saveNotesType: Mutation
Authentication: Protected
Description: Saves or updates notes for a specific skill and case combination. If notes already exist, they are updated; otherwise, a new record is created.
Input:
{
skillId: string; // The ID of the communication skill
caseId: string; // The ID of the veterinary case
notes: string; // The notes text to save
}
Output:
{
success: boolean;
}
Example:
const saveNotes = api.notes.saveNotes.useMutation();
await saveNotes.mutateAsync({
skillId: "1",
caseId: "case-123",
notes: "Remember to ask about vaccination history next time."
});
Process:
updatedAt timestamp on updateRouter Path: api.video.*
Endpoints for retrieving video URLs from Vercel Blob storage.
getVideoUrlType: Query
Authentication: Public
Description: Retrieves the public URL for a single video file stored in Vercel Blob storage.
Input:
{
filename: string; // The filename of the video (e.g., "skill1-aim3.mp4")
}
Output:
string // The full URL to access the video, or placeholder URL if not found
Example:
const { data: videoUrl } = api.video.getVideoUrl.useQuery({
filename: "skill1-aim3.mp4"
});
Note: Returns a placeholder URL (https://example.com/{filename}) if the video file is not found or inaccessible. Check the console for warnings.
getVideoUrlsType: Query
Authentication: Public
Description: Efficiently retrieves public URLs for multiple video files stored in Vercel Blob storage.
Input:
{
filenames: string[]; // Array of video filenames
}
Output:
Record<string, string> // Object mapping filename to URL
// Example: { "video1.mp4": "https://...", "video2.mp4": "https://..." }
Example:
const { data: videoUrls } = api.video.getVideoUrls.useQuery({
filenames: ["skill1-aim3.mp4", "skill2-aim5.mp4"]
});
// Returns: { "skill1-aim3.mp4": "https://...", "skill2-aim5.mp4": "https://..." }
Note: Returns placeholder URLs for any files that are not found or inaccessible. Check the console for warnings.
Router Path: api.post.*
Example router endpoints (from T3 Stack boilerplate). These endpoints demonstrate basic CRUD operations but may not be actively used in the veterinary simulation application.
helloType: Query
Authentication: Public
Description: A simple greeting endpoint for testing purposes.
Input:
{
text: string; // A text string to include in the greeting
}
Output:
{
greeting: string; // Example: "Hello {text}"
}
Example:
const { data } = api.post.hello.useQuery({ text: "World" });
// Returns: { greeting: "Hello World" }
createType: Mutation
Authentication: Public
Description: Creates a new post record in the database.
Input:
{
name: string; // Post name (minimum 1 character)
}
Output:
void
Example:
const create = api.post.create.useMutation();
await create.mutateAsync({ name: "My Post" });
getLatestType: Query
Authentication: Public
Description: Retrieves the most recently created post from the database.
Input: None
Output:
{
id: number;
name: string;
createdAt: Date;
updatedAt: Date | null;
} | null // Returns null if no posts exist
Example:
const { data: latestPost } = api.post.getLatest.useQuery();
All endpoints follow tRPC's error handling conventions:
BAD_REQUEST: Invalid input or request parametersUNAUTHORIZED: Authentication required but not provided (for protected endpoints)NOT_FOUND: Requested resource not foundINTERNAL_SERVER_ERROR: Server-side error during processingErrors include a message field with details and may include a cause field with the underlying error.
All endpoints are fully type-safe. TypeScript types are automatically inferred from the router definitions:
// Input types
import type { RouterInputs } from "@/trpc/react";
type ElevenLabsGetCustomPromptInput = RouterInputs["elevenlabs"]["getCustomPrompt"];
// Output types
import type { RouterOutputs } from "@/trpc/react";
type EvaluationEvaluateOutput = RouterOutputs["evaluation"]["evaluate"];
import { api } from "@/trpc/react";
// In a React component
function MyComponent() {
const { data, isLoading, error } = api.elevenlabs.getAgentId.useQuery();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Agent ID: {data}</div>;
}
import { api } from "@/trpc/react";
function MyComponent() {
const utils = api.useUtils();
const saveNotes = api.notes.saveNotes.useMutation({
onSuccess: () => {
// Invalidate and refetch notes
utils.notes.getNotes.invalidate();
},
});
const handleSave = () => {
saveNotes.mutate({
skillId: "1",
caseId: "case-123",
notes: "My notes here"
});
};
return <button onClick={handleSave}>Save Notes</button>;
}