From ed9a0bb795cec714bae9ab54414c2e6c996f8368 Mon Sep 17 00:00:00 2001
From: Joshua Hawxwell <m@hawx.me>
Date: Wed, 24 Jul 2024 08:31:14 +0100
Subject: [PATCH] MLPAB-2198 Allow attorney to opt-out (#1368)

---
 cypress/e2e/attorney/opt-out.cy.js            |  21 +
 cypress/e2e/attorney/sign.cy.js               |  22 +
 cypress/e2e/attorney/trust-corporation.cy.js  | 258 ++++-----
 internal/app/app.go                           |   2 +
 internal/app/attorney_store.go                |  18 +
 internal/app/attorney_store_test.go           |  61 ++
 internal/notify/email.go                      |  14 +
 .../confirm_dont_want_to_be_attorney.go       |  63 +++
 ...irm_dont_want_to_be_attorney_logged_out.go |  94 +++
 ...ont_want_to_be_attorney_logged_out_test.go | 398 +++++++++++++
 .../confirm_dont_want_to_be_attorney_test.go  | 222 ++++++++
 .../enter_reference_number_opt_out.go         |  50 ++
 .../enter_reference_number_opt_out_test.go    | 179 ++++++
 .../page/attorney/mock_AttorneyStore_test.go  |  46 ++
 internal/page/attorney/mock_Localizer_test.go | 534 ++++++++++++++++++
 .../page/attorney/mock_LpaStoreClient_test.go |  53 +-
 .../page/attorney/mock_NotifyClient_test.go   |  86 +++
 .../page/attorney/mock_SessionStore_test.go   | 106 ++++
 internal/page/attorney/mock_test.go           |   9 +
 internal/page/attorney/register.go            |  23 +
 internal/page/attorney/register_test.go       |   2 +-
 ...rm_dont_want_to_be_certificate_provider.go |   2 +-
 ...t_to_be_certificate_provider_logged_out.go |  14 +-
 ...be_certificate_provider_logged_out_test.go |  60 +-
 ...nt_want_to_be_certificate_provider_test.go |   2 +-
 .../enter_reference_number_opt_out.go         |   2 +-
 internal/page/certificateprovider/register.go |   4 +-
 ...ecided_not_to_be_a_certificate_provider.go |  24 -
 ...d_not_to_be_a_certificate_provider_test.go |  47 --
 internal/page/paths.go                        |  78 +--
 internal/page/share_code.go                   |   8 +-
 internal/page/share_code_test.go              |   7 +
 lang/cy.json                                  |  12 +-
 lang/en.json                                  |  12 +-
 .../confirm_dont_want_to_be_attorney.gohtml   |  34 ++
 .../attorney/enter_reference_number.gohtml    |   4 +-
 .../enter_reference_number_opt_out.gohtml     |  22 +
 web/template/attorney/sign.gohtml             |   6 +-
 ...you_have_decided_not_to_be_attorney.gohtml |  15 +
 .../enter_reference_number.gohtml             |  11 +-
 ...ed_not_to_be_a_certificate_provider.gohtml |   2 +-
 41 files changed, 2340 insertions(+), 287 deletions(-)
 create mode 100644 cypress/e2e/attorney/opt-out.cy.js
 create mode 100644 internal/page/attorney/confirm_dont_want_to_be_attorney.go
 create mode 100644 internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out.go
 create mode 100644 internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out_test.go
 create mode 100644 internal/page/attorney/confirm_dont_want_to_be_attorney_test.go
 create mode 100644 internal/page/attorney/enter_reference_number_opt_out.go
 create mode 100644 internal/page/attorney/enter_reference_number_opt_out_test.go
 create mode 100644 internal/page/attorney/mock_Localizer_test.go
 create mode 100644 internal/page/attorney/mock_NotifyClient_test.go
 delete mode 100644 internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.go
 delete mode 100644 internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider_test.go
 create mode 100644 web/template/attorney/confirm_dont_want_to_be_attorney.gohtml
 create mode 100644 web/template/attorney/enter_reference_number_opt_out.gohtml
 create mode 100644 web/template/attorney/you_have_decided_not_to_be_attorney.gohtml

diff --git a/cypress/e2e/attorney/opt-out.cy.js b/cypress/e2e/attorney/opt-out.cy.js
new file mode 100644
index 0000000000..37709da717
--- /dev/null
+++ b/cypress/e2e/attorney/opt-out.cy.js
@@ -0,0 +1,21 @@
+const { TestEmail, randomShareCode } = require("../../support/e2e");
+
+describe('Opting out', () => {
+    it('stops me being attorney', () => {
+        const shareCode = randomShareCode();
+        cy.visit(`/fixtures/attorney?redirect=&withShareCode=${shareCode}&email=${TestEmail}`);
+
+        cy.visit('/attorney-enter-reference-number-opt-out');
+        cy.checkA11yApp();
+        cy.get('#f-reference-number').type(shareCode);
+        cy.contains('button', 'Continue').click();
+
+        cy.url().should('contain', '/confirm-you-do-not-want-to-be-an-attorney');
+        cy.checkA11yApp();
+        cy.contains('M-FAKE-');
+
+        cy.contains('button', 'Confirm').click();
+
+        cy.url().should('contain', '/you-have-decided-not-to-be-an-attorney');
+    });
+});
diff --git a/cypress/e2e/attorney/sign.cy.js b/cypress/e2e/attorney/sign.cy.js
index 48f7d17e72..56f2fdea1a 100644
--- a/cypress/e2e/attorney/sign.cy.js
+++ b/cypress/e2e/attorney/sign.cy.js
@@ -18,6 +18,17 @@ describe('Sign', () => {
             cy.contains('h1', 'You’ve formally agreed to be an attorney');
         });
 
+        it('can be opted out of', () => {
+            cy.contains('a', 'I do not want to be an attorney').click();
+
+            cy.url().should('contain', '/confirm-you-do-not-want-to-be-an-attorney');
+            cy.checkA11yApp();
+            cy.contains('button', 'Confirm').click();
+
+            cy.url().should('contain', '/you-have-decided-not-to-be-an-attorney');
+            cy.checkA11yApp();
+        });
+
         it('shows an error when not selected', () => {
             cy.contains('button', 'Submit signature').click();
 
@@ -48,6 +59,17 @@ describe('Sign', () => {
             cy.contains('h1', 'You’ve formally agreed to be a replacement attorney');
         });
 
+        it('can be opted out of', () => {
+            cy.contains('a', 'I do not want to be an attorney').click();
+
+            cy.url().should('contain', '/confirm-you-do-not-want-to-be-an-attorney');
+            cy.checkA11yApp();
+            cy.contains('button', 'Confirm').click();
+
+            cy.url().should('contain', '/you-have-decided-not-to-be-an-attorney');
+            cy.checkA11yApp();
+        });
+
         it('shows an error when not selected', () => {
             cy.contains('button', 'Submit signature').click();
 
diff --git a/cypress/e2e/attorney/trust-corporation.cy.js b/cypress/e2e/attorney/trust-corporation.cy.js
index 385ba2e06e..fd4a9c9c83 100644
--- a/cypress/e2e/attorney/trust-corporation.cy.js
+++ b/cypress/e2e/attorney/trust-corporation.cy.js
@@ -1,133 +1,133 @@
 const { TestMobile, TestEmail, randomShareCode } = require("../../support/e2e");
 
 describe('As a trust corporation', () => {
-  beforeEach(() => {
-    const shareCode = randomShareCode()
-    cy.visit(`/fixtures/attorney?redirect=/attorney-start&is-trust-corporation=1&progress=readTheLPA&&withShareCode=${shareCode}&email=${TestEmail}`);
-
-    // start
-    cy.contains('a', 'Start').click();
-    cy.get('form').submit();
-
-    // enter reference number
-    cy.get('#f-reference-number').type(shareCode);
-    cy.contains('button', 'Save and continue').click();
-
-    // acting as an attorney
-    cy.contains('We have identified the trust corporation’s attorney reference number');
-    cy.contains('a', 'Continue').click();
-
-    // task list
-    cy.contains('a', 'Confirm your details').click();
-
-    // mobile number
-    cy.get('#f-mobile').type(TestMobile);
-    cy.contains('button', 'Save and continue').click();
-
-    // language preferences
-    cy.get('[name="language-preference"]').check('cy', { force: true })
-    cy.contains('button', 'Save and continue').click()
-
-    // confirm your company details
-    cy.contains('07700 900 000');
-    cy.contains('Welsh');
-    cy.contains('Confirm your company details');
-    cy.contains('First Choice Trust Corporation Ltd.');
-    cy.contains('555555555');
-    cy.contains('simulate-delivered@notifications.service.gov.uk');
-    cy.contains('2 RICHMOND PLACE');
-    cy.contains('B14 7ED');
-    cy.contains('button', 'Continue').click();
-
-    // task list
-    cy.contains('Read the LPA').click();
-    cy.contains('button', 'Continue').click();
-
-    // legal rights and responsibilities
-    cy.contains('Sign the LPA').click();
-    cy.contains('Before signing, you must read the trust corporation’s legal rights and responsibilities as an attorney.');
-    cy.contains('a', 'Continue').click();
-
-    // what happens when you sign the lpa
-    cy.contains('What happens when you sign the LPA');
-    cy.contains('a', 'Continue to signing page').click();
-  });
-
-  it('allows a single signatory', () => {
-    // sign
-    cy.contains('Sign the LPA on behalf of the trust corporation');
-    cy.get('#f-first-names').type('Sign');
-    cy.get('#f-last-name').type('Signson');
-    cy.get('#f-professional-title').type('Pro signer');
-    cy.get('#f-confirm').check({ force: true });
-    cy.contains('button', 'Submit signature').click();
-
-    // would like a 2nd signatory
-    cy.contains('label', 'No').click();
-    cy.contains('button', 'Continue').click();
-
-    // what happens next
-    cy.contains('First Choice Trust Corporation Ltd. has formally agreed to be an attorney');
-    cy.contains('a', 'Go to your dashboard');
-  });
-
-  it('allows a second signatory', () => {
-    // sign
-    cy.contains('Sign the LPA on behalf of the trust corporation');
-    cy.get('#f-first-names').type('Sign');
-    cy.get('#f-last-name').type('Signson');
-    cy.get('#f-professional-title').type('Pro signer');
-    cy.get('#f-confirm').check({ force: true });
-    cy.contains('button', 'Submit signature').click();
-
-    // would like a 2nd signatory
-    cy.contains('label', 'Yes').click();
-    cy.contains('button', 'Continue').click();
-
-    // task list
-    cy.contains('a', 'Return to task list').click();
-    cy.contains('Sign the LPA (signatory 1)');
-    cy.contains('Sign the LPA (signatory 2)').click();
-
-    // sign
-    cy.get('#f-first-names').type('Sign2');
-    cy.get('#f-last-name').type('Signson2');
-    cy.get('#f-professional-title').type('Pro signer2');
-    cy.get('#f-confirm').check({ force: true });
-    cy.contains('button', 'Submit signature').click();
-
-    // what happens next
-    cy.contains('First Choice Trust Corporation Ltd. has formally agreed to be an attorney');
-    cy.contains('a', 'Go to your dashboard');
-  });
-
-  it('can remove second signatory', () => {
-    // sign
-    cy.contains('Sign the LPA on behalf of the trust corporation');
-    cy.get('#f-first-names').type('Sign');
-    cy.get('#f-last-name').type('Signson');
-    cy.get('#f-professional-title').type('Pro signer');
-    cy.get('#f-confirm').check({ force: true });
-    cy.contains('button', 'Submit signature').click();
-
-    // would like a 2nd signatory
-    cy.contains('label', 'Yes').click();
-    cy.contains('button', 'Continue').click();
-
-    // task list
-    cy.contains('a', 'Return to task list').click();
-    cy.contains('Sign the LPA (signatory 1)');
-    cy.contains('Sign the LPA (signatory 2)').click();
-
-    // sign
-    cy.contains('a', 'The trust corporation no longer requires a second signatory').click();
-
-    // would like a 2nd signatory
-    cy.contains('label', 'No').click();
-    cy.contains('button', 'Continue').click();
-
-    // what happens next
-    cy.contains('First Choice Trust Corporation Ltd. has formally agreed to be an attorney');
-    cy.contains('a', 'Go to your dashboard');
-  });
+    beforeEach(() => {
+        const shareCode = randomShareCode()
+        cy.visit(`/fixtures/attorney?redirect=/attorney-start&is-trust-corporation=1&progress=readTheLPA&&withShareCode=${shareCode}&email=${TestEmail}`);
+
+        // start
+        cy.contains('a', 'Start').click();
+        cy.get('form').submit();
+
+        // enter reference number
+        cy.get('#f-reference-number').type(shareCode);
+        cy.contains('button', 'Save and continue').click();
+
+        // acting as an attorney
+        cy.contains('We have identified the trust corporation’s attorney reference number');
+        cy.contains('a', 'Continue').click();
+
+        // task list
+        cy.contains('a', 'Confirm your details').click();
+
+        // mobile number
+        cy.get('#f-mobile').type(TestMobile);
+        cy.contains('button', 'Save and continue').click();
+
+        // language preferences
+        cy.get('[name="language-preference"]').check('cy', { force: true })
+        cy.contains('button', 'Save and continue').click()
+
+        // confirm your company details
+        cy.contains('07700 900 000');
+        cy.contains('Welsh');
+        cy.contains('Confirm your company details');
+        cy.contains('First Choice Trust Corporation Ltd.');
+        cy.contains('555555555');
+        cy.contains('simulate-delivered@notifications.service.gov.uk');
+        cy.contains('2 RICHMOND PLACE');
+        cy.contains('B14 7ED');
+        cy.contains('button', 'Continue').click();
+
+        // task list
+        cy.contains('Read the LPA').click();
+        cy.contains('button', 'Continue').click();
+
+        // legal rights and responsibilities
+        cy.contains('Sign the LPA').click();
+        cy.contains('Before signing, you must read the trust corporation’s legal rights and responsibilities as an attorney.');
+        cy.contains('a', 'Continue').click();
+
+        // what happens when you sign the lpa
+        cy.contains('What happens when you sign the LPA');
+        cy.contains('a', 'Continue to signing page').click();
+    });
+
+    it('allows a single signatory', () => {
+        // sign
+        cy.contains('Sign the LPA on behalf of the trust corporation');
+        cy.get('#f-first-names').type('Sign');
+        cy.get('#f-last-name').type('Signson');
+        cy.get('#f-professional-title').type('Pro signer');
+        cy.get('#f-confirm').check({ force: true });
+        cy.contains('button', 'Submit signature').click();
+
+        // would like a 2nd signatory
+        cy.contains('label', 'No').click();
+        cy.contains('button', 'Continue').click();
+
+        // what happens next
+        cy.contains('First Choice Trust Corporation Ltd. has formally agreed to be an attorney');
+        cy.contains('a', 'Go to your dashboard');
+    });
+
+    it('allows a second signatory', () => {
+        // sign
+        cy.contains('Sign the LPA on behalf of the trust corporation');
+        cy.get('#f-first-names').type('Sign');
+        cy.get('#f-last-name').type('Signson');
+        cy.get('#f-professional-title').type('Pro signer');
+        cy.get('#f-confirm').check({ force: true });
+        cy.contains('button', 'Submit signature').click();
+
+        // would like a 2nd signatory
+        cy.contains('label', 'Yes').click();
+        cy.contains('button', 'Continue').click();
+
+        // task list
+        cy.visitLpa('/task-list');
+        cy.contains('Sign the LPA (signatory 1)');
+        cy.contains('Sign the LPA (signatory 2)').click();
+
+        // sign
+        cy.get('#f-first-names').type('Sign2');
+        cy.get('#f-last-name').type('Signson2');
+        cy.get('#f-professional-title').type('Pro signer2');
+        cy.get('#f-confirm').check({ force: true });
+        cy.contains('button', 'Submit signature').click();
+
+        // what happens next
+        cy.contains('First Choice Trust Corporation Ltd. has formally agreed to be an attorney');
+        cy.contains('a', 'Go to your dashboard');
+    });
+
+    it('can remove second signatory', () => {
+        // sign
+        cy.contains('Sign the LPA on behalf of the trust corporation');
+        cy.get('#f-first-names').type('Sign');
+        cy.get('#f-last-name').type('Signson');
+        cy.get('#f-professional-title').type('Pro signer');
+        cy.get('#f-confirm').check({ force: true });
+        cy.contains('button', 'Submit signature').click();
+
+        // would like a 2nd signatory
+        cy.contains('label', 'Yes').click();
+        cy.contains('button', 'Continue').click();
+
+        // task list
+        cy.visitLpa('/task-list');
+        cy.contains('Sign the LPA (signatory 1)');
+        cy.contains('Sign the LPA (signatory 2)').click();
+
+        // sign
+        cy.contains('a', 'The trust corporation no longer requires a second signatory').click();
+
+        // would like a 2nd signatory
+        cy.contains('label', 'No').click();
+        cy.contains('button', 'Continue').click();
+
+        // what happens next
+        cy.contains('First Choice Trust Corporation Ltd. has formally agreed to be an attorney');
+        cy.contains('a', 'Go to your dashboard');
+    });
 });
diff --git a/internal/app/app.go b/internal/app/app.go
index 2a406d1f34..53987bf905 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -199,6 +199,8 @@ func App(
 		dashboardStore,
 		lpaStoreClient,
 		lpaStoreResolvingService,
+		notifyClient,
+		appPublicURL,
 	)
 
 	donor.Register(
diff --git a/internal/app/attorney_store.go b/internal/app/attorney_store.go
index 09950d1657..9708d8c0c4 100644
--- a/internal/app/attorney_store.go
+++ b/internal/app/attorney_store.go
@@ -3,6 +3,7 @@ package app
 import (
 	"context"
 	"errors"
+	"fmt"
 	"time"
 
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
@@ -74,3 +75,20 @@ func (s *attorneyStore) Put(ctx context.Context, attorney *actor.AttorneyProvide
 	attorney.UpdatedAt = s.now()
 	return s.dynamoClient.Put(ctx, attorney)
 }
+
+func (s *attorneyStore) Delete(ctx context.Context) error {
+	data, err := page.SessionDataFromContext(ctx)
+	if err != nil {
+		return err
+	}
+
+	if data.LpaID == "" || data.SessionID == "" {
+		return errors.New("attorneyStore.Delete requires LpaID and SessionID")
+	}
+
+	if err := s.dynamoClient.DeleteOne(ctx, dynamo.LpaKey(data.LpaID), dynamo.AttorneyKey(data.SessionID)); err != nil {
+		return fmt.Errorf("error deleting attorney: %w", err)
+	}
+
+	return nil
+}
diff --git a/internal/app/attorney_store_test.go b/internal/app/attorney_store_test.go
index b66220b578..b0cb4e1151 100644
--- a/internal/app/attorney_store_test.go
+++ b/internal/app/attorney_store_test.go
@@ -3,6 +3,7 @@ package app
 import (
 	"context"
 	"errors"
+	"fmt"
 	"testing"
 	"time"
 
@@ -221,3 +222,63 @@ func TestAttorneyStorePutOnError(t *testing.T) {
 	err := attorneyStore.Put(ctx, &actor.AttorneyProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.AttorneyKey("456"), LpaID: "123"})
 	assert.Equal(t, expectedError, err)
 }
+
+func TestAttorneyStoreDelete(t *testing.T) {
+	ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "123", SessionID: "456"})
+
+	dynamoClient := newMockDynamoClient(t)
+	dynamoClient.EXPECT().
+		DeleteOne(ctx, dynamo.LpaKey("123"), dynamo.AttorneyKey("456")).
+		Return(nil)
+
+	attorneyStore := &attorneyStore{dynamoClient: dynamoClient}
+
+	err := attorneyStore.Delete(ctx)
+	assert.Nil(t, err)
+}
+
+func TestAttorneyStoreDeleteWhenSessionDataErrors(t *testing.T) {
+	attorneyStore := &attorneyStore{}
+
+	err := attorneyStore.Delete(ctx)
+	assert.Error(t, err)
+}
+
+func TestAttorneyStoreDeleteWhenMissingSessionValues(t *testing.T) {
+	testcases := map[string]struct {
+		lpaID     string
+		sessionID string
+	}{
+		"missing LpaID": {
+			sessionID: "456",
+		},
+		"missing SessionID": {
+			lpaID: "123",
+		},
+	}
+
+	for name, tc := range testcases {
+		t.Run(name, func(t *testing.T) {
+			ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: tc.lpaID, SessionID: tc.sessionID})
+
+			attorneyStore := &attorneyStore{}
+
+			err := attorneyStore.Delete(ctx)
+			assert.Error(t, err)
+		})
+	}
+}
+
+func TestAttorneyStoreDeleteWhenDynamoClientError(t *testing.T) {
+	ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "123", SessionID: "456"})
+
+	dynamoClient := newMockDynamoClient(t)
+	dynamoClient.EXPECT().
+		DeleteOne(mock.Anything, mock.Anything, mock.Anything).
+		Return(expectedError)
+
+	attorneyStore := &attorneyStore{dynamoClient: dynamoClient}
+
+	err := attorneyStore.Delete(ctx)
+	assert.Equal(t, fmt.Errorf("error deleting attorney: %w", expectedError), err)
+}
diff --git a/internal/notify/email.go b/internal/notify/email.go
index 2d1886c13c..13adbe9d1f 100644
--- a/internal/notify/email.go
+++ b/internal/notify/email.go
@@ -12,6 +12,7 @@ type InitialOriginalAttorneyEmail struct {
 	AttorneyStartPageURL      string
 	ShareCode                 string
 	DonorFirstNamesPossessive string
+	AttorneyOptOutURL         string
 }
 
 func (e InitialOriginalAttorneyEmail) emailID(isProduction bool) string {
@@ -30,6 +31,7 @@ type InitialReplacementAttorneyEmail struct {
 	AttorneyStartPageURL      string
 	ShareCode                 string
 	DonorFirstNamesPossessive string
+	AttorneyOptOutURL         string
 }
 
 func (e InitialReplacementAttorneyEmail) emailID(isProduction bool) string {
@@ -191,3 +193,15 @@ func (e PaymentConfirmationEmail) emailID(isProduction bool) string {
 
 	return "ff757818-f066-4605-8751-af481afe8a2b"
 }
+
+type AttorneyOptedOutEmail struct {
+	AttorneyFullName  string
+	DonorFullName     string
+	LpaType           string
+	LpaUID            string
+	DonorStartPageURL string
+}
+
+func (e AttorneyOptedOutEmail) emailID(isProduction bool) string {
+	return "TODO"
+}
diff --git a/internal/page/attorney/confirm_dont_want_to_be_attorney.go b/internal/page/attorney/confirm_dont_want_to_be_attorney.go
new file mode 100644
index 0000000000..3a647bc196
--- /dev/null
+++ b/internal/page/attorney/confirm_dont_want_to_be_attorney.go
@@ -0,0 +1,63 @@
+package attorney
+
+import (
+	"net/http"
+	"net/url"
+
+	"github.com/ministryofjustice/opg-go-common/template"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
+)
+
+type confirmDontWantToBeAttorneyData struct {
+	App    page.AppData
+	Errors validation.List
+	Lpa    *lpastore.Lpa
+}
+
+func ConfirmDontWantToBeAttorney(tmpl template.Template, lpaStoreResolvingService LpaStoreResolvingService, attorneyStore AttorneyStore, notifyClient NotifyClient, appPublicURL string) Handler {
+	return func(appData page.AppData, w http.ResponseWriter, r *http.Request, attorneyProvidedDetails *actor.AttorneyProvidedDetails) error {
+		lpa, err := lpaStoreResolvingService.Get(r.Context())
+		if err != nil {
+			return err
+		}
+
+		data := &confirmDontWantToBeAttorneyData{
+			App: appData,
+			Lpa: lpa,
+		}
+
+		if r.Method == http.MethodPost {
+			attorneyFullName, err := findAttorneyFullName(lpa, attorneyProvidedDetails.UID, attorneyProvidedDetails.IsTrustCorporation, attorneyProvidedDetails.IsReplacement)
+			if err != nil {
+				return err
+			}
+
+			email := notify.AttorneyOptedOutEmail{
+				AttorneyFullName:  attorneyFullName,
+				DonorFullName:     lpa.Donor.FullName(),
+				LpaType:           appData.Localizer.T(lpa.Type.String()),
+				LpaUID:            lpa.LpaUID,
+				DonorStartPageURL: appPublicURL + page.Paths.Start.Format(),
+			}
+
+			if err := attorneyStore.Delete(r.Context()); err != nil {
+				return err
+			}
+
+			if err := notifyClient.SendActorEmail(r.Context(), lpa.Donor.Email, lpa.LpaUID, email); err != nil {
+				return err
+			}
+
+			return page.Paths.Attorney.YouHaveDecidedNotToBeAttorney.RedirectQuery(w, r, appData, url.Values{
+				"donorFullName":   {lpa.Donor.FullName()},
+				"donorFirstNames": {lpa.Donor.FirstNames},
+			})
+		}
+
+		return tmpl(w, data)
+	}
+}
diff --git a/internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out.go b/internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out.go
new file mode 100644
index 0000000000..08306d70e5
--- /dev/null
+++ b/internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out.go
@@ -0,0 +1,94 @@
+package attorney
+
+import (
+	"errors"
+	"net/http"
+	"net/url"
+
+	"github.com/ministryofjustice/opg-go-common/template"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
+)
+
+type confirmDontWantToBeAttorneyDataLoggedOut struct {
+	App    page.AppData
+	Errors validation.List
+	Lpa    *lpastore.Lpa
+}
+
+func ConfirmDontWantToBeAttorneyLoggedOut(tmpl template.Template, shareCodeStore ShareCodeStore, lpaStoreResolvingService LpaStoreResolvingService, sessionStore SessionStore, notifyClient NotifyClient, appPublicURL string) page.Handler {
+	return func(appData page.AppData, w http.ResponseWriter, r *http.Request) error {
+		session, err := sessionStore.LpaData(r)
+		if err != nil {
+			return err
+		}
+
+		ctx := page.ContextWithSessionData(r.Context(), &page.SessionData{LpaID: session.LpaID})
+
+		lpa, err := lpaStoreResolvingService.Get(ctx)
+		if err != nil {
+			return err
+		}
+
+		data := &confirmDontWantToBeAttorneyDataLoggedOut{
+			App: appData,
+			Lpa: lpa,
+		}
+
+		if r.Method == http.MethodPost {
+			shareCode, err := shareCodeStore.Get(r.Context(), actor.TypeAttorney, r.URL.Query().Get("referenceNumber"))
+			if err != nil {
+				return err
+			}
+
+			attorneyFullName, err := findAttorneyFullName(lpa, shareCode.ActorUID, shareCode.IsTrustCorporation, shareCode.IsReplacementAttorney)
+			if err != nil {
+				return err
+			}
+
+			email := notify.AttorneyOptedOutEmail{
+				AttorneyFullName:  attorneyFullName,
+				DonorFullName:     lpa.Donor.FullName(),
+				LpaType:           appData.Localizer.T(lpa.Type.String()),
+				LpaUID:            lpa.LpaUID,
+				DonorStartPageURL: appPublicURL + page.Paths.Start.Format(),
+			}
+
+			if err := notifyClient.SendActorEmail(ctx, lpa.Donor.Email, lpa.LpaUID, email); err != nil {
+				return err
+			}
+
+			if err := shareCodeStore.Delete(r.Context(), shareCode); err != nil {
+				return err
+			}
+
+			return page.Paths.Attorney.YouHaveDecidedNotToBeAttorney.RedirectQuery(w, r, appData, url.Values{"donorFullName": {lpa.Donor.FullName()}})
+		}
+
+		return tmpl(w, data)
+	}
+}
+
+func findAttorneyFullName(lpa *lpastore.Lpa, uid actoruid.UID, isTrustCorporation, isReplacement bool) (string, error) {
+	if t := lpa.ReplacementAttorneys.TrustCorporation; t.UID == uid {
+		return t.Name, nil
+	}
+
+	if t := lpa.Attorneys.TrustCorporation; t.UID == uid {
+		return t.Name, nil
+	}
+
+	if a, ok := lpa.ReplacementAttorneys.Get(uid); ok {
+		return a.FullName(), nil
+	}
+
+	if a, ok := lpa.Attorneys.Get(uid); ok {
+		return a.FullName(), nil
+	}
+
+	return "", errors.New("attorney not found")
+}
diff --git a/internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out_test.go b/internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out_test.go
new file mode 100644
index 0000000000..bb856c3b81
--- /dev/null
+++ b/internal/page/attorney/confirm_dont_want_to_be_attorney_logged_out_test.go
@@ -0,0 +1,398 @@
+package attorney
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+	"time"
+
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/sesh"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestGetConfirmDontWantToBeAttorneyLoggedOut(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	lpa := lpastore.Lpa{LpaUID: "lpa-uid"}
+
+	sessionStore := newMockSessionStore(t)
+	sessionStore.EXPECT().
+		LpaData(r).
+		Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(page.ContextWithSessionData(r.Context(), &page.SessionData{LpaID: "lpa-id"})).
+		Return(&lpa, nil)
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(w, &confirmDontWantToBeAttorneyDataLoggedOut{
+			App: testAppData,
+			Lpa: &lpa,
+		}).
+		Return(nil)
+
+	err := ConfirmDontWantToBeAttorneyLoggedOut(template.Execute, nil, lpaStoreResolvingService, sessionStore, nil, "example.com")(testAppData, w, r)
+	resp := w.Result()
+
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestGetConfirmDontWantToBeAttorneyLoggedOutWhenSessionStoreErrors(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	sessionStore := newMockSessionStore(t)
+	sessionStore.EXPECT().
+		LpaData(r).
+		Return(&sesh.LpaDataSession{}, expectedError)
+
+	err := ConfirmDontWantToBeAttorneyLoggedOut(nil, nil, nil, sessionStore, nil, "example.com")(testAppData, w, r)
+	resp := w.Result()
+
+	assert.Equal(t, expectedError, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestGetConfirmDontWantToBeAttorneyLoggedOutWhenLpaStoreResolvingServiceErrors(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	sessionStore := newMockSessionStore(t)
+	sessionStore.EXPECT().
+		LpaData(r).
+		Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(mock.Anything).
+		Return(&lpastore.Lpa{}, expectedError)
+
+	err := ConfirmDontWantToBeAttorneyLoggedOut(nil, nil, lpaStoreResolvingService, sessionStore, nil, "example.com")(testAppData, w, r)
+	resp := w.Result()
+
+	assert.Equal(t, expectedError, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestGetConfirmDontWantToBeAttorneyLoggedOutWhenTemplateErrors(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	sessionStore := newMockSessionStore(t)
+	sessionStore.EXPECT().
+		LpaData(r).
+		Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(mock.Anything).
+		Return(&lpastore.Lpa{}, nil)
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(mock.Anything, mock.Anything).
+		Return(expectedError)
+
+	err := ConfirmDontWantToBeAttorneyLoggedOut(template.Execute, nil, lpaStoreResolvingService, sessionStore, nil, "example.com")(testAppData, w, r)
+	resp := w.Result()
+
+	assert.Equal(t, expectedError, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestPostConfirmDontWantToBeAttorneyLoggedOut(t *testing.T) {
+	attorneyUID := actoruid.New()
+	replacementAttorneyUID := actoruid.New()
+	trustCorporationUID := actoruid.New()
+	replacementTrustCorporationUID := actoruid.New()
+
+	testcases := map[string]struct {
+		uid              actoruid.UID
+		attorneyFullName string
+	}{
+		"attorney": {
+			uid:              attorneyUID,
+			attorneyFullName: "d e f",
+		},
+		"replacement attorney": {
+			uid:              replacementAttorneyUID,
+			attorneyFullName: "x y z",
+		},
+		"trust corporation": {
+			uid:              trustCorporationUID,
+			attorneyFullName: "trusty",
+		},
+		"replacement trust corporation": {
+			uid:              replacementTrustCorporationUID,
+			attorneyFullName: "untrusty",
+		},
+	}
+
+	for name, tc := range testcases {
+		t.Run(name, func(t *testing.T) {
+			r, _ := http.NewRequest(http.MethodPost, "/?referenceNumber=123", nil)
+			w := httptest.NewRecorder()
+			ctx := page.ContextWithSessionData(r.Context(), &page.SessionData{LpaID: "lpa-id"})
+
+			sessionStore := newMockSessionStore(t)
+			sessionStore.EXPECT().
+				LpaData(r).
+				Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+			shareCodeData := actor.ShareCodeData{
+				LpaKey:      dynamo.LpaKey("lpa-id"),
+				LpaOwnerKey: dynamo.LpaOwnerKey(dynamo.DonorKey("donor")),
+				ActorUID:    tc.uid,
+			}
+
+			shareCodeStore := newMockShareCodeStore(t)
+			shareCodeStore.EXPECT().
+				Get(r.Context(), actor.TypeAttorney, "123").
+				Return(shareCodeData, nil)
+			shareCodeStore.EXPECT().
+				Delete(r.Context(), shareCodeData).
+				Return(nil)
+
+			lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+			lpaStoreResolvingService.EXPECT().
+				Get(ctx).
+				Return(&lpastore.Lpa{
+					LpaUID:   "lpa-uid",
+					SignedAt: time.Now(),
+					Donor: lpastore.Donor{
+						FirstNames: "a b", LastName: "c", Email: "a@example.com",
+					},
+					Attorneys: lpastore.Attorneys{
+						TrustCorporation: lpastore.TrustCorporation{UID: trustCorporationUID, Name: "trusty"},
+						Attorneys: []lpastore.Attorney{
+							{UID: attorneyUID, FirstNames: "d e", LastName: "f"},
+						},
+					},
+					ReplacementAttorneys: lpastore.Attorneys{
+						TrustCorporation: lpastore.TrustCorporation{UID: replacementTrustCorporationUID, Name: "untrusty"},
+						Attorneys: []lpastore.Attorney{
+							{UID: replacementAttorneyUID, FirstNames: "x y", LastName: "z"},
+						},
+					},
+					Type: actor.LpaTypePersonalWelfare,
+				}, nil)
+
+			notifyClient := newMockNotifyClient(t)
+			notifyClient.EXPECT().
+				SendActorEmail(ctx, "a@example.com", "lpa-uid", notify.AttorneyOptedOutEmail{
+					AttorneyFullName:  tc.attorneyFullName,
+					DonorFullName:     "a b c",
+					LpaType:           "Personal welfare",
+					LpaUID:            "lpa-uid",
+					DonorStartPageURL: "example.com" + page.Paths.Start.Format(),
+				}).
+				Return(nil)
+
+			localizer := newMockLocalizer(t)
+			localizer.EXPECT().
+				T("personal-welfare").
+				Return("Personal welfare")
+
+			testAppData.Localizer = localizer
+
+			err := ConfirmDontWantToBeAttorneyLoggedOut(nil, shareCodeStore, lpaStoreResolvingService, sessionStore, notifyClient, "example.com")(testAppData, w, r)
+
+			resp := w.Result()
+
+			assert.Nil(t, err)
+			assert.Equal(t, page.Paths.Attorney.YouHaveDecidedNotToBeAttorney.Format()+"?donorFullName=a+b+c", resp.Header.Get("Location"))
+			assert.Equal(t, http.StatusFound, resp.StatusCode)
+		})
+	}
+}
+
+func TestPostConfirmDontWantToBeAttorneyLoggedOutWhenAttorneyNotFound(t *testing.T) {
+	r, _ := http.NewRequest(http.MethodPost, "/?referenceNumber=123", nil)
+	w := httptest.NewRecorder()
+	ctx := page.ContextWithSessionData(r.Context(), &page.SessionData{LpaID: "lpa-id"})
+
+	sessionStore := newMockSessionStore(t)
+	sessionStore.EXPECT().
+		LpaData(r).
+		Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+	shareCodeData := actor.ShareCodeData{
+		LpaKey:      dynamo.LpaKey("lpa-id"),
+		LpaOwnerKey: dynamo.LpaOwnerKey(dynamo.DonorKey("donor")),
+		ActorUID:    actoruid.New(),
+	}
+
+	shareCodeStore := newMockShareCodeStore(t)
+	shareCodeStore.EXPECT().
+		Get(r.Context(), actor.TypeAttorney, "123").
+		Return(shareCodeData, nil)
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(ctx).
+		Return(&lpastore.Lpa{
+			LpaUID:   "lpa-uid",
+			SignedAt: time.Now(),
+			Donor: lpastore.Donor{
+				FirstNames: "a b", LastName: "c", Email: "a@example.com",
+			},
+			Type: actor.LpaTypePersonalWelfare,
+		}, nil)
+
+	err := ConfirmDontWantToBeAttorneyLoggedOut(nil, shareCodeStore, lpaStoreResolvingService, sessionStore, nil, "example.com")(testAppData, w, r)
+	assert.EqualError(t, err, "attorney not found")
+}
+
+func TestPostConfirmDontWantToBeAttorneyLoggedOutErrors(t *testing.T) {
+	r, _ := http.NewRequest(http.MethodPost, "/?referenceNumber=123", nil)
+	ctx := page.ContextWithSessionData(r.Context(), &page.SessionData{LpaID: "lpa-id"})
+
+	shareCodeData := actor.ShareCodeData{
+		LpaKey: dynamo.LpaKey("lpa-id"),
+	}
+
+	signedLPA := lpastore.Lpa{LpaUID: "lpa-uid", SignedAt: time.Now()}
+
+	localizer := func(t *testing.T) *mockLocalizer {
+		l := newMockLocalizer(t)
+		l.EXPECT().
+			T(mock.Anything).
+			Return("a")
+
+		return l
+	}
+
+	testcases := map[string]struct {
+		sessionStore             func(*testing.T) *mockSessionStore
+		lpaStoreResolvingService func(*testing.T) *mockLpaStoreResolvingService
+		localizer                func(*testing.T) *mockLocalizer
+		shareCodeStore           func(*testing.T) *mockShareCodeStore
+		notifyClient             func(*testing.T) *mockNotifyClient
+	}{
+		"when shareCodeStore.Get() error": {
+			sessionStore: func(t *testing.T) *mockSessionStore {
+				sessionStore := newMockSessionStore(t)
+				sessionStore.EXPECT().
+					LpaData(r).
+					Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+				return sessionStore
+			},
+			lpaStoreResolvingService: func(t *testing.T) *mockLpaStoreResolvingService {
+				lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+				lpaStoreResolvingService.EXPECT().
+					Get(ctx).
+					Return(&signedLPA, nil)
+
+				return lpaStoreResolvingService
+			},
+			shareCodeStore: func(t *testing.T) *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(shareCodeData, expectedError)
+
+				return shareCodeStore
+			},
+		},
+		"when shareCodeStore.Delete() error": {
+			sessionStore: func(t *testing.T) *mockSessionStore {
+				sessionStore := newMockSessionStore(t)
+				sessionStore.EXPECT().
+					LpaData(r).
+					Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+				return sessionStore
+			},
+			lpaStoreResolvingService: func(t *testing.T) *mockLpaStoreResolvingService {
+				lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+				lpaStoreResolvingService.EXPECT().
+					Get(ctx).
+					Return(&signedLPA, nil)
+
+				return lpaStoreResolvingService
+			},
+			localizer: localizer,
+			shareCodeStore: func(t *testing.T) *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(shareCodeData, nil)
+				shareCodeStore.EXPECT().
+					Delete(mock.Anything, mock.Anything).
+					Return(expectedError)
+
+				return shareCodeStore
+			},
+			notifyClient: func(t *testing.T) *mockNotifyClient {
+				client := newMockNotifyClient(t)
+				client.EXPECT().
+					SendActorEmail(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
+					Return(nil)
+
+				return client
+			},
+		},
+		"when notifyClient.SendActorEmail() error": {
+			sessionStore: func(t *testing.T) *mockSessionStore {
+				sessionStore := newMockSessionStore(t)
+				sessionStore.EXPECT().
+					LpaData(r).
+					Return(&sesh.LpaDataSession{LpaID: "lpa-id"}, nil)
+
+				return sessionStore
+			},
+			lpaStoreResolvingService: func(t *testing.T) *mockLpaStoreResolvingService {
+				lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+				lpaStoreResolvingService.EXPECT().
+					Get(ctx).
+					Return(&signedLPA, nil)
+
+				return lpaStoreResolvingService
+			},
+			localizer: localizer,
+			shareCodeStore: func(t *testing.T) *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(shareCodeData, nil)
+
+				return shareCodeStore
+			},
+			notifyClient: func(t *testing.T) *mockNotifyClient {
+				client := newMockNotifyClient(t)
+				client.EXPECT().
+					SendActorEmail(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
+					Return(expectedError)
+
+				return client
+			},
+		},
+	}
+
+	for name, tc := range testcases {
+		t.Run(name, func(t *testing.T) {
+			w := httptest.NewRecorder()
+
+			testAppData.Localizer = evalT(tc.localizer, t)
+
+			err := ConfirmDontWantToBeAttorneyLoggedOut(nil, evalT(tc.shareCodeStore, t), evalT(tc.lpaStoreResolvingService, t), evalT(tc.sessionStore, t), evalT(tc.notifyClient, t), "example.com")(testAppData, w, r)
+
+			resp := w.Result()
+
+			assert.Equal(t, expectedError, err)
+			assert.Equal(t, http.StatusOK, resp.StatusCode)
+		})
+	}
+}
diff --git a/internal/page/attorney/confirm_dont_want_to_be_attorney_test.go b/internal/page/attorney/confirm_dont_want_to_be_attorney_test.go
new file mode 100644
index 0000000000..e2b2668970
--- /dev/null
+++ b/internal/page/attorney/confirm_dont_want_to_be_attorney_test.go
@@ -0,0 +1,222 @@
+package attorney
+
+import (
+	"context"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+	"time"
+
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestGetConfirmDontWantToBeAttorney(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	lpa := lpastore.Lpa{LpaUID: "lpa-uid"}
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(r.Context()).
+		Return(&lpa, nil)
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(w, &confirmDontWantToBeAttorneyData{
+			App: testAppData,
+			Lpa: &lpa,
+		}).
+		Return(nil)
+
+	err := ConfirmDontWantToBeAttorney(template.Execute, lpaStoreResolvingService, nil, nil, "example.com")(testAppData, w, r, &actor.AttorneyProvidedDetails{})
+	resp := w.Result()
+
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestGetConfirmDontWantToBeAttorneyWhenLpaStoreResolvingServiceErrors(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(mock.Anything).
+		Return(&lpastore.Lpa{}, expectedError)
+
+	err := ConfirmDontWantToBeAttorney(nil, lpaStoreResolvingService, nil, nil, "example.com")(testAppData, w, r, &actor.AttorneyProvidedDetails{})
+	resp := w.Result()
+
+	assert.Equal(t, expectedError, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestGetConfirmDontWantToBeAttorneyWhenTemplateErrors(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(mock.Anything).
+		Return(&lpastore.Lpa{}, nil)
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(mock.Anything, mock.Anything).
+		Return(expectedError)
+
+	err := ConfirmDontWantToBeAttorney(template.Execute, lpaStoreResolvingService, nil, nil, "example.com")(testAppData, w, r, &actor.AttorneyProvidedDetails{})
+	resp := w.Result()
+
+	assert.Equal(t, expectedError, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestPostConfirmDontWantToBeAttorney(t *testing.T) {
+	r, _ := http.NewRequestWithContext(page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "123", SessionID: "456"}), http.MethodPost, "/?referenceNumber=123", nil)
+	w := httptest.NewRecorder()
+	uid := actoruid.New()
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(r.Context()).
+		Return(&lpastore.Lpa{
+			LpaUID:   "lpa-uid",
+			SignedAt: time.Now(),
+			Donor: lpastore.Donor{
+				FirstNames: "a b", LastName: "c", Email: "a@example.com",
+			},
+			Attorneys: lpastore.Attorneys{
+				Attorneys: []lpastore.Attorney{
+					{FirstNames: "d e", LastName: "f", UID: uid},
+				},
+			},
+			Type: actor.LpaTypePersonalWelfare,
+		}, nil)
+
+	certificateProviderStore := newMockAttorneyStore(t)
+	certificateProviderStore.EXPECT().
+		Delete(r.Context()).
+		Return(nil)
+
+	localizer := newMockLocalizer(t)
+	localizer.EXPECT().
+		T("personal-welfare").
+		Return("Personal welfare")
+
+	testAppData.Localizer = localizer
+
+	notifyClient := newMockNotifyClient(t)
+	notifyClient.EXPECT().
+		SendActorEmail(r.Context(), "a@example.com", "lpa-uid", notify.AttorneyOptedOutEmail{
+			AttorneyFullName:  "d e f",
+			DonorFullName:     "a b c",
+			LpaType:           "Personal welfare",
+			LpaUID:            "lpa-uid",
+			DonorStartPageURL: "example.com" + page.Paths.Start.Format(),
+		}).
+		Return(nil)
+
+	err := ConfirmDontWantToBeAttorney(nil, lpaStoreResolvingService, certificateProviderStore, notifyClient, "example.com")(testAppData, w, r, &actor.AttorneyProvidedDetails{
+		UID: uid,
+	})
+
+	resp := w.Result()
+
+	assert.Nil(t, err)
+	assert.Equal(t, page.Paths.Attorney.YouHaveDecidedNotToBeAttorney.Format()+"?donorFirstNames=a+b&donorFullName=a+b+c", resp.Header.Get("Location"))
+	assert.Equal(t, http.StatusFound, resp.StatusCode)
+}
+
+func TestPostConfirmDontWantToBeAttorneyWhenAttorneyNotFound(t *testing.T) {
+	r, _ := http.NewRequestWithContext(page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "123", SessionID: "456"}), http.MethodPost, "/?referenceNumber=123", nil)
+	w := httptest.NewRecorder()
+	uid := actoruid.New()
+
+	lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+	lpaStoreResolvingService.EXPECT().
+		Get(r.Context()).
+		Return(&lpastore.Lpa{
+			LpaUID:   "lpa-uid",
+			SignedAt: time.Now(),
+			Donor: lpastore.Donor{
+				FirstNames: "a b", LastName: "c", Email: "a@example.com",
+			},
+			Type: actor.LpaTypePersonalWelfare,
+		}, nil)
+
+	err := ConfirmDontWantToBeAttorney(nil, lpaStoreResolvingService, nil, nil, "example.com")(testAppData, w, r, &actor.AttorneyProvidedDetails{
+		UID: uid,
+	})
+	assert.EqualError(t, err, "attorney not found")
+}
+
+func TestPostConfirmDontWantToBeAttorneyErrors(t *testing.T) {
+	r, _ := http.NewRequest(http.MethodPost, "/?referenceNumber=123", nil)
+
+	testcases := map[string]struct {
+		certificateProviderStore func(*testing.T) *mockAttorneyStore
+		notifyClient             func(*testing.T) *mockNotifyClient
+	}{
+		"when attorneyStore.Delete() error": {
+			certificateProviderStore: func(t *testing.T) *mockAttorneyStore {
+				certificateProviderStore := newMockAttorneyStore(t)
+				certificateProviderStore.EXPECT().
+					Delete(mock.Anything).
+					Return(expectedError)
+
+				return certificateProviderStore
+			},
+		},
+		"when notifyClient.SendActorEmail() error": {
+			certificateProviderStore: func(t *testing.T) *mockAttorneyStore {
+				certificateProviderStore := newMockAttorneyStore(t)
+				certificateProviderStore.EXPECT().
+					Delete(mock.Anything).
+					Return(nil)
+
+				return certificateProviderStore
+			},
+			notifyClient: func(t *testing.T) *mockNotifyClient {
+				client := newMockNotifyClient(t)
+				client.EXPECT().
+					SendActorEmail(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
+					Return(expectedError)
+
+				return client
+			},
+		},
+	}
+
+	for name, tc := range testcases {
+		t.Run(name, func(t *testing.T) {
+			w := httptest.NewRecorder()
+
+			lpaStoreResolvingService := newMockLpaStoreResolvingService(t)
+			lpaStoreResolvingService.EXPECT().
+				Get(r.Context()).
+				Return(&lpastore.Lpa{LpaUID: "lpa-uid", SignedAt: time.Now()}, nil)
+
+			localizer := newMockLocalizer(t)
+			localizer.EXPECT().
+				T(mock.Anything).
+				Return("a")
+
+			testAppData.Localizer = localizer
+
+			err := ConfirmDontWantToBeAttorney(nil, lpaStoreResolvingService, evalT(tc.certificateProviderStore, t), evalT(tc.notifyClient, t), "example.com")(testAppData, w, r, &actor.AttorneyProvidedDetails{})
+
+			resp := w.Result()
+
+			assert.Equal(t, expectedError, err)
+			assert.Equal(t, http.StatusOK, resp.StatusCode)
+		})
+	}
+}
diff --git a/internal/page/attorney/enter_reference_number_opt_out.go b/internal/page/attorney/enter_reference_number_opt_out.go
new file mode 100644
index 0000000000..dfdbe6e145
--- /dev/null
+++ b/internal/page/attorney/enter_reference_number_opt_out.go
@@ -0,0 +1,50 @@
+package attorney
+
+import (
+	"errors"
+	"net/http"
+	"net/url"
+
+	"github.com/ministryofjustice/opg-go-common/template"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/sesh"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
+)
+
+func EnterReferenceNumberOptOut(tmpl template.Template, shareCodeStore ShareCodeStore, sessionStore SessionStore) page.Handler {
+	return func(appData page.AppData, w http.ResponseWriter, r *http.Request) error {
+		data := enterReferenceNumberData{
+			App:  appData,
+			Form: &enterReferenceNumberForm{},
+		}
+
+		if r.Method == http.MethodPost {
+			data.Form = readEnterReferenceNumberForm(r)
+			data.Errors = data.Form.Validate()
+
+			if data.Errors.None() {
+				referenceNumber := data.Form.ReferenceNumber
+
+				shareCode, err := shareCodeStore.Get(r.Context(), actor.TypeAttorney, referenceNumber)
+				if err != nil {
+					if errors.Is(err, dynamo.NotFoundError{}) {
+						data.Errors.Add("reference-number", validation.CustomError{Label: "incorrectReferenceNumber"})
+						return tmpl(w, data)
+					} else {
+						return err
+					}
+				}
+
+				if err := sessionStore.SetLpaData(r, w, &sesh.LpaDataSession{LpaID: shareCode.LpaKey.ID()}); err != nil {
+					return err
+				}
+
+				return page.Paths.Attorney.ConfirmDontWantToBeAttorneyLoggedOut.RedirectQuery(w, r, appData, url.Values{"referenceNumber": {referenceNumber}})
+			}
+		}
+
+		return tmpl(w, data)
+	}
+}
diff --git a/internal/page/attorney/enter_reference_number_opt_out_test.go b/internal/page/attorney/enter_reference_number_opt_out_test.go
new file mode 100644
index 0000000000..485c21fda5
--- /dev/null
+++ b/internal/page/attorney/enter_reference_number_opt_out_test.go
@@ -0,0 +1,179 @@
+package attorney
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"strings"
+	"testing"
+
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/sesh"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestGetEnterReferenceNumberOptOut(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	data := enterReferenceNumberData{
+		App:  testAppData,
+		Form: &enterReferenceNumberForm{},
+	}
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(w, data).
+		Return(nil)
+
+	err := EnterReferenceNumberOptOut(template.Execute, newMockShareCodeStore(t), nil)(testAppData, w, r)
+
+	resp := w.Result()
+
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestGetEnterReferenceNumberOptOutOnTemplateError(t *testing.T) {
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodGet, "/", nil)
+
+	data := enterReferenceNumberData{
+		App:  testAppData,
+		Form: &enterReferenceNumberForm{},
+	}
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(w, data).
+		Return(expectedError)
+
+	err := EnterReferenceNumberOptOut(template.Execute, newMockShareCodeStore(t), nil)(testAppData, w, r)
+
+	resp := w.Result()
+
+	assert.Equal(t, expectedError, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
+
+func TestPostEnterReferenceNumberOptOut(t *testing.T) {
+	form := url.Values{
+		"reference-number": {"abcdef 123-456"},
+	}
+
+	uid := actoruid.New()
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
+	r.Header.Add("Content-Type", page.FormUrlEncoded)
+
+	shareCodeStore := newMockShareCodeStore(t)
+	shareCodeStore.EXPECT().
+		Get(r.Context(), actor.TypeAttorney, "abcdef123456").
+		Return(actor.ShareCodeData{LpaKey: dynamo.LpaKey("lpa-id"), LpaOwnerKey: dynamo.LpaOwnerKey(dynamo.DonorKey("session-id")), ActorUID: uid}, nil)
+
+	sessionStore := newMockSessionStore(t)
+	sessionStore.EXPECT().
+		SetLpaData(r, w, &sesh.LpaDataSession{LpaID: "lpa-id"}).
+		Return(nil)
+
+	err := EnterReferenceNumberOptOut(nil, shareCodeStore, sessionStore)(testAppData, w, r)
+
+	resp := w.Result()
+
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusFound, resp.StatusCode)
+	assert.Equal(t, page.Paths.Attorney.ConfirmDontWantToBeAttorneyLoggedOut.Format()+"?referenceNumber=abcdef123456", resp.Header.Get("Location"))
+}
+
+func TestPostEnterReferenceNumberOptOutErrors(t *testing.T) {
+	form := url.Values{
+		"reference-number": {"abcdef 123-456"},
+	}
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
+	r.Header.Add("Content-Type", page.FormUrlEncoded)
+
+	testcases := map[string]struct {
+		shareCodeStore func() *mockShareCodeStore
+		sessionStore   func() *mockSessionStore
+	}{
+		"when shareCodeStore error": {
+			shareCodeStore: func() *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(actor.ShareCodeData{}, expectedError)
+
+				return shareCodeStore
+			},
+			sessionStore: func() *mockSessionStore { return nil },
+		},
+		"when sessionStore error": {
+			shareCodeStore: func() *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(actor.ShareCodeData{LpaKey: dynamo.LpaKey("lpa-id")}, nil)
+
+				return shareCodeStore
+			},
+			sessionStore: func() *mockSessionStore {
+				sessionStore := newMockSessionStore(t)
+				sessionStore.EXPECT().
+					SetLpaData(mock.Anything, mock.Anything, mock.Anything).
+					Return(expectedError)
+
+				return sessionStore
+			},
+		},
+	}
+
+	for name, tc := range testcases {
+		t.Run(name, func(t *testing.T) {
+			err := EnterReferenceNumberOptOut(nil, tc.shareCodeStore(), tc.sessionStore())(testAppData, w, r)
+
+			resp := w.Result()
+
+			assert.Equal(t, expectedError, err)
+			assert.Equal(t, http.StatusOK, resp.StatusCode)
+		})
+	}
+}
+
+func TestPostEnterReferenceNumberOptOutOnShareCodeStoreNotFoundError(t *testing.T) {
+	form := url.Values{
+		"reference-number": {"abcdef 123456"},
+	}
+
+	w := httptest.NewRecorder()
+	r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode()))
+	r.Header.Add("Content-Type", page.FormUrlEncoded)
+
+	data := enterReferenceNumberData{
+		App:    testAppData,
+		Form:   &enterReferenceNumberForm{ReferenceNumber: "abcdef123456", ReferenceNumberRaw: "abcdef 123456"},
+		Errors: validation.With("reference-number", validation.CustomError{Label: "incorrectReferenceNumber"}),
+	}
+
+	template := newMockTemplate(t)
+	template.EXPECT().
+		Execute(w, data).
+		Return(nil)
+
+	shareCodeStore := newMockShareCodeStore(t)
+	shareCodeStore.EXPECT().
+		Get(r.Context(), actor.TypeAttorney, "abcdef123456").
+		Return(actor.ShareCodeData{LpaKey: dynamo.LpaKey("lpa-id"), LpaOwnerKey: dynamo.LpaOwnerKey(dynamo.DonorKey("session-id"))}, dynamo.NotFoundError{})
+
+	err := EnterReferenceNumberOptOut(template.Execute, shareCodeStore, nil)(testAppData, w, r)
+
+	resp := w.Result()
+
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+}
diff --git a/internal/page/attorney/mock_AttorneyStore_test.go b/internal/page/attorney/mock_AttorneyStore_test.go
index da813a3987..f47c5261aa 100644
--- a/internal/page/attorney/mock_AttorneyStore_test.go
+++ b/internal/page/attorney/mock_AttorneyStore_test.go
@@ -83,6 +83,52 @@ func (_c *mockAttorneyStore_Create_Call) RunAndReturn(run func(context.Context,
 	return _c
 }
 
+// Delete provides a mock function with given fields: ctx
+func (_m *mockAttorneyStore) Delete(ctx context.Context) error {
+	ret := _m.Called(ctx)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Delete")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context) error); ok {
+		r0 = rf(ctx)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockAttorneyStore_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
+type mockAttorneyStore_Delete_Call struct {
+	*mock.Call
+}
+
+// Delete is a helper method to define mock.On call
+//   - ctx context.Context
+func (_e *mockAttorneyStore_Expecter) Delete(ctx interface{}) *mockAttorneyStore_Delete_Call {
+	return &mockAttorneyStore_Delete_Call{Call: _e.mock.On("Delete", ctx)}
+}
+
+func (_c *mockAttorneyStore_Delete_Call) Run(run func(ctx context.Context)) *mockAttorneyStore_Delete_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context))
+	})
+	return _c
+}
+
+func (_c *mockAttorneyStore_Delete_Call) Return(_a0 error) *mockAttorneyStore_Delete_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockAttorneyStore_Delete_Call) RunAndReturn(run func(context.Context) error) *mockAttorneyStore_Delete_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // Get provides a mock function with given fields: ctx
 func (_m *mockAttorneyStore) Get(ctx context.Context) (*actor.AttorneyProvidedDetails, error) {
 	ret := _m.Called(ctx)
diff --git a/internal/page/attorney/mock_Localizer_test.go b/internal/page/attorney/mock_Localizer_test.go
new file mode 100644
index 0000000000..e3f9c8eb70
--- /dev/null
+++ b/internal/page/attorney/mock_Localizer_test.go
@@ -0,0 +1,534 @@
+// Code generated by mockery v2.42.2. DO NOT EDIT.
+
+package attorney
+
+import (
+	date "github.com/ministryofjustice/opg-modernising-lpa/internal/date"
+	mock "github.com/stretchr/testify/mock"
+
+	time "time"
+)
+
+// mockLocalizer is an autogenerated mock type for the Localizer type
+type mockLocalizer struct {
+	mock.Mock
+}
+
+type mockLocalizer_Expecter struct {
+	mock *mock.Mock
+}
+
+func (_m *mockLocalizer) EXPECT() *mockLocalizer_Expecter {
+	return &mockLocalizer_Expecter{mock: &_m.Mock}
+}
+
+// Concat provides a mock function with given fields: _a0, _a1
+func (_m *mockLocalizer) Concat(_a0 []string, _a1 string) string {
+	ret := _m.Called(_a0, _a1)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Concat")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func([]string, string) string); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_Concat_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Concat'
+type mockLocalizer_Concat_Call struct {
+	*mock.Call
+}
+
+// Concat is a helper method to define mock.On call
+//   - _a0 []string
+//   - _a1 string
+func (_e *mockLocalizer_Expecter) Concat(_a0 interface{}, _a1 interface{}) *mockLocalizer_Concat_Call {
+	return &mockLocalizer_Concat_Call{Call: _e.mock.On("Concat", _a0, _a1)}
+}
+
+func (_c *mockLocalizer_Concat_Call) Run(run func(_a0 []string, _a1 string)) *mockLocalizer_Concat_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].([]string), args[1].(string))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_Concat_Call) Return(_a0 string) *mockLocalizer_Concat_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_Concat_Call) RunAndReturn(run func([]string, string) string) *mockLocalizer_Concat_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// Count provides a mock function with given fields: _a0, _a1
+func (_m *mockLocalizer) Count(_a0 string, _a1 int) string {
+	ret := _m.Called(_a0, _a1)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Count")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(string, int) string); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_Count_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Count'
+type mockLocalizer_Count_Call struct {
+	*mock.Call
+}
+
+// Count is a helper method to define mock.On call
+//   - _a0 string
+//   - _a1 int
+func (_e *mockLocalizer_Expecter) Count(_a0 interface{}, _a1 interface{}) *mockLocalizer_Count_Call {
+	return &mockLocalizer_Count_Call{Call: _e.mock.On("Count", _a0, _a1)}
+}
+
+func (_c *mockLocalizer_Count_Call) Run(run func(_a0 string, _a1 int)) *mockLocalizer_Count_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string), args[1].(int))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_Count_Call) Return(_a0 string) *mockLocalizer_Count_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_Count_Call) RunAndReturn(run func(string, int) string) *mockLocalizer_Count_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// Format provides a mock function with given fields: _a0, _a1
+func (_m *mockLocalizer) Format(_a0 string, _a1 map[string]interface{}) string {
+	ret := _m.Called(_a0, _a1)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Format")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(string, map[string]interface{}) string); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_Format_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Format'
+type mockLocalizer_Format_Call struct {
+	*mock.Call
+}
+
+// Format is a helper method to define mock.On call
+//   - _a0 string
+//   - _a1 map[string]interface{}
+func (_e *mockLocalizer_Expecter) Format(_a0 interface{}, _a1 interface{}) *mockLocalizer_Format_Call {
+	return &mockLocalizer_Format_Call{Call: _e.mock.On("Format", _a0, _a1)}
+}
+
+func (_c *mockLocalizer_Format_Call) Run(run func(_a0 string, _a1 map[string]interface{})) *mockLocalizer_Format_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string), args[1].(map[string]interface{}))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_Format_Call) Return(_a0 string) *mockLocalizer_Format_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_Format_Call) RunAndReturn(run func(string, map[string]interface{}) string) *mockLocalizer_Format_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// FormatCount provides a mock function with given fields: _a0, _a1, _a2
+func (_m *mockLocalizer) FormatCount(_a0 string, _a1 int, _a2 map[string]interface{}) string {
+	ret := _m.Called(_a0, _a1, _a2)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FormatCount")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(string, int, map[string]interface{}) string); ok {
+		r0 = rf(_a0, _a1, _a2)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_FormatCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatCount'
+type mockLocalizer_FormatCount_Call struct {
+	*mock.Call
+}
+
+// FormatCount is a helper method to define mock.On call
+//   - _a0 string
+//   - _a1 int
+//   - _a2 map[string]interface{}
+func (_e *mockLocalizer_Expecter) FormatCount(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockLocalizer_FormatCount_Call {
+	return &mockLocalizer_FormatCount_Call{Call: _e.mock.On("FormatCount", _a0, _a1, _a2)}
+}
+
+func (_c *mockLocalizer_FormatCount_Call) Run(run func(_a0 string, _a1 int, _a2 map[string]interface{})) *mockLocalizer_FormatCount_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string), args[1].(int), args[2].(map[string]interface{}))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_FormatCount_Call) Return(_a0 string) *mockLocalizer_FormatCount_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_FormatCount_Call) RunAndReturn(run func(string, int, map[string]interface{}) string) *mockLocalizer_FormatCount_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// FormatDate provides a mock function with given fields: _a0
+func (_m *mockLocalizer) FormatDate(_a0 date.TimeOrDate) string {
+	ret := _m.Called(_a0)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FormatDate")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(date.TimeOrDate) string); ok {
+		r0 = rf(_a0)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_FormatDate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatDate'
+type mockLocalizer_FormatDate_Call struct {
+	*mock.Call
+}
+
+// FormatDate is a helper method to define mock.On call
+//   - _a0 date.TimeOrDate
+func (_e *mockLocalizer_Expecter) FormatDate(_a0 interface{}) *mockLocalizer_FormatDate_Call {
+	return &mockLocalizer_FormatDate_Call{Call: _e.mock.On("FormatDate", _a0)}
+}
+
+func (_c *mockLocalizer_FormatDate_Call) Run(run func(_a0 date.TimeOrDate)) *mockLocalizer_FormatDate_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(date.TimeOrDate))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_FormatDate_Call) Return(_a0 string) *mockLocalizer_FormatDate_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_FormatDate_Call) RunAndReturn(run func(date.TimeOrDate) string) *mockLocalizer_FormatDate_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// FormatDateTime provides a mock function with given fields: _a0
+func (_m *mockLocalizer) FormatDateTime(_a0 time.Time) string {
+	ret := _m.Called(_a0)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FormatDateTime")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(time.Time) string); ok {
+		r0 = rf(_a0)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_FormatDateTime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatDateTime'
+type mockLocalizer_FormatDateTime_Call struct {
+	*mock.Call
+}
+
+// FormatDateTime is a helper method to define mock.On call
+//   - _a0 time.Time
+func (_e *mockLocalizer_Expecter) FormatDateTime(_a0 interface{}) *mockLocalizer_FormatDateTime_Call {
+	return &mockLocalizer_FormatDateTime_Call{Call: _e.mock.On("FormatDateTime", _a0)}
+}
+
+func (_c *mockLocalizer_FormatDateTime_Call) Run(run func(_a0 time.Time)) *mockLocalizer_FormatDateTime_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(time.Time))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_FormatDateTime_Call) Return(_a0 string) *mockLocalizer_FormatDateTime_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_FormatDateTime_Call) RunAndReturn(run func(time.Time) string) *mockLocalizer_FormatDateTime_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// FormatTime provides a mock function with given fields: _a0
+func (_m *mockLocalizer) FormatTime(_a0 time.Time) string {
+	ret := _m.Called(_a0)
+
+	if len(ret) == 0 {
+		panic("no return value specified for FormatTime")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(time.Time) string); ok {
+		r0 = rf(_a0)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_FormatTime_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FormatTime'
+type mockLocalizer_FormatTime_Call struct {
+	*mock.Call
+}
+
+// FormatTime is a helper method to define mock.On call
+//   - _a0 time.Time
+func (_e *mockLocalizer_Expecter) FormatTime(_a0 interface{}) *mockLocalizer_FormatTime_Call {
+	return &mockLocalizer_FormatTime_Call{Call: _e.mock.On("FormatTime", _a0)}
+}
+
+func (_c *mockLocalizer_FormatTime_Call) Run(run func(_a0 time.Time)) *mockLocalizer_FormatTime_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(time.Time))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_FormatTime_Call) Return(_a0 string) *mockLocalizer_FormatTime_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_FormatTime_Call) RunAndReturn(run func(time.Time) string) *mockLocalizer_FormatTime_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// Possessive provides a mock function with given fields: _a0
+func (_m *mockLocalizer) Possessive(_a0 string) string {
+	ret := _m.Called(_a0)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Possessive")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(string) string); ok {
+		r0 = rf(_a0)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_Possessive_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Possessive'
+type mockLocalizer_Possessive_Call struct {
+	*mock.Call
+}
+
+// Possessive is a helper method to define mock.On call
+//   - _a0 string
+func (_e *mockLocalizer_Expecter) Possessive(_a0 interface{}) *mockLocalizer_Possessive_Call {
+	return &mockLocalizer_Possessive_Call{Call: _e.mock.On("Possessive", _a0)}
+}
+
+func (_c *mockLocalizer_Possessive_Call) Run(run func(_a0 string)) *mockLocalizer_Possessive_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_Possessive_Call) Return(_a0 string) *mockLocalizer_Possessive_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_Possessive_Call) RunAndReturn(run func(string) string) *mockLocalizer_Possessive_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// SetShowTranslationKeys provides a mock function with given fields: _a0
+func (_m *mockLocalizer) SetShowTranslationKeys(_a0 bool) {
+	_m.Called(_a0)
+}
+
+// mockLocalizer_SetShowTranslationKeys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetShowTranslationKeys'
+type mockLocalizer_SetShowTranslationKeys_Call struct {
+	*mock.Call
+}
+
+// SetShowTranslationKeys is a helper method to define mock.On call
+//   - _a0 bool
+func (_e *mockLocalizer_Expecter) SetShowTranslationKeys(_a0 interface{}) *mockLocalizer_SetShowTranslationKeys_Call {
+	return &mockLocalizer_SetShowTranslationKeys_Call{Call: _e.mock.On("SetShowTranslationKeys", _a0)}
+}
+
+func (_c *mockLocalizer_SetShowTranslationKeys_Call) Run(run func(_a0 bool)) *mockLocalizer_SetShowTranslationKeys_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(bool))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_SetShowTranslationKeys_Call) Return() *mockLocalizer_SetShowTranslationKeys_Call {
+	_c.Call.Return()
+	return _c
+}
+
+func (_c *mockLocalizer_SetShowTranslationKeys_Call) RunAndReturn(run func(bool)) *mockLocalizer_SetShowTranslationKeys_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// ShowTranslationKeys provides a mock function with given fields:
+func (_m *mockLocalizer) ShowTranslationKeys() bool {
+	ret := _m.Called()
+
+	if len(ret) == 0 {
+		panic("no return value specified for ShowTranslationKeys")
+	}
+
+	var r0 bool
+	if rf, ok := ret.Get(0).(func() bool); ok {
+		r0 = rf()
+	} else {
+		r0 = ret.Get(0).(bool)
+	}
+
+	return r0
+}
+
+// mockLocalizer_ShowTranslationKeys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ShowTranslationKeys'
+type mockLocalizer_ShowTranslationKeys_Call struct {
+	*mock.Call
+}
+
+// ShowTranslationKeys is a helper method to define mock.On call
+func (_e *mockLocalizer_Expecter) ShowTranslationKeys() *mockLocalizer_ShowTranslationKeys_Call {
+	return &mockLocalizer_ShowTranslationKeys_Call{Call: _e.mock.On("ShowTranslationKeys")}
+}
+
+func (_c *mockLocalizer_ShowTranslationKeys_Call) Run(run func()) *mockLocalizer_ShowTranslationKeys_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run()
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_ShowTranslationKeys_Call) Return(_a0 bool) *mockLocalizer_ShowTranslationKeys_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_ShowTranslationKeys_Call) RunAndReturn(run func() bool) *mockLocalizer_ShowTranslationKeys_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// T provides a mock function with given fields: _a0
+func (_m *mockLocalizer) T(_a0 string) string {
+	ret := _m.Called(_a0)
+
+	if len(ret) == 0 {
+		panic("no return value specified for T")
+	}
+
+	var r0 string
+	if rf, ok := ret.Get(0).(func(string) string); ok {
+		r0 = rf(_a0)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	return r0
+}
+
+// mockLocalizer_T_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'T'
+type mockLocalizer_T_Call struct {
+	*mock.Call
+}
+
+// T is a helper method to define mock.On call
+//   - _a0 string
+func (_e *mockLocalizer_Expecter) T(_a0 interface{}) *mockLocalizer_T_Call {
+	return &mockLocalizer_T_Call{Call: _e.mock.On("T", _a0)}
+}
+
+func (_c *mockLocalizer_T_Call) Run(run func(_a0 string)) *mockLocalizer_T_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string))
+	})
+	return _c
+}
+
+func (_c *mockLocalizer_T_Call) Return(_a0 string) *mockLocalizer_T_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLocalizer_T_Call) RunAndReturn(run func(string) string) *mockLocalizer_T_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// newMockLocalizer creates a new instance of mockLocalizer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func newMockLocalizer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *mockLocalizer {
+	mock := &mockLocalizer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/internal/page/attorney/mock_LpaStoreClient_test.go b/internal/page/attorney/mock_LpaStoreClient_test.go
index 176d811836..ae824dac22 100644
--- a/internal/page/attorney/mock_LpaStoreClient_test.go
+++ b/internal/page/attorney/mock_LpaStoreClient_test.go
@@ -3,9 +3,10 @@
 package attorney
 
 import (
-	context "context"
-
 	actor "github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
+	actoruid "github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid"
+
+	context "context"
 
 	lpastore "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore"
 
@@ -73,6 +74,54 @@ func (_c *mockLpaStoreClient_SendAttorney_Call) RunAndReturn(run func(context.Co
 	return _c
 }
 
+// SendAttorneyOptOut provides a mock function with given fields: ctx, lpaUID, actorUID
+func (_m *mockLpaStoreClient) SendAttorneyOptOut(ctx context.Context, lpaUID string, actorUID actoruid.UID) error {
+	ret := _m.Called(ctx, lpaUID, actorUID)
+
+	if len(ret) == 0 {
+		panic("no return value specified for SendAttorneyOptOut")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, actoruid.UID) error); ok {
+		r0 = rf(ctx, lpaUID, actorUID)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockLpaStoreClient_SendAttorneyOptOut_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendAttorneyOptOut'
+type mockLpaStoreClient_SendAttorneyOptOut_Call struct {
+	*mock.Call
+}
+
+// SendAttorneyOptOut is a helper method to define mock.On call
+//   - ctx context.Context
+//   - lpaUID string
+//   - actorUID actoruid.UID
+func (_e *mockLpaStoreClient_Expecter) SendAttorneyOptOut(ctx interface{}, lpaUID interface{}, actorUID interface{}) *mockLpaStoreClient_SendAttorneyOptOut_Call {
+	return &mockLpaStoreClient_SendAttorneyOptOut_Call{Call: _e.mock.On("SendAttorneyOptOut", ctx, lpaUID, actorUID)}
+}
+
+func (_c *mockLpaStoreClient_SendAttorneyOptOut_Call) Run(run func(ctx context.Context, lpaUID string, actorUID actoruid.UID)) *mockLpaStoreClient_SendAttorneyOptOut_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(string), args[2].(actoruid.UID))
+	})
+	return _c
+}
+
+func (_c *mockLpaStoreClient_SendAttorneyOptOut_Call) Return(_a0 error) *mockLpaStoreClient_SendAttorneyOptOut_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockLpaStoreClient_SendAttorneyOptOut_Call) RunAndReturn(run func(context.Context, string, actoruid.UID) error) *mockLpaStoreClient_SendAttorneyOptOut_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // newMockLpaStoreClient creates a new instance of mockLpaStoreClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
 // The first argument is typically a *testing.T value.
 func newMockLpaStoreClient(t interface {
diff --git a/internal/page/attorney/mock_NotifyClient_test.go b/internal/page/attorney/mock_NotifyClient_test.go
new file mode 100644
index 0000000000..88f88489d9
--- /dev/null
+++ b/internal/page/attorney/mock_NotifyClient_test.go
@@ -0,0 +1,86 @@
+// Code generated by mockery v2.42.2. DO NOT EDIT.
+
+package attorney
+
+import (
+	context "context"
+
+	notify "github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// mockNotifyClient is an autogenerated mock type for the NotifyClient type
+type mockNotifyClient struct {
+	mock.Mock
+}
+
+type mockNotifyClient_Expecter struct {
+	mock *mock.Mock
+}
+
+func (_m *mockNotifyClient) EXPECT() *mockNotifyClient_Expecter {
+	return &mockNotifyClient_Expecter{mock: &_m.Mock}
+}
+
+// SendActorEmail provides a mock function with given fields: ctx, to, lpaUID, email
+func (_m *mockNotifyClient) SendActorEmail(ctx context.Context, to string, lpaUID string, email notify.Email) error {
+	ret := _m.Called(ctx, to, lpaUID, email)
+
+	if len(ret) == 0 {
+		panic("no return value specified for SendActorEmail")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, notify.Email) error); ok {
+		r0 = rf(ctx, to, lpaUID, email)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockNotifyClient_SendActorEmail_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendActorEmail'
+type mockNotifyClient_SendActorEmail_Call struct {
+	*mock.Call
+}
+
+// SendActorEmail is a helper method to define mock.On call
+//   - ctx context.Context
+//   - to string
+//   - lpaUID string
+//   - email notify.Email
+func (_e *mockNotifyClient_Expecter) SendActorEmail(ctx interface{}, to interface{}, lpaUID interface{}, email interface{}) *mockNotifyClient_SendActorEmail_Call {
+	return &mockNotifyClient_SendActorEmail_Call{Call: _e.mock.On("SendActorEmail", ctx, to, lpaUID, email)}
+}
+
+func (_c *mockNotifyClient_SendActorEmail_Call) Run(run func(ctx context.Context, to string, lpaUID string, email notify.Email)) *mockNotifyClient_SendActorEmail_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(notify.Email))
+	})
+	return _c
+}
+
+func (_c *mockNotifyClient_SendActorEmail_Call) Return(_a0 error) *mockNotifyClient_SendActorEmail_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockNotifyClient_SendActorEmail_Call) RunAndReturn(run func(context.Context, string, string, notify.Email) error) *mockNotifyClient_SendActorEmail_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
+// newMockNotifyClient creates a new instance of mockNotifyClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func newMockNotifyClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *mockNotifyClient {
+	mock := &mockNotifyClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/internal/page/attorney/mock_SessionStore_test.go b/internal/page/attorney/mock_SessionStore_test.go
index 0721f6a6a5..d1506eae91 100644
--- a/internal/page/attorney/mock_SessionStore_test.go
+++ b/internal/page/attorney/mock_SessionStore_test.go
@@ -80,6 +80,64 @@ func (_c *mockSessionStore_Login_Call) RunAndReturn(run func(*http.Request) (*se
 	return _c
 }
 
+// LpaData provides a mock function with given fields: r
+func (_m *mockSessionStore) LpaData(r *http.Request) (*sesh.LpaDataSession, error) {
+	ret := _m.Called(r)
+
+	if len(ret) == 0 {
+		panic("no return value specified for LpaData")
+	}
+
+	var r0 *sesh.LpaDataSession
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*http.Request) (*sesh.LpaDataSession, error)); ok {
+		return rf(r)
+	}
+	if rf, ok := ret.Get(0).(func(*http.Request) *sesh.LpaDataSession); ok {
+		r0 = rf(r)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*sesh.LpaDataSession)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*http.Request) error); ok {
+		r1 = rf(r)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mockSessionStore_LpaData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LpaData'
+type mockSessionStore_LpaData_Call struct {
+	*mock.Call
+}
+
+// LpaData is a helper method to define mock.On call
+//   - r *http.Request
+func (_e *mockSessionStore_Expecter) LpaData(r interface{}) *mockSessionStore_LpaData_Call {
+	return &mockSessionStore_LpaData_Call{Call: _e.mock.On("LpaData", r)}
+}
+
+func (_c *mockSessionStore_LpaData_Call) Run(run func(r *http.Request)) *mockSessionStore_LpaData_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(*http.Request))
+	})
+	return _c
+}
+
+func (_c *mockSessionStore_LpaData_Call) Return(_a0 *sesh.LpaDataSession, _a1 error) *mockSessionStore_LpaData_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *mockSessionStore_LpaData_Call) RunAndReturn(run func(*http.Request) (*sesh.LpaDataSession, error)) *mockSessionStore_LpaData_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // OneLogin provides a mock function with given fields: r
 func (_m *mockSessionStore) OneLogin(r *http.Request) (*sesh.OneLoginSession, error) {
 	ret := _m.Called(r)
@@ -186,6 +244,54 @@ func (_c *mockSessionStore_SetLogin_Call) RunAndReturn(run func(*http.Request, h
 	return _c
 }
 
+// SetLpaData provides a mock function with given fields: r, w, lpaDataSession
+func (_m *mockSessionStore) SetLpaData(r *http.Request, w http.ResponseWriter, lpaDataSession *sesh.LpaDataSession) error {
+	ret := _m.Called(r, w, lpaDataSession)
+
+	if len(ret) == 0 {
+		panic("no return value specified for SetLpaData")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(*http.Request, http.ResponseWriter, *sesh.LpaDataSession) error); ok {
+		r0 = rf(r, w, lpaDataSession)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// mockSessionStore_SetLpaData_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetLpaData'
+type mockSessionStore_SetLpaData_Call struct {
+	*mock.Call
+}
+
+// SetLpaData is a helper method to define mock.On call
+//   - r *http.Request
+//   - w http.ResponseWriter
+//   - lpaDataSession *sesh.LpaDataSession
+func (_e *mockSessionStore_Expecter) SetLpaData(r interface{}, w interface{}, lpaDataSession interface{}) *mockSessionStore_SetLpaData_Call {
+	return &mockSessionStore_SetLpaData_Call{Call: _e.mock.On("SetLpaData", r, w, lpaDataSession)}
+}
+
+func (_c *mockSessionStore_SetLpaData_Call) Run(run func(r *http.Request, w http.ResponseWriter, lpaDataSession *sesh.LpaDataSession)) *mockSessionStore_SetLpaData_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(*http.Request), args[1].(http.ResponseWriter), args[2].(*sesh.LpaDataSession))
+	})
+	return _c
+}
+
+func (_c *mockSessionStore_SetLpaData_Call) Return(_a0 error) *mockSessionStore_SetLpaData_Call {
+	_c.Call.Return(_a0)
+	return _c
+}
+
+func (_c *mockSessionStore_SetLpaData_Call) RunAndReturn(run func(*http.Request, http.ResponseWriter, *sesh.LpaDataSession) error) *mockSessionStore_SetLpaData_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // SetOneLogin provides a mock function with given fields: r, w, session
 func (_m *mockSessionStore) SetOneLogin(r *http.Request, w http.ResponseWriter, session *sesh.OneLoginSession) error {
 	ret := _m.Called(r, w, session)
diff --git a/internal/page/attorney/mock_test.go b/internal/page/attorney/mock_test.go
index e46b515152..3ce93a7755 100644
--- a/internal/page/attorney/mock_test.go
+++ b/internal/page/attorney/mock_test.go
@@ -2,6 +2,7 @@ package attorney
 
 import (
 	"errors"
+	"testing"
 
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid"
@@ -41,3 +42,11 @@ var (
 		ActorType:   actor.TypeReplacementTrustCorporation,
 	}
 )
+
+func evalT[T any](fn func(*testing.T) T, t *testing.T) T {
+	if fn == nil {
+		return *new(T)
+	}
+
+	return fn(t)
+}
diff --git a/internal/page/attorney/register.go b/internal/page/attorney/register.go
index b3e8358227..eca50e8c7f 100644
--- a/internal/page/attorney/register.go
+++ b/internal/page/attorney/register.go
@@ -9,6 +9,7 @@ import (
 	"github.com/ministryofjustice/opg-go-common/template"
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore"
+	"github.com/ministryofjustice/opg-modernising-lpa/internal/notify"
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/onelogin"
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/place"
@@ -16,6 +17,10 @@ import (
 	"github.com/ministryofjustice/opg-modernising-lpa/internal/sesh"
 )
 
+type Localizer interface {
+	page.Localizer
+}
+
 type LpaStoreResolvingService interface {
 	Get(ctx context.Context) (*lpastore.Lpa, error)
 }
@@ -32,8 +37,10 @@ type Logger interface {
 
 type SessionStore interface {
 	Login(r *http.Request) (*sesh.LoginSession, error)
+	LpaData(r *http.Request) (*sesh.LpaDataSession, error)
 	OneLogin(r *http.Request) (*sesh.OneLoginSession, error)
 	SetLogin(r *http.Request, w http.ResponseWriter, session *sesh.LoginSession) error
+	SetLpaData(r *http.Request, w http.ResponseWriter, lpaDataSession *sesh.LpaDataSession) error
 	SetOneLogin(r *http.Request, w http.ResponseWriter, session *sesh.OneLoginSession) error
 }
 
@@ -53,6 +60,7 @@ type AttorneyStore interface {
 	Create(ctx context.Context, shareCode actor.ShareCodeData, email string) (*actor.AttorneyProvidedDetails, error)
 	Get(ctx context.Context) (*actor.AttorneyProvidedDetails, error)
 	Put(ctx context.Context, attorney *actor.AttorneyProvidedDetails) error
+	Delete(ctx context.Context) error
 }
 
 type AddressClient interface {
@@ -68,6 +76,10 @@ type LpaStoreClient interface {
 	SendAttorney(context.Context, *lpastore.Lpa, *actor.AttorneyProvidedDetails) error
 }
 
+type NotifyClient interface {
+	SendActorEmail(ctx context.Context, to, lpaUID string, email notify.Email) error
+}
+
 type ErrorHandler func(http.ResponseWriter, *http.Request, error)
 
 func Register(
@@ -82,6 +94,8 @@ func Register(
 	dashboardStore DashboardStore,
 	lpaStoreClient LpaStoreClient,
 	lpaStoreResolvingService LpaStoreResolvingService,
+	notifyClient NotifyClient,
+	appPublicURL string,
 ) {
 	handleRoot := makeHandle(rootMux, sessionStore, errorHandler)
 
@@ -91,6 +105,12 @@ func Register(
 		page.LoginCallback(logger, oneLoginClient, sessionStore, page.Paths.Attorney.EnterReferenceNumber, dashboardStore, actor.TypeAttorney))
 	handleRoot(page.Paths.Attorney.EnterReferenceNumber, RequireSession,
 		EnterReferenceNumber(tmpls.Get("enter_reference_number.gohtml"), shareCodeStore, sessionStore, attorneyStore))
+	handleRoot(page.Paths.Attorney.EnterReferenceNumberOptOut, None,
+		EnterReferenceNumberOptOut(tmpls.Get("enter_reference_number_opt_out.gohtml"), shareCodeStore, sessionStore))
+	handleRoot(page.Paths.Attorney.ConfirmDontWantToBeAttorneyLoggedOut, None,
+		ConfirmDontWantToBeAttorneyLoggedOut(tmpls.Get("confirm_dont_want_to_be_attorney.gohtml"), shareCodeStore, lpaStoreResolvingService, sessionStore, notifyClient, appPublicURL))
+	handleRoot(page.Paths.Attorney.YouHaveDecidedNotToBeAttorney, None,
+		page.Guidance(tmpls.Get("you_have_decided_not_to_be_attorney.gohtml")))
 
 	handleAttorney := makeAttorneyHandle(rootMux, sessionStore, errorHandler, attorneyStore)
 
@@ -118,6 +138,9 @@ func Register(
 		Guidance(tmpls.Get("what_happens_next.gohtml"), lpaStoreResolvingService))
 	handleAttorney(page.Paths.Attorney.Progress, None,
 		Progress(tmpls.Get("progress.gohtml"), lpaStoreResolvingService))
+
+	handleAttorney(page.Paths.Attorney.ConfirmDontWantToBeAttorney, CanGoBack,
+		ConfirmDontWantToBeAttorney(tmpls.Get("confirm_dont_want_to_be_attorney.gohtml"), lpaStoreResolvingService, attorneyStore, notifyClient, appPublicURL))
 }
 
 type handleOpt byte
diff --git a/internal/page/attorney/register_test.go b/internal/page/attorney/register_test.go
index 29bb8c9abe..86a05c2ee0 100644
--- a/internal/page/attorney/register_test.go
+++ b/internal/page/attorney/register_test.go
@@ -18,7 +18,7 @@ import (
 
 func TestRegister(t *testing.T) {
 	mux := http.NewServeMux()
-	Register(mux, &mockLogger{}, template.Templates{}, template.Templates{}, nil, nil, nil, nil, nil, &mockDashboardStore{}, &lpastore.Client{}, &lpastore.ResolvingService{})
+	Register(mux, &mockLogger{}, template.Templates{}, template.Templates{}, nil, nil, nil, nil, nil, &mockDashboardStore{}, &lpastore.Client{}, &lpastore.ResolvingService{}, &mockNotifyClient{}, "http://app")
 
 	assert.Implements(t, (*http.Handler)(nil), mux)
 }
diff --git a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider.go b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider.go
index cc37b6b1cb..544b8f8e1f 100644
--- a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider.go
+++ b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider.go
@@ -79,7 +79,7 @@ func ConfirmDontWantToBeCertificateProvider(tmpl template.Template, lpaStoreReso
 				return err
 			}
 
-			return page.Paths.CertificateProvider.YouHaveDecidedNotToBeACertificateProvider.RedirectQuery(w, r, appData, url.Values{"donorFullName": {lpa.Donor.FullName()}})
+			return page.Paths.CertificateProvider.YouHaveDecidedNotToBeCertificateProvider.RedirectQuery(w, r, appData, url.Values{"donorFullName": {lpa.Donor.FullName()}})
 		}
 
 		return tmpl(w, data)
diff --git a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out.go b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out.go
index f9238a117e..75027317b7 100644
--- a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out.go
+++ b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out.go
@@ -39,6 +39,11 @@ func ConfirmDontWantToBeCertificateProviderLoggedOut(tmpl template.Template, sha
 		}
 
 		if r.Method == http.MethodPost {
+			shareCode, err := shareCodeStore.Get(r.Context(), actor.TypeCertificateProvider, r.URL.Query().Get("referenceNumber"))
+			if err != nil {
+				return err
+			}
+
 			var email notify.Email
 
 			if !lpa.SignedAt.IsZero() {
@@ -79,8 +84,7 @@ func ConfirmDontWantToBeCertificateProviderLoggedOut(tmpl template.Template, sha
 				}
 			}
 
-			shareCode, err := shareCodeStore.Get(r.Context(), actor.TypeCertificateProvider, r.URL.Query().Get("referenceNumber"))
-			if err != nil {
+			if err := notifyClient.SendActorEmail(ctx, lpa.Donor.Email, lpa.LpaUID, email); err != nil {
 				return err
 			}
 
@@ -88,11 +92,7 @@ func ConfirmDontWantToBeCertificateProviderLoggedOut(tmpl template.Template, sha
 				return err
 			}
 
-			if err := notifyClient.SendActorEmail(ctx, lpa.Donor.Email, lpa.LpaUID, email); err != nil {
-				return err
-			}
-
-			return page.Paths.CertificateProvider.YouHaveDecidedNotToBeACertificateProvider.RedirectQuery(w, r, appData, url.Values{"donorFullName": {lpa.Donor.FullName()}})
+			return page.Paths.CertificateProvider.YouHaveDecidedNotToBeCertificateProvider.RedirectQuery(w, r, appData, url.Values{"donorFullName": {lpa.Donor.FullName()}})
 		}
 
 		return tmpl(w, data)
diff --git a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out_test.go b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out_test.go
index abed989adc..850288c90d 100644
--- a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out_test.go
+++ b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_logged_out_test.go
@@ -286,7 +286,7 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOut(t *testing.T) {
 			resp := w.Result()
 
 			assert.Nil(t, err)
-			assert.Equal(t, page.Paths.CertificateProvider.YouHaveDecidedNotToBeACertificateProvider.Format()+"?donorFullName=a+b+c", resp.Header.Get("Location"))
+			assert.Equal(t, page.Paths.CertificateProvider.YouHaveDecidedNotToBeCertificateProvider.Format()+"?donorFullName=a+b+c", resp.Header.Get("Location"))
 			assert.Equal(t, http.StatusFound, resp.StatusCode)
 		})
 	}
@@ -341,10 +341,17 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOutErrors(t *testing.T)
 
 				return lpaStoreClient
 			},
-			shareCodeStore: func() *mockShareCodeStore { return nil },
-			donorStore:     func() *mockDonorStore { return nil },
-			localizer:      func() *mockLocalizer { return localizer },
-			notifyClient:   func() *mockNotifyClient { return nil },
+			shareCodeStore: func() *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(shareCodeData, nil)
+
+				return shareCodeStore
+			},
+			donorStore:   func() *mockDonorStore { return nil },
+			localizer:    func() *mockLocalizer { return localizer },
+			notifyClient: func() *mockNotifyClient { return nil },
 		},
 		"when donorStore.GetAny() error": {
 			sessionStore: func() *mockSessionStore {
@@ -364,7 +371,14 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOutErrors(t *testing.T)
 				return lpaStoreResolvingService
 			},
 			lpaStoreClient: func() *mockLpaStoreClient { return nil },
-			shareCodeStore: func() *mockShareCodeStore { return nil },
+			shareCodeStore: func() *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(shareCodeData, nil)
+
+				return shareCodeStore
+			},
 			donorStore: func() *mockDonorStore {
 				donorStore := newMockDonorStore(t)
 				donorStore.EXPECT().
@@ -394,7 +408,14 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOutErrors(t *testing.T)
 				return lpaStoreResolvingService
 			},
 			lpaStoreClient: func() *mockLpaStoreClient { return nil },
-			shareCodeStore: func() *mockShareCodeStore { return nil },
+			shareCodeStore: func() *mockShareCodeStore {
+				shareCodeStore := newMockShareCodeStore(t)
+				shareCodeStore.EXPECT().
+					Get(mock.Anything, mock.Anything, mock.Anything).
+					Return(shareCodeData, nil)
+
+				return shareCodeStore
+			},
 			donorStore: func() *mockDonorStore {
 				donorStore := newMockDonorStore(t)
 				donorStore.EXPECT().
@@ -426,14 +447,7 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOutErrors(t *testing.T)
 
 				return lpaStoreResolvingService
 			},
-			lpaStoreClient: func() *mockLpaStoreClient {
-				lpaStoreClient := newMockLpaStoreClient(t)
-				lpaStoreClient.EXPECT().
-					SendCertificateProviderOptOut(mock.Anything, mock.Anything, mock.Anything).
-					Return(nil)
-
-				return lpaStoreClient
-			},
+			lpaStoreClient: func() *mockLpaStoreClient { return nil },
 			shareCodeStore: func() *mockShareCodeStore {
 				shareCodeStore := newMockShareCodeStore(t)
 				shareCodeStore.EXPECT().
@@ -482,9 +496,16 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOutErrors(t *testing.T)
 
 				return shareCodeStore
 			},
-			donorStore:   func() *mockDonorStore { return nil },
-			localizer:    func() *mockLocalizer { return localizer },
-			notifyClient: func() *mockNotifyClient { return nil },
+			donorStore: func() *mockDonorStore { return nil },
+			localizer:  func() *mockLocalizer { return localizer },
+			notifyClient: func() *mockNotifyClient {
+				client := newMockNotifyClient(t)
+				client.EXPECT().
+					SendActorEmail(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
+					Return(nil)
+
+				return client
+			},
 		},
 		"when notifyClient.SendActorEmail() error": {
 			sessionStore: func() *mockSessionStore {
@@ -516,9 +537,6 @@ func TestPostConfirmDontWantToBeCertificateProviderLoggedOutErrors(t *testing.T)
 				shareCodeStore.EXPECT().
 					Get(mock.Anything, mock.Anything, mock.Anything).
 					Return(shareCodeData, nil)
-				shareCodeStore.EXPECT().
-					Delete(mock.Anything, mock.Anything).
-					Return(nil)
 
 				return shareCodeStore
 			},
diff --git a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_test.go b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_test.go
index 71793f8f2a..5497b012a5 100644
--- a/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_test.go
+++ b/internal/page/certificateprovider/confirm_dont_want_to_be_certificate_provider_test.go
@@ -239,7 +239,7 @@ func TestPostConfirmDontWantToBeCertificateProvider(t *testing.T) {
 			resp := w.Result()
 
 			assert.Nil(t, err)
-			assert.Equal(t, page.Paths.CertificateProvider.YouHaveDecidedNotToBeACertificateProvider.Format()+"?donorFullName=a+b+c", resp.Header.Get("Location"))
+			assert.Equal(t, page.Paths.CertificateProvider.YouHaveDecidedNotToBeCertificateProvider.Format()+"?donorFullName=a+b+c", resp.Header.Get("Location"))
 			assert.Equal(t, http.StatusFound, resp.StatusCode)
 		})
 	}
