# smtp_server_split_transaction {{since('2025.12.02-67ee9e96')}} ```lua kumo.on('smtp_server_split_transaction', function(message, conn_meta) end) ``` Called by the ESMTP server to decide whether and how to split the recipient list into groups of recipients at the same destination site. SMTP messages can have an envelope that includes multiple recipients. Each recipient will receive a copy of the message. If multiple recipients share the same mailbox provider then it is advantageous from a bandwidth and efficiency perspective to relay that message to that provider as a single message with a list of multiple recipients, rather than sending one distinct copy per recipient. This event gives you control over how the incoming transaction is split and the recipients are grouped together. You do not need to implement this event handler in the vast majority of cases! Look at [start_esmtp_listener.batch_handling](../kumo/start_esmtp_listener/batch_handling.md) for a much simpler way to express the most common choices. You can use [message:recipient_list](../message/recipient_list.md) to retrieve the recipient list from the message. That might give you something like: ``` { 'fred@gmail.com', 'pete@gmail.com', 'joe@hotmail.com' } ``` (but each element is an [EnvelopeAddress](../address/index.md) rather than a string). The return value must be an array of arrays of addresses. For example, if you wish to recreate the `batch_handling = "BifurcateAlways"` mode of operation, the shape of the result will look like this: ``` { { 'fred@gmail.com' }, { 'pete@gmail.com' }, { 'joe@hotmail.com' } } ``` If you wish to recreate the `batch_handling = "BatchByDomain"` mode of operation, it will instead look like this, with all of the gmail addresses in the same top level array: ``` { { 'fred@gmail.com', 'pete@gmail.com' }, { 'joe@hotmail.com' } } ``` Returning `nil` (or not explicitly returning anything) from `smtp_server_split_transaction` will cause the `batch_handling` option to be consulted to decide how splitting/batching will occur. When a multi-recipient message is routed onwards, the domain of the first recipient is used to resolve the MX and decide which host to connect to. In the example above, `fred@gmail.com` will be used to resolve routing for the first batch, while `joe@hotmail.com` will be used to resolve routing for the second batch. !!! warning While you can create arbitrary batches with this event handler, if you create non-sensical batches you should expect for messages to fail to deliver. For example, if you group `user@gmail.com` together with `other.user@hotmail.com` then when the message is attempted, gmail (from the first recipient) will likely reject the hotmail address because gmail is not responsible for and may choose not to relay the hotmail recipient. !!! note This event is called even if the recipient list has a single entry It is technically possible to synthesize additional recipients by including them in the returned list of batches, but it is recommended that you perform recipient list modification in the [smtp_server_data](smtp_server_data.md) event rather than this event. ## Example: equivalent to batch_handling=BifurcateAlways Every incoming recipient is placed into a separate batch and tracked separately. ```lua kumo.on('smtp_server_split_transaction', function(message, conn_meta) local split = {} for _, recip in ipairs(message:recipient_list()) do table.insert(split, { recip }) end return split end) ``` ## Example: equivalent to batch_handling=BatchByDomain Recipients with exactly the same domain portion are grouped together. ```lua kumo.on('smtp_server_split_transaction', function(message, conn_meta) local by_domain = {} for _, recip in ipairs(message:recipient_list()) do local domain = recip.domain:lower() if not by_domain[domain] then by_domain[domain] = {} end table.insert(by_domain[domain], recip) end return by_domain end) ```