diff --git a/packages/bbui/src/Modal/Modal.svelte b/packages/bbui/src/Modal/Modal.svelte
index 12c12e3717..3ed9b93fa5 100644
--- a/packages/bbui/src/Modal/Modal.svelte
+++ b/packages/bbui/src/Modal/Modal.svelte
@@ -27,9 +27,17 @@
     visible = false
   }
 
+  export function cancel() {
+    if (!visible) {
+      return
+    }
+    dispatch("cancel")
+    hide()
+  }
+
   function handleKey(e) {
     if (visible && e.key === "Escape") {
-      hide()
+      cancel()
     }
   }
 
@@ -41,7 +49,7 @@
     }
   }
 
-  setContext(Context.Modal, { show, hide })
+  setContext(Context.Modal, { show, hide, cancel })
 </script>
 
 <svelte:window on:keydown={handleKey} />
@@ -56,15 +64,17 @@
   <Portal target=".modal-container">
     <div
       class="spectrum-Underlay is-open"
-      transition:fade|local={{ duration: 200 }}
-      on:mousedown|self={hide}
+      in:fade={{ duration: 200 }}
+      out:fade|local={{ duration: 200 }}
+      on:mousedown|self={cancel}
     >
-      <div class="modal-wrapper" on:mousedown|self={hide}>
-        <div class="modal-inner-wrapper" on:mousedown|self={hide}>
+      <div class="modal-wrapper" on:mousedown|self={cancel}>
+        <div class="modal-inner-wrapper" on:mousedown|self={cancel}>
           <div
             use:focusFirstInput
             class="spectrum-Modal is-open"
-            transition:fly|local={{ y: 30, duration: 200 }}
+            in:fly={{ y: 30, duration: 200 }}
+            out:fly|local={{ y: 30, duration: 200 }}
           >
             <slot />
           </div>
diff --git a/packages/bbui/src/Modal/ModalContent.svelte b/packages/bbui/src/Modal/ModalContent.svelte
index 16338b1ed2..dae9b9e8af 100644
--- a/packages/bbui/src/Modal/ModalContent.svelte
+++ b/packages/bbui/src/Modal/ModalContent.svelte
@@ -16,7 +16,7 @@
   export let onConfirm = undefined
   export let disabled = false
 
-  const { hide } = getContext(Context.Modal)
+  const { hide, cancel } = getContext(Context.Modal)
   let loading = false
   $: confirmDisabled = disabled || loading
 
@@ -56,7 +56,7 @@
       >
         <slot name="footer" />
         {#if showCancelButton}
-          <Button group secondary on:click={hide}>{cancelText}</Button>
+          <Button group secondary on:click={cancel}>{cancelText}</Button>
         {/if}
         {#if showConfirmButton}
           <Button
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
index 7e61d68f59..495389d95f 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
@@ -62,6 +62,7 @@ function generateTitleContainer(table, title, formId, repeaterId) {
             tableId: table._id,
             rowId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_id")} }}`,
             revId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_rev")} }}`,
+            confirm: true,
           },
           "##eventHandlerType": "Delete Row",
         },
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte
index 5092ca0773..aa657beea5 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte
@@ -1,5 +1,5 @@
 <script>
-  import { Select, Label } from "@budibase/bbui"
+  import { Select, Label, Checkbox, Input } from "@budibase/bbui"
   import { store, currentAsset } from "builderStore"
   import { tables } from "stores/backend"
   import { getBindableProperties } from "builderStore/dataBinding"
@@ -35,6 +35,17 @@
     value={parameters.revId}
     on:change={value => (parameters.revId = value.detail)}
   />
+
+  <Label small />
+  <Checkbox text="Require confirmation" bind:value={parameters.confirm} />
+
+  {#if parameters.confirm}
+    <Label small>Confirm text</Label>
+    <Input
+      placeholder="Are you sure you want to delete this row?"
+      bind:value={parameters.confirmText}
+    />
+  {/if}
 </div>
 
 <style>
@@ -42,8 +53,8 @@
     display: grid;
     column-gap: var(--spacing-l);
     row-gap: var(--spacing-s);
-    grid-template-columns: auto 1fr;
-    align-items: baseline;
+    grid-template-columns: 60px 1fr;
+    align-items: center;
     max-width: 800px;
     margin: 0 auto;
   }
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte
index 44f349a324..5d298e4b9f 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte
@@ -1,5 +1,5 @@
 <script>
-  import { Select, Layout } from "@budibase/bbui"
+  import { Select, Layout, Input, Checkbox } from "@budibase/bbui"
   import { store, currentAsset } from "builderStore"
   import { datasources, integrations, queries } from "stores/backend"
   import { getBindableProperties } from "builderStore/dataBinding"
@@ -25,7 +25,7 @@
   }
 </script>
 