diff --git a/internal/page/certificateprovider/enter_reference_number_opt_out.go b/internal/page/certificateprovider/enter_reference_number_opt_out.go
index dcb2af9a28..3de5276df7 100644
--- a/internal/page/certificateprovider/enter_reference_number_opt_out.go
+++ b/internal/page/certificateprovider/enter_reference_number_opt_out.go
@@ -24,7 +24,7 @@ func EnterReferenceNumberOptOut(tmpl template.Template, shareCodeStore ShareCode
 			data.Form = readEnterReferenceNumberForm(r)
 			data.Errors = data.Form.Validate()
 
-			if len(data.Errors) == 0 {
+			if data.Errors.None() {
 				referenceNumber := data.Form.ReferenceNumber
 
 				shareCode, err := shareCodeStore.Get(r.Context(), actor.TypeCertificateProvider, referenceNumber)
diff --git a/internal/page/certificateprovider/register.go b/internal/page/certificateprovider/register.go
index a65cc38d88..b9343ed07c 100644
--- a/internal/page/certificateprovider/register.go
+++ b/internal/page/certificateprovider/register.go
@@ -125,8 +125,8 @@ func Register(
 		EnterReferenceNumberOptOut(tmpls.Get("enter_reference_number_opt_out.gohtml"), shareCodeStore, sessionStore))
 	handleRoot(page.Paths.CertificateProvider.ConfirmDontWantToBeCertificateProviderLoggedOut,
 		ConfirmDontWantToBeCertificateProviderLoggedOut(tmpls.Get("confirm_dont_want_to_be_certificate_provider.gohtml"), shareCodeStore, lpaStoreResolvingService, lpaStoreClient, donorStore, sessionStore, notifyClient, appPublicURL))
-	handleRoot(page.Paths.CertificateProvider.YouHaveDecidedNotToBeACertificateProvider,
-		YouHaveDecidedNotToBeACertificateProvider(tmpls.Get("you_have_decided_not_to_be_a_certificate_provider.gohtml")))
+	handleRoot(page.Paths.CertificateProvider.YouHaveDecidedNotToBeCertificateProvider,
+		Guidance(tmpls.Get("you_have_decided_not_to_be_a_certificate_provider.gohtml"), nil, nil))
 
 	handleCertificateProvider := makeCertificateProviderHandle(rootMux, sessionStore, errorHandler, certificateProviderStore)
 
diff --git a/internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.go b/internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.go
deleted file mode 100644
index a1dfcf36f3..0000000000
--- a/internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package certificateprovider
-
-import (
-	"net/http"
-
-	"github.com/ministryofjustice/opg-go-common/template"
-	"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
-	"github.com/ministryofjustice/opg-modernising-lpa/internal/validation"
-)
-
-type youHaveDecidedNotToBeACertificateProviderData struct {
-	App           page.AppData
-	Errors        validation.List
-	DonorFullName string
-}
-
-func YouHaveDecidedNotToBeACertificateProvider(tmpl template.Template) page.Handler {
-	return func(appData page.AppData, w http.ResponseWriter, r *http.Request) error {
-		return tmpl(w, youHaveDecidedNotToBeACertificateProviderData{
-			App:           appData,
-			DonorFullName: r.URL.Query().Get("donorFullName"),
-		})
-	}
-}
diff --git a/internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider_test.go b/internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider_test.go
deleted file mode 100644
index d2bb313c23..0000000000
--- a/internal/page/certificateprovider/you_have_decided_not_to_be_a_certificate_provider_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package certificateprovider
-
-import (
-	"net/http"
-	"net/http/httptest"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
-)
-
-func TestYouHaveDecidedNotToBeACertificateProvider(t *testing.T) {
-	w := httptest.NewRecorder()
-	r, _ := http.NewRequest(http.MethodGet, "/?donorFullName=a+b+c", nil)
-
-	template := newMockTemplate(t)
-	template.EXPECT().
-		Execute(w, youHaveDecidedNotToBeACertificateProviderData{
-			App:           testAppData,
-			DonorFullName: "a b c",
-		}).
-		Return(nil)
-
-	err := YouHaveDecidedNotToBeACertificateProvider(template.Execute)(testAppData, w, r)
-
-	resp := w.Result()
-
-	assert.Nil(t, err)
-	assert.Equal(t, http.StatusOK, resp.StatusCode)
-}
-
-func TestYouHaveDecidedNotToBeACertificateProviderWhenTemplateError(t *testing.T) {
-	w := httptest.NewRecorder()
-	r, _ := http.NewRequest(http.MethodGet, "/?donorFullName=a+b+c", nil)
-
-	template := newMockTemplate(t)
-	template.EXPECT().
-		Execute(mock.Anything, mock.Anything).
-		Return(expectedError)
-
-	err := YouHaveDecidedNotToBeACertificateProvider(template.Execute)(testAppData, w, r)
-
-	resp := w.Result()
-
-	assert.Equal(t, expectedError, err)
-	assert.Equal(t, http.StatusOK, resp.StatusCode)
-}
diff --git a/internal/page/paths.go b/internal/page/paths.go
index 636e7473cc..4eeab84a75 100644
--- a/internal/page/paths.go
+++ b/internal/page/paths.go
@@ -237,23 +237,27 @@ func (p SupporterLpaPath) IsManageOrganisation() bool {
 }
 
 type AttorneyPaths struct {
-	EnterReferenceNumber Path
-	Login                Path
-	LoginCallback        Path
-	Start                Path
-
-	CodeOfConduct             AttorneyPath
-	ConfirmYourDetails        AttorneyPath
-	MobileNumber              AttorneyPath
-	Progress                  AttorneyPath
-	ReadTheLpa                AttorneyPath
-	RightsAndResponsibilities AttorneyPath
-	Sign                      AttorneyPath
-	TaskList                  AttorneyPath
-	WhatHappensNext           AttorneyPath
-	WhatHappensWhenYouSign    AttorneyPath
-	WouldLikeSecondSignatory  AttorneyPath
-	YourPreferredLanguage     AttorneyPath
+	ConfirmDontWantToBeAttorneyLoggedOut Path
+	EnterReferenceNumber                 Path
+	EnterReferenceNumberOptOut           Path
+	Login                                Path
+	LoginCallback                        Path
+	Start                                Path
+	YouHaveDecidedNotToBeAttorney        Path
+
+	CodeOfConduct               AttorneyPath
+	ConfirmDontWantToBeAttorney AttorneyPath
+	ConfirmYourDetails          AttorneyPath
+	MobileNumber                AttorneyPath
+	Progress                    AttorneyPath
+	ReadTheLpa                  AttorneyPath
+	RightsAndResponsibilities   AttorneyPath
+	Sign                        AttorneyPath
+	TaskList                    AttorneyPath
+	WhatHappensNext             AttorneyPath
+	WhatHappensWhenYouSign      AttorneyPath
+	WouldLikeSecondSignatory    AttorneyPath
+	YourPreferredLanguage       AttorneyPath
 }
 
 type CertificateProviderPaths struct {
@@ -262,7 +266,7 @@ type CertificateProviderPaths struct {
 	EnterReferenceNumber                            Path
 	EnterReferenceNumberOptOut                      Path
 	ConfirmDontWantToBeCertificateProviderLoggedOut Path
-	YouHaveDecidedNotToBeACertificateProvider       Path
+	YouHaveDecidedNotToBeCertificateProvider        Path
 
 	CertificateProvided                    CertificateProviderPath
 	ConfirmDontWantToBeCertificateProvider CertificateProviderPath
@@ -475,28 +479,32 @@ var Paths = AppPaths{
 		WhatHappensNext:                                 "/what-happens-next",
 		WhatIsYourHomeAddress:                           "/what-is-your-home-address",
 		WhoIsEligible:                                   "/certificate-provider-who-is-eligible",
-		YouHaveDecidedNotToBeACertificateProvider:       "/you-have-decided-not-to-be-a-certificate-provider",
+		YouHaveDecidedNotToBeCertificateProvider:        "/you-have-decided-not-to-be-a-certificate-provider",
 		YourPreferredLanguage:                           "/your-preferred-language",
 		YourRole:                                        "/your-role",
 	},
 
 	Attorney: AttorneyPaths{
-		CodeOfConduct:             "/code-of-conduct",
-		ConfirmYourDetails:        "/confirm-your-details",
-		EnterReferenceNumber:      "/attorney-enter-reference-number",
-		Login:                     "/attorney-login",
-		LoginCallback:             "/attorney-login-callback",
-		MobileNumber:              "/mobile-number",
-		Progress:                  "/progress",
-		ReadTheLpa:                "/read-the-lpa",
-		RightsAndResponsibilities: "/legal-rights-and-responsibilities",
-		Sign:                      "/sign",
-		Start:                     "/attorney-start",
-		TaskList:                  "/task-list",
-		WhatHappensNext:           "/what-happens-next",
-		WhatHappensWhenYouSign:    "/what-happens-when-you-sign-the-lpa",
-		WouldLikeSecondSignatory:  "/would-like-second-signatory",
-		YourPreferredLanguage:     "/your-preferred-language",
+		CodeOfConduct:                        "/code-of-conduct",
+		ConfirmDontWantToBeAttorney:          "/confirm-you-do-not-want-to-be-an-attorney",
+		ConfirmDontWantToBeAttorneyLoggedOut: "/confirm-you-do-not-want-to-be-an-attorney",
+		ConfirmYourDetails:                   "/confirm-your-details",
+		EnterReferenceNumber:                 "/attorney-enter-reference-number",
+		EnterReferenceNumberOptOut:           "/attorney-enter-reference-number-opt-out",
+		Login:                                "/attorney-login",
+		LoginCallback:                        "/attorney-login-callback",
+		MobileNumber:                         "/mobile-number",
+		Progress:                             "/progress",
+		ReadTheLpa:                           "/read-the-lpa",
+		RightsAndResponsibilities:            "/legal-rights-and-responsibilities",
+		Sign:                                 "/sign",
+		Start:                                "/attorney-start",
+		TaskList:                             "/task-list",
+		WhatHappensNext:                      "/what-happens-next",
+		WhatHappensWhenYouSign:               "/what-happens-when-you-sign-the-lpa",
+		WouldLikeSecondSignatory:             "/would-like-second-signatory",
+		YouHaveDecidedNotToBeAttorney:        "/you-have-decided-not-to-be-an-attorney",
+		YourPreferredLanguage:                "/your-preferred-language",
 	},
 
 	Supporter: SupporterPaths{
diff --git a/internal/page/share_code.go b/internal/page/share_code.go
index d88b5dbd19..70640b979c 100644
--- a/internal/page/share_code.go
+++ b/internal/page/share_code.go
@@ -126,8 +126,9 @@ func (s *ShareCodeSender) sendOriginalAttorney(ctx context.Context, appData AppD
 			DonorFirstNamesPossessive: appData.Localizer.Possessive(lpa.Donor.FirstNames),
 			DonorFullName:             lpa.Donor.FullName(),
 			LpaType:                   localize.LowerFirst(appData.Localizer.T(lpa.Type.String())),
-			AttorneyStartPageURL:      fmt.Sprintf("%s%s", s.appPublicURL, Paths.Attorney.Start),
+			AttorneyStartPageURL:      s.appPublicURL + Paths.Attorney.Start.Format(),
 			ShareCode:                 shareCode,
+			AttorneyOptOutURL:         s.appPublicURL + Paths.Attorney.EnterReferenceNumberOptOut.Format(),
 		})
 }
 
@@ -148,8 +149,9 @@ func (s *ShareCodeSender) sendReplacementAttorney(ctx context.Context, appData A
 			DonorFirstNamesPossessive: appData.Localizer.Possessive(lpa.Donor.FirstNames),
 			DonorFullName:             lpa.Donor.FullName(),
 			LpaType:                   localize.LowerFirst(appData.Localizer.T(lpa.Type.String())),
-			AttorneyStartPageURL:      fmt.Sprintf("%s%s", s.appPublicURL, Paths.Attorney.Start),
+			AttorneyStartPageURL:      s.appPublicURL + Paths.Attorney.Start.Format(),
 			ShareCode:                 shareCode,
+			AttorneyOptOutURL:         s.appPublicURL + Paths.Attorney.EnterReferenceNumberOptOut.Format(),
 		})
 }
 
@@ -176,6 +178,7 @@ func (s *ShareCodeSender) sendTrustCorporation(ctx context.Context, appData AppD
 			LpaType:                   localize.LowerFirst(appData.Localizer.T(lpa.Type.String())),
 			AttorneyStartPageURL:      fmt.Sprintf("%s%s", s.appPublicURL, Paths.Attorney.Start),
 			ShareCode:                 shareCode,
+			AttorneyOptOutURL:         s.appPublicURL + Paths.Attorney.EnterReferenceNumberOptOut.Format(),
 		})
 }
 
