The problem
Sending a one-off push notification — a time-sensitive promo, a service alert — meant filing a request with engineering and waiting in their queue. For anything urgent, that delay could mean the moment had passed by the time it went out.
What I actually built
A Canvas App in Power Apps that lets anyone on the marketing team compose a segmented, rich push notification — title, message, image, target segment, deep link — and send it straight from their phone, no desktop required. Power Automate sits behind it, taking those inputs and firing a direct HTTP call into the MoEngage Campaigns API.
The iOS payload black hole
The front end and the automation logic came together fast. What didn't come together fast was cross-platform delivery: Android rendered the push perfectly every time — image, deep link, all of it — while iOS devices stayed completely silent. MoEngage still returned a clean HTTP 200, so from the outside it looked like it worked; Apple devices were just quietly swallowing the payload.
That took an overnight debugging sprint to run down. The real issue was how the aps dictionary — the Apple-specific push block — was getting nested and escaped inside Power Automate's dynamic content. I isolated the android_payload and ios_payload blocks separately, made iOS's mandatory keys (alert strings, badge counts, sound flags) explicit instead of implicit, and wrote clean string-escaping expressions so a marketer typing spaces or special characters into the app could never break the JSON going out. Once notifications started landing, deep links still opened the app to the wrong screen — fixed by aligning the MoEngage payload's key-value pairs with the Param() logic running on the app's OnStart.
The decisions that mattered
I scoped this tightly on purpose. It would have been easy to over-engineer a full campaign-management tool; instead I built exactly enough to solve the actual complaint (the wait time), kept it zero-middleware by using Power Automate's native HTTP actions instead of a third-party layer, and didn't call it done until Android and iOS were both actually confirmed working.