3rd-Party Declarations · Part 3 of 3

Confirm, submit, notify, and ship

Parts 1 and 2 built a multi-row form that validates itself. This last leg gives the Submit click its consequences: a confirmation popup, a batch write to the 3rd party inventory list with a full audit trail, an email to the IAM team, and a success screen that resets for the next declaration. Then you test end-to-end and publish. Every name, number and formula has a copy button.

1 confirmation popup 1 notification flow 1 big submit formula then publish 🎉
0 of 9 steps done

Where you're starting from

Prerequisites — Parts 1 and 2 are doneThe gallery form adds, edits and deletes rows with every keystroke mirrored into colSystems; validation blocks bad submissions with red borders and per-row messages. A fully valid Submit click already sets gblShowConfirm — it just has nothing to show yet. That's the first thing we fix.
Substitute your valuesWherever you see <YOUR-SITE-URL> or <IAM-DL-EMAIL>, substitute your own SharePoint site URL and the IAM distribution list address.
How to read this guide — click vs enterEvery step uses the same three markers, so you always know where to look. A navy button like + Insert is something you click in Power Apps, SharePoint or Power Automate — a menu, a button, or a property name. An amber chip like btnConfirmYes is a value you enter — tap the chip to copy it (the ⧉ mark is the reminder), then paste it where the step says. A key cap like Ctrl+A is pressed on the keyboard. Dark boxes are complete formulas with their own Copy button. Paste, never retype.

Phase 1 — The confirmation popup

C1Build the confirmation popup

Six controls, inserted in this order so they stack correctly (later inserts sit on top). All six get the same Visible formula, so the whole popup appears and disappears as one. To rename a control: double-click its name in the Tree view → paste → Enter.

Click empty canvas BEFORE each insertClick an empty part of the canvas (or select scrMain at the top of the Tree view) before inserting — if a gallery control is selected, the new control lands inside the gallery and replicates into every row. Check after each insert: in the Tree view the new control sits at screen level, not indented under galSystems. If it's nested: delete it, click the screen, insert again.
  1. + Insert (left rail) → search Rectangle → click it → rename it recOverlay. This is the full-screen dimmer.
  2. + Insert → search Rectangle → rename it recConfirmCard. This is the white card.
  3. + Insert → search Text label → rename it lblConfirmTitle.
  4. + Insert → search Text label → rename it lblConfirmMsg.
  5. + Insert → search Button → rename it btnConfirmYes → property dropdown (top-left, next to the fx bar) → TextCtrl+A → paste (quotes included): "Submit".
  6. + Insert → search Button → rename it btnConfirmNo → property dropdown → TextCtrl+A → paste: "Cancel".

Size and place them: select each control, then type these into the Properties pane (right side) → Position → X / Y and Size → Width / Height. Every number is tap-to-copy:

recOverlay
X 0 · Y 0 · Width 1366 · Height 768
recConfirmCard
X 433 · Y 234 · Width 500 · Height 300
lblConfirmTitle
X 457 · Y 254 · Width 452 · Height 40
lblConfirmMsg
X 457 · Y 300 · Width 452 · Height 130
btnConfirmYes
X 693 · Y 460 · Width 216 · Height 48
btnConfirmNo
X 457 · Y 460 · Width 216 · Height 48

Fills and fonts cannot be pasted into the Properties pane's colour picker — they go through the formula bar. Select recOverlay → property dropdown → FillCtrl+A → paste:

recOverlay · Fill

RGBA(0, 0, 0, 0.4)

Select recConfirmCard → property dropdown → FillCtrl+A → paste:

recConfirmCard · Fill

RGBA(255, 255, 255, 1)

Select lblConfirmTitle → right pane → Font weight Bold · Font size 22.