@@ -202,6 +205,7 @@ func (s *ShareCodeSender) sendReplacementTrustCorporation(ctx context.Context, a
 			LpaType:                   localize.LowerFirst(appData.Localizer.T(lpa.Type.String())),
 			AttorneyStartPageURL:      fmt.Sprintf("%s%s", s.appPublicURL, Paths.Attorney.Start),
 			ShareCode:                 shareCode,
+			AttorneyOptOutURL:         s.appPublicURL + Paths.Attorney.EnterReferenceNumberOptOut.Format(),
 		})
 }
 
diff --git a/internal/page/share_code_test.go b/internal/page/share_code_test.go
index d7d010a51b..1400c58357 100644
--- a/internal/page/share_code_test.go
+++ b/internal/page/share_code_test.go
@@ -682,6 +682,7 @@ func TestShareCodeSenderSendAttorneys(t *testing.T) {
 			DonorFirstNamesPossessive: "Jan's",
 			LpaType:                   "property and affairs",
 			AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+			AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 		}).
 		Return(nil)
 	notifyClient.EXPECT().
@@ -693,6 +694,7 @@ func TestShareCodeSenderSendAttorneys(t *testing.T) {
 			DonorFirstNamesPossessive: "Jan's",
 			LpaType:                   "property and affairs",
 			AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+			AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 		}).
 		Return(nil)
 	notifyClient.EXPECT().
