@Rycaut "cull" is perhaps too overloaded of a term. I just meant there is a column in the user→coupon mapping table that can be (bulk) updated to represent whether a code has been redeemed. Conceptually it can be a boolean, concretely it's a timestamp, in the current implementation it's just a IS NULL clause.
Assigning codes and informing users are definitely intended to be done as close together as possible to avoid missing user notifications, so a single transaction would be best, yes.