Now the formulas. Select each of the six controls in turn → property dropdown → VisibleCtrl+A → paste (a new control's Visible already contains trueCtrl+A replaces it):

all six popup controls · Visible

gblShowConfirm

Select lblConfirmTitle → property dropdown → TextCtrl+A → paste:

lblConfirmTitle · Text

"Confirm submission"

Select lblConfirmMsg → property dropdown → TextCtrl+A → paste:

lblConfirmMsg · Text

"You are about to submit " & CountRows(colSystems) & " system record(s) to the 3rd party inventory. The IAM team will be notified by email. Do you want to proceed?"

Select btnConfirmNo → property dropdown → OnSelectCtrl+A → paste (a new button's OnSelect already contains false):

btnConfirmNo · OnSelect

Set(gblShowConfirm, false)
Why btnConfirmYes stays empty for nowIts OnSelect is the big submit formula — but that formula calls the notification flow, which doesn't exist yet. We build the flow first (C2–C4), then wire the button (C5). The requirement is exactly this popup: a confirmation message with a Submit and a Cancel button.
Check before moving onIn Preview: fill the form validly and press Submit — the screen dims and the card appears with the correct record count in the message. Cancel closes it and the form is untouched. The Submit button on the card does nothing yet — expected.

Phase 2 — The notification flow

C2Create the flow and its four inputs

The flow is built in Power Automate, in the same environment as your app.

  1. Open https://make.powerautomate.com → check the environment picker (top right) matches the app's environment.
  2. Create (left rail) → Instant cloud flow.
  3. Flow name: paste SystemDeclarationNotify. Choose the trigger PowerApps (V2)Create.
  4. Click the PowerApps (V2) trigger card → Add an inputText. The name goes in the box labeled Input on the new row — Ctrl+A → paste ResponderName.
  5. Add an inputTextResponderEmail.
  6. Add an inputTextSubmissionDate.
  7. Add an inputTextSystemCount.
The input ORDER is the .Run() signatureCreate the four inputs in exactly this order — ResponderName, ResponderEmail, SubmissionDate, SystemCount. When the app calls SystemDeclarationNotify.Run(...) in C5, the arguments are matched by position. All four are Text on purpose: the app pre-formats the date and count, which sidesteps timezone and locale conversion entirely.
Check before moving onThe trigger card lists four inputs, top to bottom: ResponderName, ResponderEmail, SubmissionDate, SystemCount — all of type Text.
C3The email to the IAM team
  1. Under the trigger: New step (in the new designer: + Add an action) → search Send an email (V2) → pick the Office 365 Outlook action.
  2. To: your IAM distribution list — <IAM-DL-EMAIL>.
  3. Subject: paste exactly:

Send an email (V2) · Subject

New Third-Party Application Submission Received

Body: click the </> (code view) toggle in the body field's top-right, then paste this skeleton:

Send an email (V2) · Body (HTML)

<p>A new third-party application declaration has been submitted.</p> <ul> <li><b>Responder:</b> </li> <li><b>Submission date:</b> </li> <li><b>Number of declared systems:</b> </li> </ul> <p><a href="<YOUR-SITE-URL>/Lists/3rd%20party%20inventory/AllItems.aspx">Open the 3rd party inventory list</a></p>
  1. Place the cursor after Responder: (before the closing </li>), then click the lightning-bolt Dynamic content button (or the Add dynamic content link under the field) → insert the ResponderName chip.
  2. Same for Submission date: → insert SubmissionDate.
  3. Same for Number of declared systems: → insert SystemCount.
  4. Fix the link: replace <YOUR-SITE-URL> with your real site URL.
  5. Save the flow.
Optional: CC the responderShow advanced options (in the new designer: Advanced parameters) → CC → insert the ResponderEmail dynamic content chip. The submitter gets a copy of what IAM received — a nice audit courtesy, and it's why the app passes the email at all.
Never rename this flow laterThe app will bind to the name SystemDeclarationNotify. Renaming the flow after C4 breaks SystemDeclarationNotify.Run(...) in the app — the fix is to remove and re-add the flow in the Power Automate pane. Name it right once, now, before it ever touches the app.
Check before moving onThe flow shows: PowerApps (V2) trigger with 4 inputs → Send an email (V2) with To = the IAM DL, the exact subject, and a body containing three dynamic-content chips and the list link. Saved without errors.
C4Connect the flow to the app
  1. Back in Power Apps Studio (your app open for editing).
  2. Left rail → Power Automate (the flow icon) → Add flow.
  3. Pick SystemDeclarationNotify from the list.
First-time connection consentIf a Connections screen appears asking to sign in to Office 365 Outlook, click Sign in / Allow — the flow can't send mail without it.
Check before moving onClick into any formula bar and type SystemDeclarationNotify.Run( — IntelliSense autocompletes the flow name and hints four text arguments. Press Esc without saving the test-typing.

Phase 3 — Wire the submit

C5The submit formula — write, notify, succeed

One paste. It writes every row of colSystems to the list, stamps the audit trail (responder name, email, date/time), calls the flow, and flips to the success screen. It reads only the collection — never control state — which is the whole point of the Part 1 architecture.

Tree view → click btnConfirmYes → property dropdown → OnSelectCtrl+A → paste (the button's OnSelect already contains falseCtrl+A replaces it; pasting without it breaks the formula):

btnConfirmYes · OnSelect

Set(gblSubmitting, true); IfError( ForAll(colSystems As row, Patch('3rd party inventory', Defaults('3rd party inventory'), { 'Application Name': row.AppName, 'Vendor/ Provider Name': row.Vendor, 'Business owner': row.BizOwner, 'Application Purpose': row.Purpose, 'IT Custodian': row.Custodian, 'Admin Accounts Used': row.AdminAccts, 'Permissions / Access levels': row.Permissions, 'Support / Vendor Contact Information': row.SupportContact, 'IAM Role': row.IAMRole, 'Access Link': row.AccessLink, 'Division ': row.Division, Status: row.Status, 'Handover Status': row.Handover, 'Number of Accounts': row.NumAccts, 'Submission Date': Now(), 'Responder Name': User().FullName, 'Responder Email': User().Email } ) ), Set(gblSubmitting, false); Notify("Submission failed — please try again. " & FirstError.Message, NotificationType.Error), SystemDeclarationNotify.Run(User().FullName, User().Email, Text(Now(), "dd mmm yyyy hh:mm"), Text(CountRows(colSystems))); Set(gblShowConfirm, false); Set(gblShowErrors, false); Set(gblSubmitting, false); Set(gblShowSuccess, true) )
How to paste the quirky column namesFour column names have traps baked in by the original list: 'Vendor/ Provider Name' (space after the slash), 'Business owner' (lowercase o), 'Permissions / Access levels' (lowercase l), and 'Division ' — with a trailing space. The paste above already has them exactly right. If you ever retype one, accept IntelliSense's autocomplete — a hand-typed 'Division' (no space) will not resolve.
If a column type differs from the assumptionThe formula above is written for the six assumed types from step A1: IAM Role, Division , Status, Handover Status and Number of Accounts as text, Submission Date as Date and Time — the live list data backs these. If you noted a different type at A1 (or the formula bar flags a line), apply the matching one-line fix:
· Column is Choice → wrap in a record: 'IAM Role': {Value: row.IAMRole} — same treatment for 'Division ', Status, 'Handover Status'.
· Number of Accounts is a Number column → 'Number of Accounts': Value(row.NumAccts).
· Submission Date is Text'Submission Date': Text(Now(), "yyyy-mm-dd hh:mm").
Guessed wrong? The formula bar flags that exact line with "expected record value, found text" (or the reverse). Fix just that line per the rules above.
IfError needs its switch onThis formula relies on Formula-level error management, which you enabled in A2 (Settings → search error). If IfError shows as an unknown function, flip that switch on and re-paste.
Check before moving onNo red in the formula bar (after any column-type fixes from the note above). The App checker (stethoscope icon) reports no formula errors on btnConfirmYes. Don't test-click yet — the success screen it flips to doesn't exist until C6.
C6The success screen and the reset

Same recipe as C1 — five controls, inserted in order, all sharing one Visible flag. Rename each by double-clicking its name in the Tree view → paste → Enter. Geometry goes in the Properties pane → Position / Size, exactly as in C1:

Same trap as C1 — click empty canvas firstBefore every insert, click an empty part of the canvas or select scrMain in the Tree view; then confirm the new control sits at screen level, not indented under galSystems. If it's nested: delete it, click the screen, insert again.
  1. + Insert → search Rectangle → rename recSuccessOverlay — X 0 · Y 0 · Width 1366 · Height 768.
  2. + Insert → search Rectangle → rename recSuccessCard — X 433 · Y 234 · Width 500 · Height 300.
  3. + Insert → search Text label → rename lblSuccessTitle — X 457 · Y 254 · Width 452 · Height 40 · right pane → Font weight Bold · Font size 22.
  4. + Insert → search Text label → rename lblSuccessMsg — X 457 · Y 300 · Width 452 · Height 130.
  5. + Insert → search Button → rename btnNewDeclaration — X 457 · Y 460 · Width 452 · Height 48 → property dropdown → TextCtrl+A → paste: "Start a new declaration".

The two fills go through the formula bar (not the Properties pane). Select recSuccessOverlay → property dropdown → FillCtrl+A → paste:

recSuccessOverlay · Fill

RGBA(0, 0, 0, 0.4)

Select recSuccessCard → property dropdown → FillCtrl+A → paste:

recSuccessCard · Fill

RGBA(255, 255, 255, 1)

Select each of the five controls → property dropdown → VisibleCtrl+A → paste:

all five success controls · Visible

gblShowSuccess

Select lblSuccessTitle → property dropdown → TextCtrl+A → paste:

lblSuccessTitle · Text

"Declaration submitted 🎉"

Select lblSuccessMsg → property dropdown → TextCtrl+A → paste:

lblSuccessMsg · Text

"Your system records have been added to the 3rd party inventory and the IAM team has been notified. You can close the app or start a new declaration."

Select btnNewDeclaration → property dropdown → OnSelectCtrl+A → paste:

btnNewDeclaration · OnSelect

Set(gblNextSeq, 2); ClearCollect(colSystems, {RowID: GUID(), SeqNo: 1, AppName: "", Vendor: "", BizOwner: "", Purpose: "", Custodian: "", AdminAccts: "", Permissions: "", SupportContact: "", IAMRole: "", AccessLink: "", Division: "", Status: "", Handover: "", NumAccts: ""}); Set(gblShowSuccess, false)
Why the reset mints a fresh GUIDRe-seeding colSystems with a brand-new RowID forces the gallery to build brand-new items, so no leftover control state from the previous declaration can bleed into the next one. No Reset() calls needed — the collection is, as always, the single source of truth.
Check before moving onBoth overlays are invisible in the editor's default state (their flags are false). To eyeball the layout: select recSuccessOverlay → property dropdown → VisibleCtrl+A → temporarily type true — repeat for the card, title, message and button (or Ctrl-click all five in the Tree view and set them once). The card reads cleanly and the button spans under the message. Then restore each Visible to gblShowSuccess.

Phase 4 — Prove it, then ship it

C7Check the audit trail with one real record
  1. Preview the app (F5). Fill the single row with obviously-test values (e.g. Application Name ZZ Test App — delete me), pick any dropdown values, a valid URL and a number.
  2. Submit → the popup appears → Submit.
  3. The success screen appears. Press Esc to leave Preview, then open the 3rd party inventory list in the browser.
Check before moving onThe newest list item holds your test values in the right columns, and the audit trio is stamped: Responder Name is your full name, Responder Email is your address, Submission Date is just now. SharePoint's built-in Created By / Created corroborate them. The IAM mailbox (or your CC copy) received the notification. Delete the test item when you're done looking.
C8The end-to-end test script

The full journey, exactly as a business user will drive it. F5 from the top:

  • + Add System twice → three rows. Fill all three completely (distinct app names — Test A, Test B, Test C).
  • Submit → popup says "3 system record(s)" → press Cancel → popup closes, all three rows intact.
  • Submit again → Submit on the popup → success screen appears.
  • In SharePoint: three new items, values in the right columns — spot-check that each row's division landed in the Division column (the trailing-space one) and the number in Number of Accounts.
  • The IAM email arrived: subject exact, responder name right, Number of declared systems: 3, and the link opens the list.
  • Back in the app: Start a new declaration → one blank row, count label says 1, no red anywhere.
  • Clean up the three test items in SharePoint.
Check before moving onEvery bullet passed — including Cancel preserving the rows and the reset landing on exactly one clean blank row.
C9The victory lap — publish and share
  1. FileSavePublishPublish this version.
  2. Back on the app's detail page (or File → Share): Share the app with your users or a security group.
  3. Give the same people Contribute permission on the 3rd party inventory list — the app writes as the signed-in user, so without list rights their submits fail. The clicks: open the list → gear iconList settingsPermissions for this listGrant Permissions → enter the users or group → choose Contribute — or ask your site owner to do it.
  4. Flow check: in Power Automate, open SystemDeclarationNotifyDetails → confirm its connections are healthy and, under Run only users, that app users are allowed to run it (adding the flow to the app normally handles this — verify once).
Blue underlines are fineThe App checker may show blue delegation hints on formulas that count or filter colSystems. Every one of those reads a local collection, not SharePoint — delegation doesn't apply, nothing is truncated, nothing blocks publishing.
DonePublished, shared, permissions in place, notification flowing. Business users can now declare their third-party systems in batches, IAM gets a mail per submission, and every record carries its responder and timestamp. The build is complete. 🏁