@@ -704,6 +706,7 @@ func TestShareCodeSenderSendAttorneys(t *testing.T) {
 			DonorFirstNamesPossessive: "Jan's",
 			LpaType:                   "property and affairs",
 			AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+			AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 		}).
 		Return(nil)
 	notifyClient.EXPECT().
@@ -715,6 +718,7 @@ func TestShareCodeSenderSendAttorneys(t *testing.T) {
 			DonorFirstNamesPossessive: "Jan's",
 			LpaType:                   "property and affairs",
 			AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+			AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 		}).
 		Return(nil)
 	notifyClient.EXPECT().
@@ -726,6 +730,7 @@ func TestShareCodeSenderSendAttorneys(t *testing.T) {
 			DonorFirstNamesPossessive: "Jan's",
 			LpaType:                   "property and affairs",
 			AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+			AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 		}).
 		Return(nil)
 
@@ -889,6 +894,7 @@ func TestShareCodeSenderSendAttorneysWithTestCode(t *testing.T) {
 					DonorFirstNamesPossessive: "Jan's",
 					LpaType:                   "property and affairs",
 					AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+					AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 				}).
 				Return(nil)
 			notifyClient.EXPECT().
@@ -900,6 +906,7 @@ func TestShareCodeSenderSendAttorneysWithTestCode(t *testing.T) {
 					DonorFirstNamesPossessive: "Jan's",
 					LpaType:                   "property and affairs",
 					AttorneyStartPageURL:      fmt.Sprintf("http://app%s", Paths.Attorney.Start),
+					AttorneyOptOutURL:         fmt.Sprintf("http://app%s", Paths.Attorney.EnterReferenceNumberOptOut),
 				}).
 				Return(nil)
 