-<Layout>
+<Layout gap="XS">
   <Select
     label="Datasource"
     bind:value={parameters.datasourceId}
@@ -44,22 +44,34 @@
       getOptionLabel={query => query.name}
       getOptionValue={query => query._id}
     />
-  {/if}
 
-  {#if query?.parameters?.length > 0}
-    <div>
-      <ParameterBuilder
-        bind:customParams={parameters.queryParams}
-        parameters={query.parameters}
-        bindings={bindableProperties}
-      />
-      <IntegrationQueryEditor
-        height={200}
-        {query}
-        schema={fetchQueryDefinition(query)}
-        editable={false}
-        {datasource}
-      />
-    </div>
+    {#if parameters.queryId}
+      <Checkbox text="Require confirmation" bind:value={parameters.confirm} />
+
+      {#if parameters.confirm}
+        <Input
+          label="Confirm text"
+          placeholder="Are you sure you want to execute this query?"
+          bind:value={parameters.confirmText}
+        />
+      {/if}
+
+      {#if query?.parameters?.length > 0}
+        <div>
+          <ParameterBuilder
+            bind:customParams={parameters.queryParams}
+            parameters={query.parameters}
+            bindings={bindableProperties}
+          />
+          <IntegrationQueryEditor
+            height={200}
+            {query}
+            schema={fetchQueryDefinition(query)}
+            editable={false}
+            {datasource}
+          />
+        </div>
+      {/if}
+    {/if}
   {/if}
 </Layout>
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveFields.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveFields.svelte
index f4a4bea334..bf88ed2caf 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveFields.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveFields.svelte
@@ -79,7 +79,7 @@
       on:click={() => removeField(field[0])}
     />
   {/each}
-  <div>
+  <div style="margin-top: 10px">
     <Button icon="AddCircle" secondary on:click={addField}>
       Add
       {fieldLabel}
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte
index 664129ee02..a2c3b6d49e 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte
@@ -1,5 +1,5 @@
 <script>
-  import { Select, Label, Body } from "@budibase/bbui"
+  import { Select, Label, Body, Checkbox, Input } from "@budibase/bbui"
   import { store, currentAsset } from "builderStore"
   import { tables } from "stores/backend"
   import {
@@ -33,7 +33,8 @@
     optional.<br />
     You can always add or override fields manually.
   </Body>
-  <div class="fields">
+
+  <div class="params">
     <Label small>Data Source</Label>
     <Select
       bind:value={parameters.providerId}
@@ -51,37 +52,58 @@
       getOptionValue={option => option._id}
     />
 
-    {#if parameters.tableId}
+    <Label small />
+    <Checkbox text="Require confirmation" bind:value={parameters.confirm} />
+
+    {#if parameters.confirm}
+      <Label small>Confirm text</Label>
+      <Input
+        placeholder="Are you sure you want to save this row?"
+        bind:value={parameters.confirmText}
+      />
+    {/if}
+  </div>
+
+  {#if parameters.tableId}
+    <div class="fields">
       <SaveFields
         parameterFields={parameters.fields}
         {schemaFields}
         on:change={onFieldsChanged}
       />
-    {/if}
-  </div>
+    </div>
+  {/if}
 </div>
 
 <style>
   .root {
+    width: 100%;
     max-width: 800px;
     margin: 0 auto;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    align-items: stretch;
+    gap: var(--spacing-xl);
   }
 
   .root :global(p) {
     line-height: 1.5;
   }
 
+  .params {
+    display: grid;
+    column-gap: var(--spacing-l);
+    row-gap: var(--spacing-s);
+    grid-template-columns: 60px 1fr;
+    align-items: center;
+  }
+
   .fields {
     display: grid;
     column-gap: var(--spacing-l);
     row-gap: var(--spacing-s);
-    grid-template-columns: auto 1fr auto 1fr auto;
+    grid-template-columns: 60px 1fr auto 1fr auto;
     align-items: center;
   }
-
-  .fields :global(> div:nth-child(2)),
-  .fields :global(> div:nth-child(4)) {
-    grid-column-start: 2;
-    grid-column-end: 6;
-  }
 </style>
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte
index e40c182131..545a805cc5 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte
@@ -1,5 +1,5 @@
 <script>
-  import { Select, Label, Input } from "@budibase/bbui"
+  import { Select, Label, Input, Checkbox } from "@budibase/bbui"
   import { automationStore } from "builderStore"
   import SaveFields from "./SaveFields.svelte"
 
@@ -72,7 +72,7 @@
     </div>
   </div>
 
-  <div class="fields">
+  <div class="params">
     <Label small>Automation</Label>
 
     {#if automationStatus === AUTOMATION_STATUS.EXISTING}
@@ -90,6 +90,19 @@
       />
     {/if}
 
+    <Label small />
+    <Checkbox text="Require confirmation" bind:value={parameters.confirm} />
+
+    {#if parameters.confirm}
+      <Label small>Confirm text</Label>
+      <Input
+        placeholder="Are you sure you want to trigger this automation?"
+        bind:value={parameters.confirmText}
+      />
+    {/if}
+  </div>
+
+  <div class="fields">
     {#key parameters.automationId}
       <SaveFields
         schemaFields={selectedSchema}
@@ -107,16 +120,21 @@
     margin: 0 auto;
   }
 
-  .fields {
+  .params {
     display: grid;
     column-gap: var(--spacing-l);
     row-gap: var(--spacing-s);
-    grid-template-columns: auto 1fr auto 1fr auto;
-    align-items: baseline;
+    grid-template-columns: 60px 1fr;
+    align-items: center;
   }
 
-  .fields :global(> div:nth-child(2)) {
-    grid-column: 2 / span 4;
+  .fields {
+    margin-top: var(--spacing-l);
+    display: grid;
+    column-gap: var(--spacing-l);
+    row-gap: var(--spacing-s);
+    grid-template-columns: 60px 1fr auto 1fr auto;
+    align-items: center;
   }
 
   .radios,
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte
index 7a02a7203f..462597f265 100644
--- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte
+++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte
@@ -24,15 +24,12 @@
 
 <style>
   .root {
-    display: flex;
-    flex-direction: row;
+    display: grid;
+    column-gap: var(--spacing-l);
+    row-gap: var(--spacing-s);
+    grid-template-columns: 60px 1fr;
     align-items: center;
     max-width: 800px;
     margin: 0 auto;
   }
-
-  .root :global(> div) {
-    flex: 1;
-    margin-left: var(--spacing-l);
-  }
 </style>
diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte
index 5e0efe5451..c2bc0caaa3 100644
--- a/packages/client/src/components/ClientApp.svelte
+++ b/packages/client/src/components/ClientApp.svelte
@@ -3,6 +3,7 @@
   import { setContext, onMount } from "svelte"
   import Component from "./Component.svelte"
   import NotificationDisplay from "./NotificationDisplay.svelte"
+  import ConfirmationDisplay from "./ConfirmationDisplay.svelte"
   import Provider from "./Provider.svelte"
   import SDK from "../sdk"
   import {
@@ -70,6 +71,7 @@
         {/key}
       </div>
       <NotificationDisplay />
+      <ConfirmationDisplay />
       <!-- Key block needs to be outside the if statement or it breaks -->
       {#key $builderStore.selectedComponentId}
         {#if $builderStore.inBuilder}
diff --git a/packages/client/src/components/ConfirmationDisplay.svelte b/packages/client/src/components/ConfirmationDisplay.svelte
new file mode 100644
index 0000000000..454cf009a5
--- /dev/null
+++ b/packages/client/src/components/ConfirmationDisplay.svelte
@@ -0,0 +1,15 @@
+<script>
+  import { confirmationStore } from "../store"
+  import { Modal, ModalContent } from "@budibase/bbui"
+</script>
+
+{#if $confirmationStore.showConfirmation}
+  <Modal fixed on:cancel={confirmationStore.actions.cancel}>
+    <ModalContent
+      title={$confirmationStore.title}
+      onConfirm={confirmationStore.actions.confirm}
+    >
+      {$confirmationStore.text}
+    </ModalContent>
+  </Modal>
+{/if}
diff --git a/packages/client/src/store/auth.js b/packages/client/src/store/auth.js
index 9e01a5648f..9829d2e350 100644
--- a/packages/client/src/store/auth.js
+++ b/packages/client/src/store/auth.js
@@ -1,40 +1,11 @@
 import * as API from "../api"
 import { writable, get } from "svelte/store"
-import { initialise } from "./initialise"
-import { routeStore } from "./routes"
 import { builderStore } from "./builder"
 import { TableNames } from "../constants"
 
 const createAuthStore = () => {
   const store = writable(null)
 
-  const goToDefaultRoute = () => {
-    // Setting the active route forces an update of the active screen ID,
-    // even if we're on the same URL
-    routeStore.actions.setActiveRoute("/")
-
-    // Navigating updates the URL to reflect this route
-    routeStore.actions.navigate("/")
-  }
-
-  // Logs a user in
-  const logIn = async ({ email, password }) => {
-    const auth = await API.logIn({ email, password })
-    if (auth.success) {
-      await fetchUser()
-      await initialise()
-      goToDefaultRoute()
-    }
-  }
-
-  // Logs a user out
-  const logOut = async () => {
-    store.set(null)
-    window.document.cookie = `budibase:auth=; budibase:currentapp=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
-    await initialise()
-    goToDefaultRoute()
-  }
-
   // Fetches the user object if someone is logged in and has reloaded the page
   const fetchUser = async () => {
     // Fetch the first user if inside the builder
@@ -54,7 +25,7 @@ const createAuthStore = () => {
 
   return {
     subscribe: store.subscribe,
-    actions: { logIn, logOut, fetchUser },
+    actions: { fetchUser },
   }
 }
 
diff --git a/packages/client/src/store/confirmation.js b/packages/client/src/store/confirmation.js
new file mode 100644
index 0000000000..497b021b04
--- /dev/null
+++ b/packages/client/src/store/confirmation.js
@@ -0,0 +1,39 @@
+import { writable, get } from "svelte/store"
+
+const initialState = {
+  showConfirmation: false,
+  title: null,
+  text: null,
+  callback: null,
+}
+
+const createConfirmationStore = () => {
+  const store = writable(initialState)
+
+  const showConfirmation = (title, text, callback) => {
+    store.set({
+      showConfirmation: true,
+      title,
+      text,
+      callback,
+    })
+  }
+  const confirm = async () => {
+    const state = get(store)
+    if (!state.showConfirmation || !state.callback) {
+      return
+    }
+    store.set(initialState)
+    await state.callback()
+  }
+  const cancel = () => {
+    store.set(initialState)
+  }
+
+  return {
+    subscribe: store.subscribe,
+    actions: { showConfirmation, confirm, cancel },
+  }
+}
+
+export const confirmationStore = createConfirmationStore()
diff --git a/packages/client/src/store/index.js b/packages/client/src/store/index.js
index ebf89e14e3..9f2dbfdcfb 100644
--- a/packages/client/src/store/index.js
+++ b/packages/client/src/store/index.js
@@ -4,6 +4,7 @@ export { routeStore } from "./routes"
 export { screenStore } from "./screens"
 export { builderStore } from "./builder"
 export { dataSourceStore } from "./dataSource"
+export { confirmationStore } from "./confirmation"
 
 // Context stores are layered and duplicated, so it is not a singleton
 export { createContextStore } from "./context"
diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js
index 4d2865d586..6a72c494f0 100644
--- a/packages/client/src/utils/buttonActions.js
+++ b/packages/client/src/utils/buttonActions.js
@@ -1,5 +1,5 @@
 import { get } from "svelte/store"
-import { routeStore, builderStore, authStore } from "../store"
+import { routeStore, builderStore, confirmationStore } from "../store"
 import { saveRow, deleteRow, executeQuery, triggerAutomation } from "../api"
 import { ActionTypes } from "../constants"
 
@@ -68,15 +68,6 @@ const refreshDatasourceHandler = async (action, context) => {
   )
 }
 
-const loginHandler = async action => {
-  const { email, password } = action.parameters
-  await authStore.actions.logIn({ email, password })
-}
-
-const logoutHandler = async () => {
-  await authStore.actions.logOut()
-}
-
 const handlerMap = {
   ["Save Row"]: saveRowHandler,
   ["Delete Row"]: deleteRowHandler,
@@ -85,13 +76,19 @@ const handlerMap = {
   ["Trigger Automation"]: triggerAutomationHandler,
   ["Validate Form"]: validateFormHandler,
   ["Refresh Datasource"]: refreshDatasourceHandler,
-  ["Log In"]: loginHandler,
-  ["Log Out"]: logoutHandler,
+}
+
+const confirmTextMap = {
+  ["Delete Row"]: "Are you sure you want to delete this row?",
+  ["Save Row"]: "Are you sure you want to save this row?",
+  ["Execute Query"]: "Are you sure you want to execute this query?",
+  ["Trigger Automation"]: "Are you sure you want to trigger this automation?",
 }
 
 /**
  * Parses an array of actions and returns a function which will execute the
  * actions in the current context.
+ * A handler returning `false` is a flag to stop execution of handlers
  */
 export const enrichButtonActions = (actions, context) => {
   // Prevent button actions in the builder preview
@@ -102,15 +99,44 @@ export const enrichButtonActions = (actions, context) => {
   return async () => {
     for (let i = 0; i < handlers.length; i++) {
       try {
-        const result = await handlers[i](actions[i], context)
-        // A handler returning `false` is a flag to stop execution of handlers
-        if (result === false) {
+        const action = actions[i]
+        const callback = async () => handlers[i](action, context)
+
+        // If this action is confirmable, show confirmation and await a
+        // callback to execute further actions
+        if (action.parameters?.confirm) {
+          const defaultText = confirmTextMap[action["##eventHandlerType"]]
+          const confirmText = action.parameters?.confirmText || defaultText
+          confirmationStore.actions.showConfirmation(
+            action["##eventHandlerType"],
+            confirmText,
+            async () => {
+              // When confirmed, execute this action immediately,
+              // then execute the rest of the actions in the chain
+              const result = await callback()
+              if (result !== false) {
+                const next = enrichButtonActions(actions.slice(i + 1), context)
+                await next()
+              }
+            }
+          )
+
+          // Stop enriching actions when encountering a confirmable action,
+          // as the callback continues the action chain
           return
         }
+
+        // For non-confirmable actions, execute the handler immediately
+        else {
+          const result = await callback()
+          if (result === false) {
+            return
+          }
+        }
       } catch (error) {
         console.error("Error while executing button handler")
         console.error(error)
-        // Stop executing on an error
+        // Stop executing further actions on error
         return
       }
     }