Skip to content

Timeline Architecture

The timeline is the immutable audit log of Ikamet OS. Every meaningful action — status change, communication sent, task completed, document uploaded, payment received — writes a TimelineEvent.

Core principle

writeTimelineEvent() is the single entry point for all timeline writes. Never write to the TimelineEvent table directly in a route handler.

TimelineEvent structure

interface TimelineEvent {
id: string
event_type: string // e.g. "STATUS_CHANGE", "DOCUMENT_UPLOADED"
title: string // Human-readable summary
body?: string // Optional detail / notes
metadata?: object // Machine-readable extra data
staff?: StaffMember // Who triggered it (null = system)
createdAt: Date
// Polymorphic links — one of these is set per event:
user_id?: string // Customer timeline
order_id?: string // Order timeline
policy_id?: string // Policy timeline
task_id?: string // Task timeline
renewal_id?: string // Renewal timeline
// ... other entity links
}

Event types

Event typeTriggered when
STATUS_CHANGEAny workflow status updates
ORDER_CREATEDNew order placed
POLICY_ISSUEDInsurance policy issued
DOCUMENT_UPLOADEDCustomer uploads a document
DOCUMENT_REJECTEDStaff rejects a document
COMMUNICATION_SENTWhatsApp / email / SMS sent
COMMUNICATION_RECEIVEDInbound WhatsApp / email
PAYMENT_RECEIVEDPayment confirmed
PAYMENT_FAILEDPayment declined
TASK_CREATEDNew task created
TASK_COMPLETEDTask marked complete
RENEWAL_TRIGGEREDRenewal threshold crossed
RENEWAL_COMPLETEDRenewal processed
NOTE_ADDEDInternal note written
AGENT_ASSIGNEDCustomer assigned to agent
COMPLIANCE_FLAGCompliance issue flagged
SYSTEM_EVENTAutomated system action

Writing timeline events

import { writeTimelineEvent } from '@/lib/timeline.service';
// Direct write
await writeTimelineEvent({
event_type: 'DOCUMENT_UPLOADED',
title: 'Passport uploaded',
body: 'Customer uploaded passport scan for verification.',
user_id: customer.id,
staff_id: req.staff?.id,
});
// Convenience wrapper for status changes
await writeStatusChange(
user_id,
'Residency Application',
'SUBMITTED',
'UNDER_REVIEW',
{ staff_id: req.staff.id, order_id: order.id }
);

Reading timeline events

The getCustomerTimeline() function fetches events for a customer, ordered newest-first, with staff details included:

const timeline = await getCustomerTimeline(userId, { limit: 100 });
// Returns: { data: TimelineEvent[], total: number }

Why timeline-first?

  1. Auditability — every action is traceable to a person, time, and context
  2. Debugging — when something goes wrong, the timeline shows exactly what happened
  3. Customer visibility — the customer-facing portal can show appropriate timeline events
  4. AI context — the timeline gives AI agents the full operational history of a customer
  5. Compliance — regulated workflows (immigration, insurance) require complete audit trails