diff --git a/lang/cy.json b/lang/cy.json
index 09057e907f..4d13a29ae8 100644
--- a/lang/cy.json
+++ b/lang/cy.json
@@ -554,7 +554,8 @@
     "certificateProvidedConcerns": "<p class=\"govuk-body\">Os bydd unrhyw bryderon o gwbl gan Swyddfa’r Gwarcheidwad Cyhoeddus ynghylch LPA {{ .DonorFirstNamesPossessive }} yn y dyfodol, gallem gysylltu â chi.</p> <p class=\"govuk-body\">Os bydd angen i chi adrodd am bryder o gwbl, gallwch wneud hyn ar unrhyw adeg gan ddefnyddio’r <a href=\"/\" class=\"govuk-link\">gwasanaeth adrodd am bryder ynghylch LPA</a>.</p>",
     "goToYourDashboard": "Mynd i’ch dangosfwrdd",
     "beAnAttorney": "Bod yn atwrnai ar atwrneiaeth arhosol (LPA)",
-    "enterYourAttorneyReferenceNumber": "Rhowch eich cyfeirnod",
+    "enterYourReferenceNumber": "Rhowch eich cyfeirnod",
+    "enterYourAttorneyReferenceNumber": "Welsh",
     "attorneyReferenceNumberContent": "<p class=\"govuk-body\">Gallwch weld y cyfeirnod yn yr e-bost sy’n eich gwahodd i fod yn atwrnai neu’n atwrnai wrth gefn.</p>",
     "attorneyReferenceNumber": "Cyfeirnod",
     "attorney18OrOverWarning": "Rhaid i chi fod yn 18 oed neu hŷn i fod yn atwrnai.",
