Documentation Index
Fetch the complete documentation index at: https://docs.renchi.ai/llms.txt
Use this file to discover all available pages before exploring further.
Type Organization
/web/src/lib/schemas/ — Zod validation schemas
auth.ts — Authentication validation
team.ts — Team/organization validation
customer.ts — Customer data validation
job.ts — Job/ticket validation
agent-config.ts — Agent configuration validation
/web/src/lib/types/ — TypeScript type definitions
conversation.ts, team.ts, agent-config.ts
knowledge-base.ts, sentiment.ts, customer.ts
job.ts, call-notes.ts, inbox.ts, extraction.ts
/web/src/lib/integrations/ — Third-party integration types
types.ts
elevenlabs/types.ts
twilio/types.ts
Core Types
Conversation Types
Full conversation data model for call handling:
interface ConversationDetail {
id: string;
customer: CustomerInfo;
metadata: CallMetadata;
problem: ProblemDetails;
booking?: BookingDetails;
transfer?: TransferInfo;
analysis: AIAnalysis;
transcript: TranscriptEntry[];
recording?: RecordingInfo;
notes: ConversationNote[];
}
// Problem categories for UK HVAC/plumbing
type ProblemCategory =
| "annual_service"
| "boiler_breakdown"
| "blocked_drain"
| "leak_repair"
| "gas_safety"
| "emergency_callout"
| "technical_question"
| "warranty"
| "other";
// Call outcomes
type CallOutcome =
| "booked"
| "transferred"
| "resolved"
| "callback"
| "abandoned"
| "voicemail";
// Urgency levels
type UrgencyLevel = "normal" | "high" | "emergency";
Customer Types
Customer management with full history tracking:
interface Customer {
id: string;
firstName: string;
lastName: string;
email?: string;
phone: string;
addressLine1?: string;
addressLine2?: string;
city?: string;
postcode?: string;
companyName?: string;
status: CustomerStatus;
type: CustomerType;
createdAt: Date;
updatedAt: Date;
}
type CustomerStatus = "active" | "new" | "inactive";
type CustomerType = "residential" | "commercial";
// Filtering options
interface CustomerFilters {
search: string;
status: CustomerStatus | "all";
postcode: string;
callVolume: CallVolumeLevel;
lastContact: LastContactPeriod;
}
Agent Configuration Types
AI agent configuration with business hours, services, and transfer routing:
interface AgentConfig {
// Identity
name: string;
persona: string;
greeting: string;
// Voice settings
voiceModel: string;
speechRate: number;
afterHoursGreeting?: string;
// Business hours
businessHours: BusinessHours;
// Services offered
services: Service[];
// Transfer configuration
transferConfig: TransferConfig;
// Emergency handling
emergencyKeywords: string[];
}
interface BusinessHours {
monday: DaySchedule;
tuesday: DaySchedule;
wednesday: DaySchedule;
thursday: DaySchedule;
friday: DaySchedule;
saturday: DaySchedule;
sunday: DaySchedule;
}
interface DaySchedule {
enabled: boolean;
start: string; // HH:MM format
end: string;
}
interface Service {
id: string;
label: string;
description?: string;
enabled: boolean;
price?: number;
premium?: boolean;
}
Job/Ticket Types
Service ticket management:
interface Job {
id: string;
jobNumber: string;
customerId: string;
callId?: string;
status: JobStatus;
jobType: JobType;
appointmentDate?: Date;
timeSlot?: string;
technicianName?: string;
technicianPhone?: string;
technicianNotes?: string;
quotedPriceCents?: number;
finalPriceCents?: number;
createdAt: Date;
updatedAt: Date;
}
type JobStatus = "open" | "scheduled" | "completed" | "closed";
type JobType =
| "annual_service"
| "boiler_breakdown"
| "blocked_drain"
| "leak_repair"
| "gas_safety"
| "emergency_callout"
| "technical_question"
| "warranty"
| "other";
// Status transitions (validated)
// open → scheduled → completed → closed
Integration Types
Twilio Types
interface PhoneLine {
id: string;
teamId: string;
phoneNumber: string;
friendlyName: string;
twilioSid: string;
capabilities: {
voice: boolean;
sms: boolean;
mms: boolean;
};
monthlyCostCents: number;
isActive: boolean;
}
interface Call {
id: string;
phoneLineId: string;
customerId?: string;
direction: "inbound" | "outbound";
status: CallStatus;
duration?: number;
recordingUrl?: string;
transcriptSegments?: TranscriptSegment[];
aiSummary?: CallAISummary;
}
type CallStatus =
| "queued"
| "ringing"
| "in-progress"
| "completed"
| "busy"
| "failed"
| "no-answer"
| "canceled";
ElevenLabs Types
// Webhook event types
type ElevenLabsWebhookEvent =
| ElevenLabsPostCallTranscriptionEvent
| ElevenLabsPostCallAudioEvent
| ElevenLabsCallInitiationFailureEvent;
interface ElevenLabsPostCallTranscriptionEvent {
type: "post_call_transcription";
event_timestamp: number;
data: {
agent_id: string;
conversation_id: string;
status: string;
transcript: ElevenLabsTranscriptEntry[];
metadata: Record<string, unknown>;
analysis: ElevenLabsAnalysis;
};
}
interface ElevenLabsTranscriptEntry {
role: "agent" | "user";
message: string;
time_in_call_secs: number;
end_time_in_call_secs: number;
tool_calls?: unknown[];
feedback?: unknown;
}
Zod Schemas
All major types have corresponding Zod schemas for runtime validation:
Authentication Schema
import { z } from "zod";
export const loginSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(1, "Password is required"),
});
export const signupSchema = z.object({
email: z.string().email("Invalid email address"),
password: z
.string()
.min(8, "Password must be at least 8 characters")
.regex(/[A-Z]/, "Must contain uppercase letter")
.regex(/[a-z]/, "Must contain lowercase letter")
.regex(/[0-9]/, "Must contain number"),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"],
});
Customer Schema
export const customerSchema = z.object({
firstName: z.string().min(1, "First name is required"),
lastName: z.string().min(1, "Last name is required"),
email: z.string().email().optional().or(z.literal("")),
phone: z.string().min(10, "Valid phone number required"),
addressLine1: z.string().optional(),
city: z.string().optional(),
postcode: z.string().regex(/^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i, "Invalid UK postcode").optional(),
companyName: z.string().optional(),
type: z.enum(["residential", "commercial"]),
});
Job Schema
export const jobSchema = z.object({
customerId: z.string().uuid(),
jobType: z.enum([
"annual_service",
"boiler_breakdown",
"blocked_drain",
"leak_repair",
"gas_safety",
"emergency_callout",
"technical_question",
"warranty",
"other",
]),
appointmentDate: z.date().optional(),
timeSlot: z.string().regex(/^\d{2}:\d{2}$/).optional(),
technicianName: z.string().optional(),
quotedPriceCents: z.number().int().min(0).optional(),
});
Type Utilities
Status Labels and Colors
Each enum-like type has corresponding label and color mappings:
export const JOB_STATUS_LABELS: Record<JobStatus, string> = {
open: "Open",
scheduled: "Scheduled",
completed: "Completed",
closed: "Closed",
};
export const JOB_STATUS_COLORS: Record<JobStatus, { bg: string; text: string; border: string }> = {
open: { bg: "bg-blue-50", text: "text-blue-700", border: "border-blue-200" },
scheduled: { bg: "bg-yellow-50", text: "text-yellow-700", border: "border-yellow-200" },
completed: { bg: "bg-green-50", text: "text-green-700", border: "border-green-200" },
closed: { bg: "bg-zinc-50", text: "text-zinc-700", border: "border-zinc-200" },
};
Conversion Functions
Snake_case database rows are converted to camelCase TypeScript types:
export function toCustomer(row: CustomerRow): Customer {
return {
id: row.id,
firstName: row.first_name,
lastName: row.last_name,
email: row.email,
phone: row.phone,
// ... more fields
};
}
Best Practices
- Use Zod for validation - All user input should be validated with Zod schemas
- Type conversion at boundaries - Convert database rows to domain types at the data layer
- Enum labels and colors - Use the parallel
*_LABELS and *_COLORS objects for display
- Partial schemas for forms - Use
.pick() for tab-level validation in multi-step forms