API Guide
This guide provides an overview of the MSR (Multi-Session Replay) module's API endpoints. The API is used for managing recorded sessions, querying session metadata, and controlling the replay process.
Authentication
All API endpoints require a valid JWT access token from IAMs. The token must be included in the Authorization header of each request.
Headers:
Content-Type: application/jsonAuthorization: Bearer {token}
Session API
1. Create a session
POST /session
Request
No request body.
Response
| Name | Type | Description |
|---|---|---|
| id | uuid | The ID of the session |
| user_id | uuid | The user_id that session belongs to |
| tenant_id | uuid | The tenant_id that session belongs to |
| created_at | string | The timestamp in RFC3339 format |
| update_at | string | The timestamp in RFC3339 format |
| created_by | uuid | The ID of the user who created the session |
| updated_by | uuid | The ID of the user who updated the session |
| status | string | The status of the session: ACTIVE, INACTIVE |
| occ_lock | number | The occ lock of the session |
{
"data": {
"id": "4a966973-2131-4521-be48-e3d79306a847",
"user_id": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"tenant_id": "2878ac59-ad8b-4a94-bd97-2d9ef371fc06",
"created_at": "2025-08-05T17:13:24+07:00",
"update_at": "2025-08-05T17:19:05+07:00",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"status": "ACTIVE",
"occ_lock": 0
},
"message": "Created session successfully",
"sent_at": "2025-08-05T10:19:05Z"
}
2. Get initial state
- Return the state of the session at the given timestamp. If the timestamp is not provided, return the current state of the session.
- Returns the full, reconstructed state of all entities at a specific point in time.
GET /replay/state?timestamp={timestamp}
Optional query parameters for filtering:
- filter_tables — comma-separated list of tables to filter (e.g., patients,medications)
- filter_min_timestamp — ISO timestamp; events older than this are excluded for the specified tables
Example:
GET /replay/state?timestamp=2024-06-15T10:00:00Z&filter_tables=patients,medications&filter_min_timestamp=2024-06-08T10:00:00Z
The requested timestamp must be within the available data range. If the timestamp is:
- Too early: Returns an error with the earliest available timestamp based on
EARLIEST_VALID_TIMESTAMPor snapshot cutoff - In the future: Returns an error indicating future timestamps are not supported
- Outside MAX_PLAYBACK_RANGE: Returns an error with the configured playback limit (default: 7 days)
Response
| Name | Type | Description |
|---|---|---|
| entity_id | string | The unique id of the entity |
| entity_state | JSONB | The state of the entity |
| op | string | The operation: c, r, u, d |
| event_timestamp | string | The timestamp in RFC3339 format |
{
"data": [
{
"entity_id": "655e82ca-678c-47f5-8d81-5091c39f8d63",
"entity_state": {
"id": "611f2472-d781-4870-b5fc-69b90bafc067",
"geojson": "{\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[103.8493269960454, 1.391235132350934], [103.8493269960454, 1.391235132350934]]]}, \"properties\": {\"kind\": \"task\", \"name\": \"ST Engineering\"}}",
"occ_lock": 0,
"tenant_id": "2878ac59-ad8b-4a94-bd97-2d9ef371fc06",
"created_at": "2025-06-09T03:58:14.762000Z",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_at": "2025-06-09T03:58:14.762000Z",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"entity_type": "static"
},
"op": "r",
"event_timestamp": "2025-08-01T14:15:14+07:00"
},
{
"entity_id": "8fbeedce-cbca-4aee-a01f-0ee23b583797",
"entity_state": {
"id": "e10e029a-6971-41af-9791-0378fe6f699b",
"geojson": "{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [103.8493269960454, 1.391235132350934]}, \"properties\": {\"kind\": \"task\", \"name\": \"ST Engineering\"}}",
"occ_lock": 0,
"tenant_id": "2878ac59-ad8b-4a94-bd97-2d9ef371fc06",
"created_at": "2025-06-09T04:00:31.229000Z",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_at": "2025-06-09T04:00:31.229000Z",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"entity_type": "static"
},
"op": "r",
"event_timestamp": "2025-08-01T14:15:14+07:00"
}
],
"message": "Get initial state successfully",
"sent_at": "2025-08-07T02:25:38Z"
}
3. Get transaction logs
- Returns the transaction logs between the start and end timestamps.
GET /replay/events?start={timestamp}&end={timestamp}
Optional query parameters for filtering:
- filter_tables — comma-separated list of tables to filter (e.g., audit_logs,activity_logs)
- filter_min_timestamp — ISO timestamp; events older than this are excluded for the specified tables
Example:
GET /replay/events?start=2024-06-15T10:00:00Z&end=2024-06-15T12:00:00Z&filter_tables=audit_logs,activity_logs&filter_min_timestamp=2024-06-14T10:00:00Z
Response
| Name | Type | Description |
|---|---|---|
| entity_id | string | The unique id of the entity |
| entity_state | JSONB | The state of the entity |
| op | string | The operation: c, r, u, d |
| event_timestamp | string | The timestamp in RFC3339 format |
{
"data": [
{
"entity_id": "655e82ca-678c-47f5-8d81-5091c39f8d63",
"entity_state": {
"id": "611f2472-d781-4870-b5fc-69b90bafc067",
"geojson": "{\"type\": \"Feature\", \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[103.8493269960454, 1.391235132350934], [103.8493269960454, 1.391235132350934]]]}, \"properties\": {\"kind\": \"task\", \"name\": \"ST Engineering\"}}",
"occ_lock": 0,
"tenant_id": "2878ac59-ad8b-4a94-bd97-2d9ef371fc06",
"created_at": "2025-06-09T03:58:14.762000Z",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_at": "2025-06-09T03:58:14.762000Z",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"entity_type": "static"
},
"op": "r",
"event_timestamp": "2025-08-01T14:15:14+07:00"
},
{
"entity_id": "8fbeedce-cbca-4aee-a01f-0ee23b583797",
"entity_state": {
"id": "e10e029a-6971-41af-9791-0378fe6f699b",
"geojson": "{\"type\": \"Feature\", \"geometry\": {\"type\": \"Point\", \"coordinates\": [103.8493269960454, 1.391235132350934]}, \"properties\": {\"kind\": \"task\", \"name\": \"ST Engineering\"}}",
"occ_lock": 0,
"tenant_id": "2878ac59-ad8b-4a94-bd97-2d9ef371fc06",
"created_at": "2025-06-09T04:00:31.229000Z",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_at": "2025-06-09T04:00:31.229000Z",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"entity_type": "static"
},
"op": "r",
"event_timestamp": "2025-08-01T14:15:14+07:00"
}
],
"message": "Successfully retrieved transaction logs.",
"sent_at": "2025-08-07T02:25:38Z"
}
4. Delete a session
DELETE /session/{id}
Request
| Parameter | Type | Description |
|---|---|---|
| id | uuid | The unique identifier of the session to delete (path parameter) |
Response
HTTP status code 204 with message "Deleted session successfully".
5. Terminate a session
POST /session/{session_id}/terminate
This endpoint only allows users to terminate sessions they own. To terminate sessions for other users, use the admin endpoint POST /admin/sessions/terminate.
Response
HTTP status code 204.
6. Admin terminate multiple sessions
POST /admin/sessions/terminate
This admin endpoint allows terminating sessions for any user. For terminating your own sessions, you can use the user endpoint POST /session/{session_id}/terminate.
Request Body
| Name | Type | Description |
|---|---|---|
| session_ids | array | Array of session IDs (UUIDs) to terminate |
{
"session_ids": ["468a27ed-f6d9-4c7c-8772-0dafd4e79ac3", "8766a359-17a4-4dbe-b6e4-d0e73eabadc7"]
}
Admin API
7. Get sessions configuration
- Returns the session configuration: max_active_sessions, max_playback_range, data_retention_cron_expression, earliest_valid_timestamp
GET /admin/settings
Response
| Name | Type | Description |
|---|---|---|
| max_active_sessions | number | The maximum number of active sessions that can be created |
| max_playback_range | number | The maximum historical range allowed for playback (unit: day(s)) |
| data_retention_cron_expression | string | Cron expression for data retention cleanup schedule (standard cron format) |
| earliest_valid_timestamp | string | System deployment boundary timestamp in ISO 8601 format with timezone |
| query_work_mem_mb | number | Query work memory allocation in MB for database operations |
| columnstore_compression_age_days | number | Age in days after which columnstore data gets compressed |
| cagg_refresh_interval_hours | number | Continuous aggregate refresh interval in hours |
| cagg_refresh_lag_days | number | Continuous aggregate refresh lag in days |
{
"data": {
"max_active_sessions": 10,
"max_playback_range": 30,
"data_retention_cron_expression": "0 2 * * *",
"earliest_valid_timestamp": "2024-01-15T00:00:00Z",
"query_work_mem_mb": 256,
"columnstore_compression_age_days": 7,
"cagg_refresh_interval_hours": 1,
"cagg_refresh_lag_days": 1
},
"sent_at": "2025-08-05T10:19:05Z"
}
8. Update configuration
- Update the runtime configuration for the service.
PUT /admin/settings
Request
| Name | Type | Description |
|---|---|---|
| max_active_sessions | number | The maximum number of active sessions that can be created (must be > 0) |
| max_playback_range | number | The maximum historical range allowed for playback in days (must be > 0) |
| data_retention_cron_expression | string | Cron expression for data retention cleanup schedule (standard cron format) |
| earliest_valid_timestamp | string | System deployment boundary timestamp in ISO 8601 format with timezone (YYYY-MM-DDTHH:MM:SSZ) |
| query_work_mem_mb | number | Query work memory allocation in MB for database operations (must be > 0) |
| columnstore_compression_age_days | number | Age in days after which columnstore data gets compressed (must be > 0) |
| cagg_refresh_interval_hours | number | Continuous aggregate refresh interval in hours (must be > 0) |
| cagg_refresh_lag_days | number | Continuous aggregate refresh lag in days (must be > 0) |
{
"max_active_sessions": 15,
"max_playback_range": 45,
"data_retention_cron_expression": "0 2 * * *",
"earliest_valid_timestamp": "2024-01-15T00:00:00Z",
"query_work_mem_mb": 512,
"columnstore_compression_age_days": 14,
"cagg_refresh_interval_hours": 2,
"cagg_refresh_lag_days": 2
}
Response
Returns the updated configuration values that were provided in the request.
{
"data": {
"max_active_sessions": 15,
"max_playback_range": 45,
"data_retention_cron_expression": "0 2 * * *",
"earliest_valid_timestamp": "2024-01-15T00:00:00Z",
"query_work_mem_mb": 512,
"columnstore_compression_age_days": 14,
"cagg_refresh_interval_hours": 2,
"cagg_refresh_lag_days": 2
},
"sent_at": "2025-09-04T10:19:05Z"
}
9. List sessions
GET /admin/sessions
Query params
| Name | Type | Description |
|---|---|---|
| tenant_id | string | The tenant id to filter session that belongs to tenant |
| status | string | The status to filter sessions (ACTIVE, INACTIVE) |
| page | number | Pagination page |
| size | number | Pagination size |
| sort | number | Condition for sorting the sessions data |
Response
| Name | Type | Description |
|---|---|---|
| id | uuid | The unique id of the session |
| user_id | uuid | The user id that session belongs to |
| status | string | The session status: ACTIVE, INACTIVE |
| last_seen_at | string | The timestamp of the last API call using this session |
| tenant_id | string | The tenant id that session belongs to |
| occ_lock | string | The a mutex for optimistic concurrency control |
| created_at | string | The time this session was created |
| updated_at | string | The time this session was last updated |
| number | int | The page from request |
| size | int | The size from request |
| total_records | int | The total records that match request query |
| count | int | The number of sessions in the current page |
| sort | string | The sort column and direction |
{
"data": [
{
"id": "468a27ed-f6d9-4c7c-8772-0dafd4e79ac3",
"user_id": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"status": "INACTIVE",
"last_seen_at": "2025-08-11T14:48:12+07:00",
"tenant_id": "6c17fb87-d715-439f-943f-69f01318adac",
"occ_lock": 2,
"created_at": "2025-08-11T14:47:38+07:00",
"update_at": "2025-08-11T14:48:12+07:00",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0"
},
{
"id": "8766a359-17a4-4dbe-b6e4-d0e73eabadc7",
"user_id": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db1",
"status": "ACTIVE",
"last_seen_at": "2025-08-11T14:43:48+07:00",
"tenant_id": "6c17fb87-d715-439f-943f-69f01318adac",
"occ_lock": 1,
"created_at": "2025-08-11T14:43:48+07:00",
"update_at": "2025-08-11T14:46:55+07:00",
"created_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0",
"updated_by": "f67cb8f5-0645-444d-a4bd-c61aaf1b2db0"
}
],
"message": "Retrieved list sessions successfully",
"sent_at": "2025-08-12T02:53:24Z",
"page": {
"number": 1,
"size": 2,
"total_records": 7,
"count": 2,
"sort": ["last_seen_at,desc"]
}
}
10. Get active sessions count
GET /admin/sessions/status
Response
| Name | Type | Description |
|---|---|---|
| active_sessions | number | The count of currently active sessions |
{
"data": {
"active_sessions": 3
},
"sent_at": "2025-09-09T10:19:05Z"
}
Performance Tuning Configuration
The MSR service supports additional performance tuning configuration parameters through the Admin API endpoints (GET/PUT /admin/settings). These parameters allow fine-tuning of database performance and data management operations.
Available Performance Parameters
| Parameter | Type | Description | Use Case |
|---|---|---|---|
| query_work_mem_mb | number | Memory allocation in MB for database query operations | Higher values improve performance for complex queries but consume more memory |
| columnstore_compression_age_days | number | Age threshold in days after which columnstore data undergoes compression | Shorter periods save storage space but may impact write performance |
| cagg_refresh_interval_hours | number | Refresh interval in hours for continuous aggregates | More frequent refreshes provide up-to-date aggregate data but increase system load |
| cagg_refresh_lag_days | number | Lag time in days for continuous aggregate refresh operations | Prevents refreshing recent data that might still be changing |
Configuration Guidelines
Query Work Memory (query_work_mem_mb)
- Default: Varies by deployment
- Range: 64MB - 2048MB recommended
- Impact: Higher values improve sort and hash operations but increase memory usage per connection
- Recommendation: Start with 256MB, increase for heavy analytical workloads
Columnstore Compression (columnstore_compression_age_days)
- Default: 7 days
- Range: 1-30 days recommended
- Impact: Shorter periods reduce storage costs but increase background compression activity
- Recommendation: 7-14 days for most workloads
Continuous Aggregate Refresh (cagg_refresh_interval_hours)
- Default: 1 hour
- Range: 0.25-24 hours
- Impact: More frequent refreshes provide fresher data but consume more CPU/IO
- Recommendation: 1-4 hours depending on data freshness requirements
Refresh Lag (cagg_refresh_lag_days)
- Default: 1 day
- Range: 0-7 days
- Impact: Longer lag prevents refreshing unstable recent data
- Recommendation: 1-2 days for stable performance
Example Configuration
{
"query_work_mem_mb": 512,
"columnstore_compression_age_days": 10,
"cagg_refresh_interval_hours": 2,
"cagg_refresh_lag_days": 1
}
After adjusting these parameters, monitor system metrics:
- Memory usage per connection
- Storage compression ratios
- Aggregate refresh execution times
- Query performance metrics
Health Check API
11. Readiness Check
GET /readiness
Description
Checks if the service is ready to handle requests. This endpoint verifies database connectivity.
Response
- 200 OK: Service is ready
- 503 Service Unavailable: Service is not ready (e.g., database connection failed)
12. Liveness Check
GET /health
Description
Checks if the service is alive and running. This endpoint verifies basic service health including database connectivity.
Response
- 200 OK: Service is alive
- 503 Service Unavailable: Service is not healthy
Frontend API Client
The MSR modlet includes a comprehensive TypeScript API client for interacting with backend services. The client provides:
- Error handling: Automatic error interception with user-friendly toast notifications
- Type safety: Full TypeScript types for requests and responses
- Correlation IDs: Support tracking for debugging and support
- Custom error handlers: Override default behavior with custom logic
Client Functions
Session Management
import {
createSession,
terminateSession,
getPlaybackRange,
getInitialState,
getReplayEvents,
} from "$lib/aoh/msr/api/client";
// Create a new replay session
const { sessionId } = await createSession();
// Terminate a session
await terminateSession(sessionId);
Playback Data
// Get playback range configuration
const range: PlaybackRange = await getPlaybackRange();
// Returns: { maxDays: 7, startDate: "2024-01-01T00:00:00", endDate: "2024-01-08T00:00:00", earliestValidDate?: "2024-01-01T00:00:00" }
// Get initial state at timestamp
const timestamp = new Date("2024-01-05T12:00:00Z");
const events: ReplayEvent[] = await getInitialState(timestamp);
// Get events between timestamps
const startTime = new Date("2024-01-05T12:00:00Z");
const endTime = new Date("2024-01-05T13:00:00Z");
const changeEvents: ReplayEvent[] = await getReplayEvents(startTime, endTime);
Error Handling System
The API client includes a comprehensive error handling system with:
Structured Error Format:
interface MsrError {
code: string; // Error code (e.g., "SESSION_LIMIT_REACHED")
message: string; // User-friendly error message
correlationId: string; // Unique ID for support tracking
isRetryable: boolean; // Whether operation can be retried
action?: string; // Suggested action (e.g., "terminate_existing_session")
details?: Record<string, unknown>; // Additional error context
}
Default Error Handling:
By default, errors are displayed using Sonner toast notifications with correlation IDs:
// Automatic error handling with toast
try {
const { sessionId } = await createSession();
} catch (error) {
// Error is already displayed to user via toast
// Correlation ID included for support
}
Custom Error Handling:
import { configureMsrErrorHandling } from "$lib/aoh/msr/api/client";
// Configure custom error handling
configureMsrErrorHandling({
onError: async (error, context, options) => {
// Send to custom error tracking
await sendToSentry({
errorCode: error.code,
correlationId: error.correlationId,
operation: context.operation,
timestamp: context.timestamp,
});
// Show custom UI
if (error.code === "SESSION_LIMIT_REACHED") {
showCustomSessionLimitModal();
}
},
defaultOptions: {
showToast: true, // Still show default toasts
logError: true, // Log to console
throwError: false, // Don't re-throw
},
});
Error Context:
interface ErrorContext {
operation: string; // Operation that failed (e.g., "create_session")
timestamp: Date; // When error occurred
sessionId?: string; // Session ID if applicable
component: string; // Component that initiated request
}
Type Definitions
Key types for API responses:
// Playback range configuration
interface PlaybackRange {
maxDays: number; // MAX_PLAYBACK_RANGE value
startDate: string; // Earliest selectable date (ISO format)
endDate: string; // Latest selectable date (ISO format)
earliestValidDate?: string; // EARLIEST_VALID_TIMESTAMP if configured
}
// Replay event
interface ReplayEvent {
entityId: string;
entityState: EntityState | null; // Null for delete operations
operation: "create" | "read" | "update" | "delete";
timestamp: Date;
tableName: string;
}
// Entity state (generic)
type EntityState<T = JSONValue> = T;
// Change event (backend format)
interface ChangeEvent {
entity_id: string;
op: "c" | "r" | "u" | "d";
entity_state: EntityState | null;
event_timestamp: string;
table_name: string;
}
Common Error Codes
| Error Code | Description | Retryable | Suggested Action |
|---|---|---|---|
SESSION_LIMIT_REACHED | Maximum concurrent sessions exceeded | No | Terminate existing session |
SESSION_CREATION_FAILED | Failed to create session (backend issue) | Yes | Retry after a moment |
NETWORK_ERROR | Network connectivity problem | Yes | Check connection and retry |
INTERNAL_SERVER_ERROR | Unexpected backend error | No | Contact support with correlation ID |
TIMESTAMP_OUT_OF_RANGE | Requested timestamp outside available data range | No | Select different timestamp |
INVALID_PLAYBACK_WINDOW | Playback window exceeds configured limits | No | Reduce playback window duration |
UNAUTHORIZED | Invalid or expired authentication token | No | Re-authenticate |
Best Practices
1. Let the client handle errors by default:
// ✅ Simple - errors shown automatically
const { sessionId } = await createSession();
// ❌ Unnecessary - already handled
try {
const { sessionId } = await createSession();
} catch (error) {
alert("Error creating session"); // Duplicates toast
}
2. Provide correlation IDs to users when needed:
// Errors already include correlation IDs in toast messages
// Users can reference these when contacting support
3. Use custom handlers for application-specific logic:
// ✅ Add custom behavior while keeping default toasts
configureMsrErrorHandling({
onError: async (error, context) => {
// Track in analytics
trackEvent("msr_error", {
errorCode: error.code,
operation: context.operation,
});
// Redirect on specific errors
if (error.code === "UNAUTHORIZED") {
window.location.href = "/login";
}
},
defaultOptions: {
showToast: true, // Keep showing toasts
logError: true,
},
});
4. Handle expected errors gracefully:
// For operations where errors are expected
try {
const range = await getPlaybackRange();
} catch (error) {
// Error already shown to user, provide fallback
const range = {
maxDays: 30,
startDate: dayjs().subtract(30, "day").format(),
endDate: dayjs().format(),
};
}