Chronoverse provides a real-time notification system that alerts users to important workflow and job state changes. Notifications are delivered instantly through Redis Pub/Sub and stored in PostgreSQL for historical access.
Overview
Notifications keep you informed about:
Workflow Events : Creation, updates, build status changes, terminations
Job Events : Execution starts, completions, failures, cancellations
System Events : Critical errors, threshold breaches, service alerts
All notifications are:
Created automatically by internal services
Delivered in real-time via dashboard subscriptions
Stored persistently in PostgreSQL
Paginated for historical access
Notifications are read-only from the user perspective. They’re created by the system as events occur and can be marked as read but not edited or deleted individually.
Notification Structure
Each notification follows a standardized format:
{
"id" : "550e8400-e29b-41d4-a716-446655440000" ,
"kind" : "WORKFLOW_CREATED" ,
"payload" : {
"workflow_id" : "workflow-uuid" ,
"workflow_name" : "API Health Check" ,
"workflow_kind" : "HEARTBEAT"
},
"read_at" : null ,
"created_at" : "2026-03-03T10:30:45.123Z" ,
"updated_at" : "2026-03-03T10:30:45.123Z"
}
Field Descriptions
Field Type Description idstring Unique notification identifier (UUID) kindstring Notification type (see Notification Kinds below) payloadobject Event-specific data as JSON read_atstring|null ISO timestamp when marked as read, or null if unread created_atstring When the notification was created updated_atstring Last update timestamp
Notification Kinds
Different events generate different notification types:
Workflow Notifications
WORKFLOW_CREATED
WORKFLOW_UPDATED
WORKFLOW_BUILD_COMPLETED
WORKFLOW_BUILD_FAILED
WORKFLOW_TERMINATED
New workflow successfully created {
"workflow_id" : "uuid" ,
"workflow_name" : "Daily ETL" ,
"workflow_kind" : "CONTAINER" ,
"interval" : 1440
}
Workflow configuration changed {
"workflow_id" : "uuid" ,
"workflow_name" : "Daily ETL" ,
"changes" : [ "interval" , "payload" ]
}
Docker image build/pull succeeded {
"workflow_id" : "uuid" ,
"workflow_name" : "Data Processor" ,
"build_status" : "COMPLETED"
}
Docker image build/pull failed {
"workflow_id" : "uuid" ,
"workflow_name" : "Data Processor" ,
"build_status" : "FAILED" ,
"error" : "Image not found: invalid:tag"
}
Workflow terminated (manually or by failure threshold) {
"workflow_id" : "uuid" ,
"workflow_name" : "API Monitor" ,
"reason" : "consecutive_failures" ,
"failure_count" : 5
}
Job Notifications
JOB_STARTED
JOB_COMPLETED
JOB_FAILED
JOB_CANCELED
Job execution began {
"job_id" : "uuid" ,
"workflow_id" : "uuid" ,
"workflow_name" : "Health Check" ,
"trigger" : "AUTOMATIC" ,
"scheduled_at" : "2026-03-03T10:30:00Z"
}
Job finished successfully {
"job_id" : "uuid" ,
"workflow_id" : "uuid" ,
"workflow_name" : "Health Check" ,
"duration_seconds" : 12.5 ,
"status" : "COMPLETED"
}
Job execution failed {
"job_id" : "uuid" ,
"workflow_id" : "uuid" ,
"workflow_name" : "Health Check" ,
"status" : "FAILED" ,
"error" : "Connection timeout" ,
"duration_seconds" : 30.0
}
Job was manually canceled {
"job_id" : "uuid" ,
"workflow_id" : "uuid" ,
"workflow_name" : "Long Process" ,
"canceled_by" : "user-uuid" ,
"status" : "CANCELED"
}
Creating Notifications (Internal)
Notifications are created by internal services via gRPC:
service NotificationsService {
rpc CreateNotification ( CreateNotificationRequest )
returns ( CreateNotificationResponse ) {}
}
message CreateNotificationRequest {
string user_id = 1 ;
string kind = 2 ;
string payload = 3 ; // JSON string
}
Internal Usage Example
// When a job fails
notificationReq := & notificationspb . CreateNotificationRequest {
UserId : job . UserID ,
Kind : "JOB_FAILED" ,
Payload : json . Marshal ( map [ string ] any {
"job_id" : job . ID ,
"workflow_id" : job . WorkflowID ,
"workflow_name" : workflow . Name ,
"status" : "FAILED" ,
"error" : err . Error (),
}),
}
res , err := notificationsClient . CreateNotification ( ctx , notificationReq )
The CreateNotification RPC is an internal API. It’s not exposed in the public REST API and can only be called by authenticated internal services.
Retrieving Notifications
Users can list their notifications via the REST API:
curl "https://api.chronoverse.io/v1/notifications" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"notifications" : [
{
"id" : "notif-1" ,
"kind" : "JOB_FAILED" ,
"payload" : {
"job_id" : "job-uuid" ,
"workflow_name" : "Health Check" ,
"error" : "Timeout"
},
"read_at" : null ,
"created_at" : "2026-03-03T11:00:00Z" ,
"updated_at" : "2026-03-03T11:00:00Z"
},
{
"id" : "notif-2" ,
"kind" : "WORKFLOW_CREATED" ,
"payload" : {
"workflow_id" : "wf-uuid" ,
"workflow_name" : "New ETL"
},
"read_at" : "2026-03-03T10:45:00Z" ,
"created_at" : "2026-03-03T10:30:00Z" ,
"updated_at" : "2026-03-03T10:45:00Z"
}
],
"cursor" : "base64-next-page-token"
}
curl "https://api.chronoverse.io/v1/notifications?cursor=eyJpZCI6Im5vdGlmLTIwIn0=" \
-H "Authorization: Bearer YOUR_TOKEN"
Notifications are ordered by created_at descending (newest first). Each page returns up to 50 notifications.
Marking Notifications as Read
Mark one or more notifications as read:
curl -X PATCH "https://api.chronoverse.io/v1/notifications/read" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"550e8400-e29b-41d4-a716-446655440000",
"660e8400-e29b-41d4-a716-446655440001"
]
}'
Request Validation
message MarkNotificationsReadRequest {
repeated string ids = 1 ; // Required, min 1 ID
string user_id = 2 ; // Required, from auth token
}
Constraints:
Minimum 1 notification ID required
Maximum 100 IDs per request
User can only mark their own notifications
Already-read notifications can be marked again (idempotent)
Marking notifications as read updates the read_at and updated_at timestamps. Use this to track when users acknowledged specific events.
Real-Time Delivery
Notifications are delivered in real-time through the dashboard via Redis Pub/Sub.
Pub/Sub Architecture
Event Occurs
Workflow or job state changes trigger notification creation
Database Insert
Notification stored in PostgreSQL for persistence
Redis Publish
Notification published to user-specific channel: notifications:{user_id}
Dashboard Subscription
Connected dashboard clients receive notification instantly
UI Update
Dashboard displays notification in real-time
Channel Pattern
// User-specific notification channel
notifications:550e8400-e29b-41d4-a716-446655440000
// Published message format
{
"id": "notification-uuid",
"kind": "JOB_FAILED",
"payload": {...},
"created_at": "2026-03-03T10:30:45Z"
}
Dashboard clients subscribe to their user-specific channel on connection. When notifications are published, all connected clients receive them immediately.
Notification Payload Validation
Payloads must be valid JSON objects:
// Valid payloads
{
"workflow_id" : "uuid" ,
"status" : "COMPLETED"
}
// Invalid - not JSON
"just a string"
// Invalid - null
null
// Invalid - array
[ "item1" , "item2" ]
Notification creation fails if the payload is not a valid JSON object. Ensure all event data is properly serialized before creating notifications.
Common Notification Patterns
Workflow Lifecycle
Job Execution Flow
Filtering and Queries
While the current API doesn’t support filtering, you can filter notifications client-side:
// Filter by notification kind
const failedJobs = notifications . filter ( n => n . kind === 'JOB_FAILED' );
// Filter unread notifications
const unread = notifications . filter ( n => n . read_at === null );
// Filter by workflow
const workflowNotifs = notifications . filter ( n =>
n . payload . workflow_id === 'target-workflow-uuid'
);
For high-volume notification scenarios, consider implementing server-side filtering by notification kind or read status in your dashboard.
Best Practices
Subscribe in Dashboard Connect to Redis Pub/Sub in your dashboard for instant notification delivery without polling.
Batch Mark as Read Mark multiple notifications as read in a single API call to reduce requests.
Handle Pagination Always paginate through historical notifications to avoid missing important events.
Parse Payloads Safely Validate notification payload structure before accessing fields to handle schema evolution.
Notification Retention
Retention Policy:
Notifications are stored indefinitely in PostgreSQL
No automatic cleanup or archival
Users can only mark as read, not delete
Consider periodic manual cleanup for very old notifications
Next Steps
Analytics Track workflow and job performance metrics
API Reference Complete API documentation for all endpoints