Correlation
Correlation links incoming messages to the correct saga instance.
Basic Correlation
Define how each message type correlates to a saga:
const saga = createSagaMachine<OrderState, OrderMessages>()
.name('OrderSaga')
.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })
.correlate('PaymentCaptured', msg => msg.orderId)
.correlate('InventoryReserved', msg => msg.orderId)
// ...
The canStart Option
Messages with canStart: true can create new saga instances:
// This message can start a new saga
.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })
// This message cannot start a saga (must find existing)
.correlate('PaymentCaptured', msg => msg.orderId)
If a message without canStart arrives and no saga exists, it's ignored.
Wildcard Correlation
Use '*' to define a default correlation for all message types:
.correlate('OrderSubmitted', msg => msg.orderId, { canStart: true })
.correlate('*', msg => msg.orderId) // All other messages use orderId
This is useful when all messages share the same correlation field.
Multiple Correlation IDs
A single message type can correlate by different fields:
// In a payment saga, correlate by different IDs
.correlate('PaymentRequested', msg => msg.paymentId, { canStart: true })
.correlate('PaymentConfirmed', msg => msg.paymentId)
.correlate('RefundRequested', msg => msg.originalPaymentId) // Different field!
Correlation Flow
Best Practices
Use Business IDs
Correlate by meaningful business identifiers:
// ✅ Good - uses business ID
.correlate('OrderSubmitted', msg => msg.orderId)
// ❌ Avoid - uses technical ID
.correlate('OrderSubmitted', msg => msg.messageId)
Consistent Correlation
Ensure all related messages include the correlation field:
// All order-related messages should have orderId
interface PaymentCaptured {
type: 'PaymentCaptured';
orderId: string; // ✅ Correlation field
transactionId: string;
}