--- a/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/vault/VaultBlockEntity.java
@@ -257,7 +_,12 @@
                     if (!list.isEmpty()) {
                         player.awardStat(Stats.ITEM_USED.get(stack.getItem()));
                         stack.consume(config.keyItem().getCount(), player);
-                        unlock(level, state, pos, config, serverData, sharedData, list);
+                        // CraftBukkit start
+                        org.bukkit.event.block.BlockDispenseLootEvent vaultDispenseLootEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockDispenseLootEvent(level, pos, player, list);
+                        if (vaultDispenseLootEvent.isCancelled()) return;
+                        list = vaultDispenseLootEvent.getDispensedLoot().stream().map(org.bukkit.craftbukkit.inventory.CraftItemStack::asNMSCopy).toList();
+                        // CraftBukkit end
+                        unlock(level, state, pos, config, serverData, sharedData, list, player); // Paper - Vault API
                         serverData.addToRewardedPlayers(player);
                         sharedData.updateConnectedPlayersWithinRange(level, pos, serverData, config, config.deactivationRange());
                     }
@@ -266,8 +_,30 @@
         }
 
         static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState, VaultConfig config, VaultSharedData sharedData) {
-            VaultState vaultState = oldState.getValue(VaultBlock.STATE);
-            VaultState vaultState1 = newState.getValue(VaultBlock.STATE);
+        // Paper start - Vault API
+            setVaultState(level, pos, oldState, newState, config,sharedData, null);
+        }
+
+        static void setVaultState(ServerLevel level, BlockPos pos, BlockState oldState, BlockState newState, VaultConfig config, VaultSharedData sharedData, @Nullable Player associatedPlayer) {
+            VaultState vaultState = oldState.getValue(VaultBlock.STATE); final VaultState oldVaultState = vaultState;
+            VaultState vaultState1 = newState.getValue(VaultBlock.STATE); final VaultState newVaultState = vaultState1;
+            org.bukkit.entity.Player apiAssociatedPlayer = null;
+            if (associatedPlayer != null) {
+                apiAssociatedPlayer = (org.bukkit.entity.Player) associatedPlayer.getBukkitEntity();
+            } else if (newVaultState == VaultState.ACTIVE) {
+                final Set<UUID> connectedPlayers = sharedData.getConnectedPlayers();
+                if (!connectedPlayers.isEmpty()) { // Used over sharedData#hasConnectedPlayers to ensure belows iterator#next is always okay.
+                    apiAssociatedPlayer = level.getCraftServer().getPlayer(connectedPlayers.iterator().next());
+                }
+            }
+            final io.papermc.paper.event.block.VaultChangeStateEvent event = new io.papermc.paper.event.block.VaultChangeStateEvent(
+                org.bukkit.craftbukkit.block.CraftBlock.at(level, pos),
+                apiAssociatedPlayer,
+                org.bukkit.craftbukkit.block.data.CraftBlockData.toBukkit(oldVaultState, org.bukkit.block.data.type.Vault.State.class),
+                org.bukkit.craftbukkit.block.data.CraftBlockData.toBukkit(newVaultState, org.bukkit.block.data.type.Vault.State.class)
+            );
+            if (!event.callEvent()) return;
+        // Paper end - Vault API
             level.setBlock(pos, newState, 3);
             vaultState.onTransition(level, pos, vaultState1, config, sharedData, newState.getValue(VaultBlock.OMINOUS));
         }
@@ -279,6 +_,11 @@
                 ItemStack randomDisplayItemFromLootTable = getRandomDisplayItemFromLootTable(
                     level, pos, config.overrideLootTableToDisplay().orElse(config.lootTable())
                 );
+                // CraftBukkit start
+                org.bukkit.event.block.VaultDisplayItemEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callVaultDisplayItemEvent(level, pos, randomDisplayItemFromLootTable);
+                if (event.isCancelled()) return;
+                randomDisplayItemFromLootTable = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getDisplayItem());
+                // CraftBukkit end
                 sharedData.setDisplayItem(randomDisplayItemFromLootTable);
             }
         }
@@ -301,10 +_,24 @@
             VaultSharedData sharedData,
             List<ItemStack> itemsToEject
         ) {
+        // Paper start - Vault API
+            unlock(level, state, pos, config, serverData, sharedData, itemsToEject, null);
+        }
+        private static void unlock(
+            ServerLevel level,
+            BlockState state,
+            BlockPos pos,
+            VaultConfig config,
+            VaultServerData serverData,
+            VaultSharedData sharedData,
+            List<ItemStack> itemsToEject,
+            final @Nullable Player associatedPlayer
+        ) {
+        // Paper end - Vault API
             serverData.setItemsToEject(itemsToEject);
             sharedData.setDisplayItem(serverData.getNextItemToEject());
             serverData.pauseStateUpdatingUntil(level.getGameTime() + 14L);
-            setVaultState(level, pos, state, state.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData);
+            setVaultState(level, pos, state, state.setValue(VaultBlock.STATE, VaultState.UNLOCKING), config, sharedData, associatedPlayer); // Paper - Vault API
         }
 
         private static List<ItemStack> resolveItemsToEject(ServerLevel level, VaultConfig config, BlockPos pos, Player player, ItemStack key) {
