Don't call external systems directly from power platform

Calling external systems straight from Power Automate or plugins breaks under load and fails silently. The Service Bus outbound pattern that fixes it. Part 2.

Power Platform · Messaging · Service Bus · Integration · Dataverse

A cloud at the centre of a network of connected systems
Part 2 of 2 Integration Architecture Power Platform

Don't let Power Platform call external systems directly — here's what breaks when you do.

Part 1 covered what happens when external systems push data in without protection. Part 2 is the other direction — what breaks when Power Platform pushes data out without a buffer, and the outbound pattern that fixes it.

Missed Part 1?
We covered the inbound journey — why external systems shouldn't write directly to Dataverse, and the Service Bus pattern that protects you.
Read Part 1 →

Part 1 dealt with the inbound problem — external systems hammering directly at Dataverse with no buffer, no retry, no deduplication. The fix was a Service Bus queue sitting in front of everything, absorbing the chaos before it reached your CRM.

The outbound problem looks different but the failure modes are just as painful. A record changes in Dataverse. A Power Automate flow fires. It makes a direct HTTP call to an external system — a warehouse, a finance platform, a third-party API. And somewhere in that chain, something goes wrong.

The question is whether you find out before or after the data diverges.


Five outbound failure modes — and why they're harder to spot.

01
No guaranteed delivery — the call goes out but does it land?
Power Automate fires the HTTP call and moves on. If the external API returns a timeout or a 500, the flow may retry — or it may not, depending on how it was built. Either way, there's no durable guarantee the message was received and processed. The external system and Dataverse are now out of sync and neither knows it.
02
Retry storms — you make a struggling API worse.
When an external system is slow or rate-limiting, the worst thing you can do is hammer it with immediate retries. Power Automate's built-in retry logic has limited backoff control. Under load you can end up amplifying the problem — more requests hitting a system that's already struggling, making recovery take longer.
03
Partial success — the record updated but the downstream system didn't.
A flow updates a Dataverse record and then calls an external API. The Dataverse write succeeds. The external call fails. The flow errors. Now your CRM says one thing and your downstream system says another — with no automatic way to reconcile them. This is the silent divergence problem and it compounds over time.
04
Ordering — the second message arrives before the first.
Two updates to the same record happen in quick succession. Two flows fire. The second HTTP call reaches the external system before the first. The external system now has stale data — and has no way of knowing. Direct calls have no ordering guarantee. Without a queue, you cannot control the sequence.
05
No dead-letter visibility — failed messages disappear.
When a direct outbound call fails permanently, where does it go? Into a flow run history that gets purged after 28 days. There's no persistent store of failed messages, no way to replay them, no audit trail. If nobody is watching the flow dashboard at the right moment, the failure is invisible.

Outbound via Azure Service Bus.

Same philosophy as the inbound pattern — introduce a durable queue between the trigger and the destination. Dataverse or Power Automate publishes a message. A processor picks it off the queue and handles delivery to the external system with retry, ordering, and dead-letter handling built in.

Direct outbound call — fragile
Dataverse
Record created / updated
Triggers directly
Power Automate Flow
No buffer · Limited retry control
No ordering
Retry storm
No dead-letter
Partial success
Finance System
External API
Warehouse
External system
Third-party CRM
External API
Legacy Platform
Old infrastructure
✕ Silent divergence ✕ Lost messages ✕ Out-of-order writes ✕ Retry storms ✕ No audit trail
Resilient outbound pattern — production grade
Dataverse
Record change / plugin
Power Automate
Scheduled / triggered flow
publish message
Azure Service Bus
Queue / Topic
Guaranteed delivery Message ordering Configurable backoff Dead-letter queue Duplicate detection
peek-lock / consume
Message Processor
Azure Function / Power Automate
Validate & transform outbound payload
Call external API with controlled retry
Confirm delivery before completing lock
Dead-letter on permanent failure
controlled, ordered delivery
Finance System
External API
Warehouse
External system
Third-party CRM
External API
Dead-Letter Store
Failed messages
Guaranteed delivery Ordered writes Controlled retry Dead-letter visibility Duplicate detection Full audit trail

What each layer actually does for you.

Azure Service Bus — the outbound buffer.
Decouples the trigger from the delivery
  • Dataverse or Power Automate publishes and moves on. They don't need to know whether the external system is available, slow, or rate-limiting.
  • Messages are durable and ordered. Even under high volume, the sequence is preserved — the external system always receives events in the right order.
  • Duplicate detection catches the same message being published twice — common when flows re-trigger on retry.
  • Configurable backoff means a struggling external API gets time to recover rather than being hammered into a longer outage.
The processor — controlled delivery.
Nothing leaves until delivery is confirmed
  • The processor holds a peek-lock on the message. It only completes the lock — removing the message from the queue — once the external API confirms receipt. If the call fails, the message stays on the queue and retries.
  • This is what gives you guaranteed delivery. The message cannot be lost between Dataverse updating and the external system receiving it.
  • Transformation happens here too — the outbound payload format is often different from what Dataverse stores. The processor bridges that gap cleanly.
  • Permanent failures are moved to the dead-letter queue automatically — visible, replayable, auditable.
External systems — receive clean, ordered data.
Protected from duplicate writes and out-of-sequence updates
  • Because the queue enforces ordering and the processor confirms delivery before releasing the lock, the external system always receives events in sequence — no stale overwrites.
  • Rate-limited APIs are protected by the processor's controlled retry — it backs off intelligently rather than retrying immediately.
  • Any message the external system permanently rejects ends up in the dead-letter store with full context — not silently discarded into a flow run history that expires in 28 days.

When does this pattern make sense?

Same rule as the inbound pattern — not every outbound call needs this. A simple flow sending a notification email doesn't need a Service Bus in the middle. But reach for this pattern whenever the stakes are higher.

01External system you don't control or can't rely on
02Transactional data where both sides must agree
03High-volume update scenarios
04Rate-limited or slow third-party APIs
05Any integration where ordering matters
06Any environment where silent data loss is unacceptable

The pattern is the same. The direction is different.

Inbound or outbound, the principle doesn't change — never let two systems talk directly to each other when the data matters. Put something durable in the middle.

The Service Bus queue is that something. It absorbs the chaos, enforces the order, confirms the delivery, and gives you somewhere to look when things go wrong. The alternative is a flow run history, a 28-day expiry, and a support call you weren't expecting.

Together, these two patterns — inbound and outbound — give you a complete integration architecture that can be reasoned about, monitored, and trusted under load.

This was Part 2 of 2

"The inbound journey — why external systems shouldn't write directly to Dataverse, and the pattern that protects you."

Start with Part 1 if you haven't already.

← Read Part 1