All events conform to the standard envelope (see 04-event-driven-architecture.md §3). What follows is the per-topic payload schema. Format: Protocol Buffers proto3. Registered in Confluent Schema Registry; compatibility = BACKWARD (FULL_TRANSITIVE for financial topics).
syntax = "proto3";
package redbus.common.v1;
import "google/protobuf/timestamp.proto";
message Money {
// Stored as minor units (e.g. paise for INR) for precision
int64 amount_minor = 1;
string currency = 2; // ISO 4217
}
message UserRef { string user_id = 1; }
message OperatorRef{ string operator_id = 1; }
message TripRef { string trip_id = 1; string journey_date = 2; } // YYYY-MM-DD
message SeatRef { string trip_id = 1; string journey_date = 2; string seat_no = 3; }package redbus.user.v1;
message UserSignedUp {
string user_id = 1;
string source = 2; // app_android, app_ios, web, partner
google.protobuf.Timestamp signed_up_at = 3;
string country = 4;
string referrer = 5;
string device_id = 6;
string ip_country = 7;
}message UserLoggedIn {
string user_id = 1;
string method = 2; // otp, password, oauth_google, oauth_apple
string device_id = 3;
google.protobuf.Timestamp at = 4;
string ip_hash = 5;
bool step_up = 6;
}package redbus.booking.v1;
message BookingCreated {
string booking_id = 1;
string pnr = 2;
redbus.common.v1.UserRef user = 3;
redbus.common.v1.TripRef trip = 4;
repeated string seat_nos = 5;
redbus.common.v1.Money total = 6;
google.protobuf.Timestamp created_at = 7;
google.protobuf.Timestamp expires_at = 8;
string quote_id = 9;
string source = 10;
}message BookingSeatsLocked {
string booking_id = 1;
string lock_token = 2;
repeated redbus.common.v1.SeatRef seats = 3;
google.protobuf.Timestamp lock_expires_at = 4;
}message BookingConfirmed {
string booking_id = 1;
string pnr = 2;
redbus.common.v1.UserRef user = 3;
redbus.common.v1.TripRef trip = 4;
repeated string seat_nos = 5;
redbus.common.v1.Money total = 6;
string payment_id = 7;
google.protobuf.Timestamp confirmed_at = 8;
string operator_id = 9;
}message BookingCancelled {
string booking_id = 1;
google.protobuf.Timestamp at = 2;
string reason = 3; // user_initiated, operator_cancelled, ops_override, fraud
string initiated_by = 4; // user_id or operator_id or admin id
redbus.common.v1.Money refund_amount = 5;
redbus.common.v1.Money penalty_amount = 6;
}message BookingExpired {
string booking_id = 1;
google.protobuf.Timestamp at = 2;
string reason = 3; // payment_timeout, lock_expired
}package redbus.payment.v1;
message PaymentInitiated {
string intent_id = 1;
string booking_id = 2;
string user_id = 3;
redbus.common.v1.Money amount = 4;
string method = 5; // upi, card, netbanking, wallet
string psp = 6;
string idempotency_key = 7;
google.protobuf.Timestamp at = 8;
}message PaymentSucceeded {
string intent_id = 1;
string booking_id = 2;
string user_id = 3;
redbus.common.v1.Money amount = 4;
string psp = 5;
string psp_payment_id = 6;
google.protobuf.Timestamp captured_at = 7;
string last4 = 8; // optional, card-only
string bin_country = 9;
}message PaymentFailed {
string intent_id = 1;
string booking_id = 2;
string user_id = 3;
string psp = 4;
string error_code = 5; // standardized internal taxonomy
string psp_error = 6;
google.protobuf.Timestamp at = 7;
bool retryable = 8;
}message RefundInitiated {
string refund_id = 1;
string payment_id = 2;
string booking_id = 3;
redbus.common.v1.Money amount = 4;
string reason = 5; // cancellation, goodwill, dispute
string target = 6; // source, wallet
google.protobuf.Timestamp at = 7;
}message RefundCompleted {
string refund_id = 1;
string payment_id = 2;
string booking_id = 3;
redbus.common.v1.Money amount = 4;
string psp_refund_id = 5;
google.protobuf.Timestamp completed_at = 6;
}package redbus.seat.v1;
message SeatLocked {
redbus.common.v1.SeatRef seat = 1;
string lock_token = 2;
string user_id = 3;
google.protobuf.Timestamp at = 4;
google.protobuf.Timestamp expires_at = 5;
}message SeatReleased {
redbus.common.v1.SeatRef seat = 1;
google.protobuf.Timestamp at = 2;
string reason = 3; // ttl, cancelled, abandoned, ops
}message SeatBooked {
redbus.common.v1.SeatRef seat = 1;
string booking_id = 2;
google.protobuf.Timestamp at = 3;
}message SeatInventorySnapshot {
redbus.common.v1.TripRef trip = 1;
int32 total = 2;
int32 available = 3;
// bitmap of states, 2 bits per seat: 0 avail, 1 lock, 2 booked, 3 blocked
bytes state_bitmap = 4;
int64 version = 5;
google.protobuf.Timestamp at = 6;
}package redbus.notification.v1;
message NotificationSend {
string message_id = 1; // ulid
string channel = 2; // sms, email, push, whatsapp, inapp
string template_id = 3;
map<string,string> variables = 4;
string user_id = 5; // optional
string phone_e164 = 6; // optional (KMS-encrypted)
string email = 7; // optional (KMS-encrypted)
string topic = 8; // booking, marketing, payment...
string priority = 9; // high, normal, low
google.protobuf.Timestamp send_after = 10; // for scheduling
}message NotificationDelivered {
string message_id = 1;
string channel = 2;
string vendor = 3;
google.protobuf.Timestamp delivered_at = 4;
}package redbus.realtime.v1;
message BusLocationUpdated {
redbus.common.v1.TripRef trip = 1;
double lat = 2;
double lon = 3;
float speed_kmph = 4;
float bearing_deg = 5;
google.protobuf.Timestamp at = 6;
string provider = 7; // telematics vendor
}message ETAUpdated {
redbus.common.v1.TripRef trip = 1;
string next_stop_id = 2;
google.protobuf.Timestamp eta = 3;
int32 delay_min = 4;
}package redbus.pricing.v1;
message CouponApplied {
string booking_id = 1;
string coupon_code = 2;
redbus.common.v1.Money discount = 3;
string user_id = 4;
google.protobuf.Timestamp at = 5;
}package redbus.wallet.v1;
message WalletUpdated {
string user_id = 1;
string txn_id = 2;
string direction = 3; // D, C
redbus.common.v1.Money amount = 4;
redbus.common.v1.Money balance_after = 5;
string reference = 6;
google.protobuf.Timestamp at = 7;
string source = 8; // refund, cashback, redemption
}package redbus.review.v1;
message ReviewCreated {
string review_id = 1;
string user_id = 2;
string booking_id = 3;
string operator_id = 4;
string trip_id = 5;
int32 rating = 6; // 1-5
string text = 7;
repeated string tags = 8;
google.protobuf.Timestamp at = 9;
}message PriceQuoteCreated {
string quote_id = 1;
string user_id = 2;
redbus.common.v1.TripRef trip = 3;
repeated string seat_nos = 4;
redbus.common.v1.Money base = 5;
redbus.common.v1.Money surge = 6;
redbus.common.v1.Money discount = 7;
redbus.common.v1.Money total = 8;
google.protobuf.Timestamp expires_at = 9;
string version = 10; // pricing-engine version
}package redbus.audit.v1;
message AuditEvent {
string actor_id = 1;
string actor_role = 2;
string action = 3;
string resource_type = 4;
string resource_id = 5;
string before_hash = 6; // sha256 of before-state json
string after_hash = 7;
string approval_id = 8; // checker if any
string trace_id = 9;
string ip = 10;
string user_agent = 11;
google.protobuf.Timestamp at = 12;
string prev_event_hash = 13; // tamper-evident chain
}- New fields → optional with sensible default (forward compat).
- Never reuse field numbers.
- Never change field types.
- Removed fields → mark
reserved. - For breaking changes: new topic/event type, parallel run, deprecate after one sprint.
For major schema breaks we publish to <topic>.v2. Producers dual-write during migration; consumers migrate first. After migration window, v1 retired.
Consumers MUST:
- Idempotent on
event_id. - Tolerate unknown fields.
- Reject malformed events to DLQ, never silently drop.
- Validate semantic invariants (e.g.
amount > 0).