@@ -1301,5 +1302,12 @@
     "thisIsNotTheRegisteredLpaWarning": "Welsh",
     "howLpaCanBeUsed": "Welsh",
     "trustCorporationAttorney": "Welsh",
-    "replacementTrustCorporationAttorney": "Welsh"
+    "replacementTrustCorporationAttorney": "Welsh",
+    "iDoNotWantToBeAttorney": "Welsh",
+    "confirmYouDoNotWantToBeAttorney": "Welsh",
+    "youHaveToldUsYouDoNotWantToBeAttorneyOn": "Welsh {{.DonorFullNamePossessive}}",
+    "whenYouConfirmWeWillContactToExplain": "Welsh {{.DonorFullName}}",
+    "youHaveConfirmedYouDoNotWantToBeAttorney": "Welsh",
+    "youHaveConfirmedYouDoNotWantToBeDonorsAttorney": "Welsh {{.DonorFullNamePossessive}}",
+    "youHaveConfirmedYouDoNotWantToBeAttorneyContent": "<p class=\"govuk-body\">Welsh {{.DonorFirstNames}}</p>"
 }
diff --git a/lang/en.json b/lang/en.json
index bef7b3e9e4..e4bddca28c 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -504,7 +504,8 @@
     "certificateProvidedConcerns": "<p class=\"govuk-body\">If the Office of the Public Guardian ever has any concerns about {{ .DonorFirstNamesPossessive }} LPA in future, we may contact you.</p> <p class=\"govuk-body\">If you ever need to report a concern, you can do this at any time using the <a href=\"/\" class=\"govuk-link\">report a concern about an LPA service</a>.</p>",
     "goToYourDashboard": "Go to your dashboard",
     "beAnAttorney": "Be an attorney on a lasting power of attorney (LPA)",
