The diagram with a queue in the middle
The review I keep replaying had a whiteboard with three boxes. A Power Apps request form on the left, Oracle Fusion Cloud Payables on the right, and an Azure Service Bus queue in the middle with a Power Automate flow on either side. The lead said the queue meant nothing could be lost and retries were handled. Everyone in the room relaxed, which is the moment I have learned to ask one question. When Fusion rejects an invoice because the cost centre was end-dated last week, whose name is on it by nine the next morning?
Nobody had an answer. A queue solves delivery. It holds a message until a consumer takes it, retries when the consumer fails, and parks the message when retries run out. It has no idea what the message means, so it cannot decide whether a second copy is a duplicate, whether an approval from Friday still applies on Monday, or whether a supplier without a tax registration should be created anyway. Those are business decisions, and the diagram had assigned them to nobody.
Three decisions a queue cannot make
The duplicate usually arrives through a timeout. The outbound flow calls the Fusion REST endpoint to create an invoice, the call runs past the action's timeout, and the action reports failure. Fusion committed the invoice anyway. The retry policy on the action fires, the second call succeeds, and Payables now holds two invoices for one delivery. Payables will often catch a repeated invoice number for the same supplier. It will not catch a repeated supplier, and I have seen one company created four times in the supplier master by a single afternoon of retries.
The stale request is quieter. A requisition is approved on Friday afternoon, the Service Bus connection in the consuming flow breaks over the weekend, and the queue fills up. Someone fixes the connection on Monday and the backlog drains in eleven minutes. Every message in it was valid when it was sent. Two of them charge a project that was closed in Saturday's period-end run. The queue did its job. The meaning of the message changed while it waited, and nothing was placed to notice.
The incomplete record is the one Fusion tells you about. The request form let a supplier through without a tax registration number, the flow passed it along, and the Suppliers API returned a validation error. The message went to the dead-letter queue with a block of JSON attached. The person who can fix it is the requester, or the procurement analyst who owns supplier onboarding. Neither can see a dead-letter queue, and neither was told anything had happened.
A retry has to be safe before it can be automatic
The fix for the duplicate starts with a business key on the message, one the business already uses. For an invoice that is the supplier number plus the invoice number the supplier printed. For a supplier request it is the tax registration number or, where that is missing, the request number the form issued. The field guide to durable business keys covers how to choose one that survives a rebuild. The key goes into the message body and the message ID, so the same request can never be mistaken for a new one.
Then the consumer has to handle the case where it has already done this work. Before creating anything, the consuming flow asks Fusion whether an invoice with that supplier and invoice number already exists, or a supplier with that tax registration. If it does, the flow records the existing Fusion identifier against the request and completes the message without writing anything new. That lookup costs one extra call, and it turns the retry policy from a hazard into something you can leave switched on.
I also ask the team to define a completed message before a failed one. Completed should mean the Fusion record exists and its identifier has been written back to the request in Dataverse. A flow that completes a message on a 202 from an import job that has not finished leaves the request showing success and the ledger showing nothing, and the supplier chasing payment is the first to notice.
Put a name on every parked message
A dead-letter queue is a holding area for the integration team. It shows that a message could not be delivered and the last error. It says nothing about who should act, by when, or what it costs to leave the message there. On the project I described, the oldest dead-letter message was seven weeks old, a supplier request from someone who had since left the company.
What I ask for instead is an exception record in a place the business already works. On the Microsoft side that is usually a Dataverse table with a model-driven view. Each row carries the business key, the step that failed, the reason in words a finance analyst can read, the raw response for the engineer, and an owner. The owner is a role chosen from the failure reason. A closed cost centre goes to the cost centre owner, a validation error goes to the procurement analyst, and a genuine outage goes to the integration on-call. The row also carries a due date and a resolution, so the analyst can say a request was withdrawn and must never be replayed.
The line I press on is that the owner resolves the business issue and the integration team resolves the delivery issue. If the analyst decides the supplier should be created without a registration under a temporary exception, they say so on the record and the integration replays it with that flag. If they decide the request is stale, they close it, and the message is completed without any write to Fusion. Where automation owners lose the thread describes how a run reports success while the work sits with nobody, and the exception record is the direct answer.
What I ask in the review
I ask four questions and stop talking after each one. What does the consuming flow do when Fusion returns an error after it has already written the record? Who can close a request that is no longer valid, and can they do it without a ticket to the integration team? Where does a rejected supplier request appear for the person who submitted it? And how old is the oldest message allowed to be in any queue before someone gets paged?
The answers I want are boring. The flow queries by business key and completes the message. The procurement analyst closes the request from the exception view. The requester sees a status on the form they submitted rather than on a queue they cannot open. The oldest message age is a monitored number with a threshold, and the first thirty minutes of a platform incident names the person who reads it. If the answer to any of these starts with the name of a queue feature, the team has answered a delivery question when I asked an ownership one.
The message between the two flows needs room for an owner, a request identifier, and a replay flag, and a small integration contract that names those fields up front is easier to keep than one widened after the first duplicate invoice.
Start with the oldest parked message
If the design is already live, do not start with a redesign. Pull the oldest message in the dead-letter queue on the Power Automate side, read the Fusion error it carries, and work out who should have decided what happens to it. Write that role next to the failure reason, then do the same for the next ten messages. The roles you end up with are the owner column of your exception table, and the reasons that had no owner are the decisions nobody in the room ever agreed to make.



