HV Tech Stack Chapter 03 · Flows

Flows.

Power Automate moves every byte between systems. Each flow has a trigger, a target, and a loop guard.

Contents

In this chapter

  1. i.Patterns and the loop guardI
  2. ii.Upstream and downstream flowsII
  3. iii.Bridges: MaintainX, Constant Contact, Teams, ExcelIII
  4. iv.Operating rulesIV

I.

Part One

Patterns and the loop guard

One naming rule. One request shape. One rule that stops every loop.

Flow naming convention

Every flow name reads HV-<source>-<verb>-<target>. Examples: HV-WP-Submit-VarianceRequest, HV-DV-Sync-ResidentRoster, HV-MX-Mirror-WorkOrders.

Every flow logs to hv_sync_log with flow_name, direction, payload_hash, initiated_by, and status. The log is the audit trail and the loop-break mechanism.

The Request Pattern

Every upstream submission uses the same shape.

  1. Resident submits a form in WordPress.
  2. WordPress validates locally, assigns a form_submission CPT, and fires a webhook.
  3. Power Automate receives the payload, hashes it, and logs Submitted in hv_sync_log.
  4. Power Automate creates the matching Dataverse row.
  5. Power Automate either auto-approves or posts a Teams Adaptive Card to the responsible staff channel.
  6. Staff approves in Teams. Dataverse updates status.
  7. A downstream sync flow sees the change and pushes it back to WordPress for the resident dashboard.
  8. The downstream flow checks initiated_by. If the Power Automate service account made the update, the flow skips the webhook-back step. No loop.

Loop prevention

Every Dataverse update carries Modified By. The service-account identity is known. Power Automate applies this rule on every DV-to-WP flow:

if trigger.modified_by == @{variables('svc_account_id')}
    -> log Skipped, exit
else
    -> proceed with WP sync

Every WordPress-to-Dataverse flow writes initiated_by = 'resident' or initiated_by = 'staff-ui'. DV-to-WP flows only fire when initiated_by is one of those values.

II.

Part Two

Upstream and downstream flows

Eight flows run upstream. Five run downstream. Each has one trigger, one action, one loop guard.

Upstream: WordPress to Dataverse

Flow Trigger Action Notify
HV-WP-Submit-VarianceRequest Webhook, form_type = variance_request Create hv_variance_requests, status Submitted Adaptive Card to Carol
HV-WP-Submit-EVChargerRequest Webhook, ev_charger Create hv_ev_charger_requests Card to Nate
HV-WP-Submit-OccupancySurvey Webhook, occupancy_survey Create hv_occupancy_surveys None
HV-WP-Submit-AlternateAddress Webhook, snowbird_address Create hv_alternate_addresses None — staff reviews in batch
HV-WP-Submit-EmergencyContact Webhook, emergency_contact Upsert hv_emergency_contacts None
HV-WP-Submit-ContactChange Webhook, contact_change Create hv_contact_change_requests Auto-approve for secondary email; else staff card
HV-WP-Submit-NewsletterOptIn Webhook, newsletter_optin Create hv_email_optins, Requested Card to Sarah
HV-WP-Submit-AccessRequest Public form, access_request Create hv_access_requests Card to office triage

Downstream: Dataverse to WordPress

Flow Trigger Action Cadence
HV-DV-Sync-ResidentRoster Row change on hv_residents Upsert wp_users; map associated_units Real-time; batched 02:00 for bulk moves
HV-DV-Sync-VarianceStatus Row change on hv_variance_requests Update WP variance dashboard Real-time
HV-DV-Sync-EVChargerStatus Row change on hv_ev_charger_requests Update WP dashboard entry Real-time
HV-DV-Sync-ApprovedVehicles Row change on hv_vehicles Update resident dashboard vehicle list Nightly 02:00
HV-DV-Sync-UnitAssignments Row change on hv_resident_units Rebuild associated_units ACF repeater Real-time

Every downstream flow applies the loop-break rule. Service-account writes skip the WP hop and log Skipped.

III.

Part Three

Bridges

MaintainX, Constant Contact, Teams, and Excel each get their own lane.

MaintainX bridge

MaintainX stays master for work orders. Residents read their own orders through a WordPress REST proxy that calls MaintainX server-side. Dataverse mirrors the data for staff reporting.

HV-MX-Mirror-WorkOrders

WordPress REST proxy

Constant Contact bridge

HV-DV-Publish-NewsletterOptIn

Teams Adaptive Cards

The free-tier staff (Carol, Sarah, others) never log into Power Apps. They approve from Teams.

Card flow Target On approve
HV-DV-Card-VarianceCarol, Governance channelWrite decision back to Dataverse
HV-DV-Card-EVChargerNate, Infrastructure channelWrite back
HV-DV-Card-NewsletterOptInSarahFire HV-DV-Publish-NewsletterOptIn
HV-DV-Card-AccessRequestOffice triageCreate hv_residents; provision WP user downstream

The service-account Adaptive Card pattern is safe for occasional approvals. If volume grows into dozens per day across many staff, Microsoft may flag the pattern.

Excel-on-SharePoint reporting

Tier-3 staff get Excel files on SharePoint. No Power Apps access. No Dataverse licenses.

Flow Cadence Writes to Recipients
HV-DV-Export-VarianceLogNightly 01:30variance-log.xlsxOps staff
HV-DV-Export-WorkOrderSummaryNightly 01:45work-orders-summary.xlsxTrades supervisors
HV-DV-Export-ResidentRosterWeekly Sunday 02:00resident-roster.xlsxOffice staff, library volunteers

The Excel connector is the right tool at this scale. Dataverse Export to Data Lake is overkill.

IV.

Part Four

Operating rules

Webhook vs batch. Error handling. Rate limits. Security.

Webhook vs scheduled sync

Use real-time webhooks when a human waits for the result. Use scheduled sync when batch is fine.

SignalPatternWhy
Variance submittedReal-time webhookCarol needs to see it immediately
Access requestReal-time webhookOffice triages same day
Occupancy surveyReal-time webhookIndividual rows, trivial volume
Nightly roster reconciliationScheduled 02:00Catches name-spelling or unit-transfer drift
Excel reportsScheduledNo live consumer
Bulk move-in seasonScheduled 02:00Avoids spamming webhooks

Error handling

Every flow uses the same pattern:

  1. Wrap the main step in a scope with Configure run after set to has failed.
  2. On failure, write hv_sync_log.status = Failed with the error.
  3. Post a card to Nate's Teams channel with the payload and the error message.
  4. Exit without partial writes.

For idempotency, every flow uses payload_hash to detect replays. If the hash matches a recent log row (within 60 seconds), skip.

Rate limits and batching

Security