-    "enterYourAttorneyReferenceNumber": "Enter your reference number",
+    "enterYourReferenceNumber": "Enter your reference number",
+    "enterYourAttorneyReferenceNumber": "Enter your attorney reference number",
     "attorneyReferenceNumberContent": "<p class=\"govuk-body\">You can find this in the email inviting you to be an attorney or a replacement attorney.</p>",
     "attorneyReferenceNumber": "Reference number",
     "attorney18OrOverWarning": "You must be aged 18 or over to be an attorney.",
@@ -1230,5 +1231,12 @@
     "thisIsNotTheRegisteredLpaWarning": "This is not the registered LPA. You cannot use it or give organisations access to it.",
     "howLpaCanBeUsed": "How the LPA can be used",
     "trustCorporationAttorney": "Trust corporation attorney",
-    "replacementTrustCorporationAttorney": "Replacement trust corporation attorney"
+    "replacementTrustCorporationAttorney": "Replacement trust corporation attorney",
+    "iDoNotWantToBeAttorney": "I do not want to be an attorney",
+    "confirmYouDoNotWantToBeAttorney": "Confirm you do not want to be an attorney",
+    "youHaveToldUsYouDoNotWantToBeAttorneyOn": "You have told us that you do not want to be an attorney on {{.DonorFullNamePossessive}} LPA:",
+    "whenYouConfirmWeWillContactToExplain": "When you confirm, we will contact {{.DonorFullName}} to explain what they can do next.",
+    "youHaveConfirmedYouDoNotWantToBeAttorney": "You have confirmed that you do not want to be an attorney",
+    "youHaveConfirmedYouDoNotWantToBeDonorsAttorney": "You have confirmed that you do not want to be {{.DonorFullNamePossessive}} attorney.",
+    "youHaveConfirmedYouDoNotWantToBeAttorneyContent": "<p class=\"govuk-body\">We have let {{.DonorFirstNames}} know about your decision.</p><p class=\"govuk-body\">You do not need to do anything else.</p>"
 }
diff --git a/web/template/attorney/confirm_dont_want_to_be_attorney.gohtml b/web/template/attorney/confirm_dont_want_to_be_attorney.gohtml
new file mode 100644
index 0000000000..009fa92b67
--- /dev/null
+++ b/web/template/attorney/confirm_dont_want_to_be_attorney.gohtml
@@ -0,0 +1,34 @@
+{{ template "page" . }}
+
+{{ define "pageTitle" }}{{ tr .App "confirmYouDoNotWantToBeAttorney" }}{{ end }}
+
+{{ define "main" }}
+    <div class="govuk-grid-row">
+        <div class="govuk-grid-column-two-thirds">
+            <h1 class="govuk-heading-xl">{{ tr .App "confirmYouDoNotWantToBeAttorney" }}</h1>
+
+            <p class="govuk-body">{{ trFormat .App "youHaveToldUsYouDoNotWantToBeAttorneyOn" "DonorFullNamePossessive" (possessive .App .Lpa.Donor.FullName) }}
+            
+            <div class="govuk-inset-text">
+                <dl class="govuk-summary-list govuk-summary-list--no-border app-summary-list--no-vertical-padding">
+                    <div class="govuk-summary-list__row">
+                        <dt class="govuk-summary-list__key">{{ tr .App "lpaType" }}:</dt>
+                        <dd class="govuk-summary-list__value">{{ tr .App .Lpa.Type.String }}</dd>
+                    </div>
+                    <div class="govuk-summary-list__row">
+                        <dt class="govuk-summary-list__key">{{ tr .App "lpaNumber" }}:</dt>
+                        <dd class="govuk-summary-list__value">{{ .Lpa.LpaUID }}</dd>
+                    </div>
+                </dl>
+            </div>
+
+            <p class="govuk-body">{{ trFormat .App "whenYouConfirmWeWillContactToExplain" "DonorFullName" .Lpa.Donor.FullName }}
+            
+            <form novalidate method="post">
+                <button type="submit" class="govuk-button" data-module="govuk-button">{{ tr .App "confirm" }}</button>
+
+                {{ template "csrf-field" . }}
+            </form>
+        </div>
+    </div>
+{{ end }}
diff --git a/web/template/attorney/enter_reference_number.gohtml b/web/template/attorney/enter_reference_number.gohtml
index 9a00c5dd49..8d21782985 100644
--- a/web/template/attorney/enter_reference_number.gohtml
+++ b/web/template/attorney/enter_reference_number.gohtml
@@ -1,11 +1,11 @@
 {{ template "page" . }}
 
-{{ define "pageTitle" }}{{ tr .App "enterYourAttorneyReferenceNumber" }}{{ end }}
+{{ define "pageTitle" }}{{ tr .App "enterYourReferenceNumber" }}{{ end }}
 
 {{ define "main" }}
   <div class="govuk-grid-row">
     <div class="govuk-grid-column-two-thirds">
-      <h1 class="govuk-heading-xl">{{ tr .App "enterYourAttorneyReferenceNumber" }}</h1>
+      <h1 class="govuk-heading-xl">{{ tr .App "enterYourReferenceNumber" }}</h1>
 
       {{ trHtml .App "attorneyReferenceNumberContent" }}
 
diff --git a/web/template/attorney/enter_reference_number_opt_out.gohtml b/web/template/attorney/enter_reference_number_opt_out.gohtml
new file mode 100644
index 0000000000..401059b103
--- /dev/null
+++ b/web/template/attorney/enter_reference_number_opt_out.gohtml
@@ -0,0 +1,22 @@
+{{ template "page" . }}
+
+{{ define "pageTitle" }}{{ tr .App "enterYourAttorneyReferenceNumber" }}{{ end }}
+
+{{ define "main" }}
+    <div class="govuk-grid-row">
+        <div class="govuk-grid-column-two-thirds">
+            <h1 class="govuk-heading-xl">{{ tr .App "enterYourAttorneyReferenceNumber" }}</h1>
+
+            {{ trHtml .App "attorneyReferenceNumberContent" }}
+
+            <form novalidate method="post">
+                {{ template "input" (input . "reference-number" "attorneyReferenceNumber" .Form.ReferenceNumberRaw
+                    "classes" "govuk-input--width-20 govuk-!-margin-bottom-3 govuk-input--extra-letter-spacing"
+                    "hint" "referenceNumberHint")}}
+
+                <button type="submit" class="govuk-button" data-module="govuk-button">{{ tr .App "continue" }}</button>
+                {{ template "csrf-field" . }}
+            </form>
+        </div>
+    </div>
+{{ end }}
diff --git a/web/template/attorney/sign.gohtml b/web/template/attorney/sign.gohtml
index 6951c96d03..333214abcb 100644
--- a/web/template/attorney/sign.gohtml
+++ b/web/template/attorney/sign.gohtml
@@ -99,7 +99,11 @@
           </div>
         </div>
 
-        {{ template "buttons" (button .App "submitSignature") }}
+        <div class="govuk-button-group">
+          <button type="submit" class="govuk-button" data-module="govuk-button">{{ tr .App "submitSignature" }}</button>
+          <a href="{{ link .App (global.Paths.Attorney.ConfirmDontWantToBeAttorney.Format .LpaID) }}" class="govuk-button govuk-button--warning" data-module="govuk-button">{{ tr .App "iDoNotWantToBeAttorney" }}</a>
+        </div>
+        
         {{ template "csrf-field" . }}
       </form>
     </div>
diff --git a/web/template/attorney/you_have_decided_not_to_be_attorney.gohtml b/web/template/attorney/you_have_decided_not_to_be_attorney.gohtml
new file mode 100644
index 0000000000..5325d477f8
--- /dev/null
+++ b/web/template/attorney/you_have_decided_not_to_be_attorney.gohtml
@@ -0,0 +1,15 @@
+{{ template "page" . }}
+
+{{ define "pageTitle" }}{{ tr .App "youHaveConfirmedYouDoNotWantToBeAttorney" }}{{ end }}
+
+{{ define "main" }}
+    <div class="govuk-grid-row">
+        <div class="govuk-grid-column-two-thirds">
+            <div class="govuk-panel govuk-panel--confirmation">
+                <h1 class="govuk-panel__title">{{ trFormat .App "youHaveConfirmedYouDoNotWantToBeDonorsAttorney" "DonorFullNamePossessive" (possessive .App (.App.Query.Get "donorFullName")) }}</h1>
+            </div>
+
+            {{ trFormatHtml .App "youHaveConfirmedYouDoNotWantToBeAttorneyContent" "DonorFirstNames" (.App.Query.Get "donorFirstNames") }}
+        </div>
+    </div>
+{{ end }}
diff --git a/web/template/certificateprovider/enter_reference_number.gohtml b/web/template/certificateprovider/enter_reference_number.gohtml
index b614b98962..fe40786d3f 100644
--- a/web/template/certificateprovider/enter_reference_number.gohtml
+++ b/web/template/certificateprovider/enter_reference_number.gohtml
@@ -10,12 +10,11 @@
             {{ trHtml .App "certificateProviderReferenceNumberContent" }}
 
             <form novalidate method="post">
-                <div class="govuk-form-group">
-                    <fieldset class="govuk-fieldset">
-                        {{ template "input" (input . "reference-number" "certificateProviderReferenceNumber" .Form.ReferenceNumberRaw "classes" "govuk-input--width-20 govuk-!-margin-bottom-3 govuk-input--extra-letter-spacing" "hint" "referenceNumberHint")}}
-                        <button type="submit" class="govuk-button" data-module="govuk-button">{{ tr .App "saveAndContinue" }}</button>
-                    </fieldset>
-                </div>
+                {{ template "input" (input . "reference-number" "certificateProviderReferenceNumber" .Form.ReferenceNumberRaw
+                    "classes" "govuk-input--width-20 govuk-!-margin-bottom-3 govuk-input--extra-letter-spacing"
+                    "hint" "referenceNumberHint")}}
+
+                <button type="submit" class="govuk-button" data-module="govuk-button">{{ tr .App "saveAndContinue" }}</button>
                 {{ template "csrf-field" . }}
             </form>
         </div>
diff --git a/web/template/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.gohtml b/web/template/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.gohtml
index d59a13a030..6d1a7137e5 100644
--- a/web/template/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.gohtml
+++ b/web/template/certificateprovider/you_have_decided_not_to_be_a_certificate_provider.gohtml
@@ -8,7 +8,7 @@
             <div class="govuk-panel govuk-panel--confirmation">
                 <h1 class="govuk-panel__title">{{ tr .App "thankYou" }}</h1>
                 <div class="govuk-panel__body">
-                    {{ trFormat .App "weHaveContactedDonorToLetThemKnowYourDecision" "DonorFullName" .DonorFullName }}
+                    {{ trFormat .App "weHaveContactedDonorToLetThemKnowYourDecision" "DonorFullName" (.App.Query.Get "donorFullName") }}
                 </div>
             </div>