Most integrations only know two endings

Ask a team how their integration handles failure and you hear about two outcomes. The run finished, or it threw and somebody got an email. The failures that cost money sit between those answers. Three hundred of four hundred records landed. A record came back rejected for a business reason rather than a technical one. A run finished in four seconds and moved nothing.

That gap exists because error handling gets designed last. The developer builds the path that works, watches what the sandbox throws back, and handles those exceptions. Almost nobody asks at the start which conditions should reach a human in ten minutes, which should wait in a queue until Tuesday, and which should be recorded and never mentioned again.

Those three questions are the design. The retry policy, the exception table, the wording of the alert and the name on the rota follow from them. Answer them on a whiteboard before the first connector is configured.

Three hundred of four hundred is not a successful run

Take a nightly batch of four hundred journal lines going into Oracle Fusion Cloud. Three hundred and seventy two post cleanly. Twenty eight come back rejected because the cost centre on them closed at the end of last quarter. The run ends with a single status value, and whichever value the developer chose is wrong for somebody.

So stop reporting a status and start reporting counts. Every run should record what it attempted, what the target accepted, what it rejected and what it skipped on purpose, in a table that outlives the run and holds the identifiers of everything that did not land. A partial run deserves its own outcome and its own alert threshold.

A connection reset and a closed cost centre both produce an error and land in the same retry loop. One clears itself in thirty seconds. The other fails identically every night for a month while nobody in finance hears about it. Classify the error where the target answers, because that is the last point where you know which kind you have.

Decide what can be retried before you write the retry

Retry the throttle response, the socket timeout on a read and the gateway error. Never blind retry an operation that might already have taken effect. The worst version is a create call that times out after the receiving system committed the record. The sender records a failure, tries again, and the business now has two purchase orders where it asked for one.

Safe repetition has to be built into the operation rather than bolted onto the retry. The sender derives a key from something durable in the source data, sends it with every attempt, and the receiver refuses the duplicate or returns the record it already created. A durable business key beats a row id, because the source can reproduce it after a reload.

Where an operation cannot be made repeatable, the retry policy should be short and end with a person. Two attempts with a backoff, then park the message with everything somebody needs to decide what happened. I have seen more damage from a patient retry loop that duplicated payments than from one that gave up and asked.

A rejected record needs an address, not a log line

Ask where a rejected record goes and the answer is usually the run log, or a shared mailbox three people filter into a folder they stopped opening in March. The person who can fix a closed cost centre or a missing bank detail sits in finance and has no access to the integration platform.

Give rejections a home the business can open. One row per rejected record with the source identifier, the reason in words a non-developer can act on, the payload as sent and a status somebody closes. On Workday Extend I have used a custom object, and on Oracle a staging table with a screen over it. A rejection on a small integration contract points at one field, not forty.

Then put a name on the queue. A queue with no named team is a queue nobody works, which is why I keep saying the queue is not the owner. Write the team and the rota beside the exception table in the design document, and review the age of the oldest open rejection where you review incidents.

The feed that moved nothing for nine days

A manufacturer I worked with last year moved contingent worker records from Workday into Oracle Fusion Cloud every night. A Workday Extend orchestration read a custom report and posted each worker into Fusion so they could raise requisitions on site. Then somebody edited the report to help a recruiter, added a prompt default scoped to one supervisory organisation, and saved it.

The report returned zero rows for everyone else. The orchestration fetched zero rows, looped zero times, posted nothing and wrote a completed status with no errors. Every dashboard stayed green for nine working days, and the weekly review showed a perfect success rate, because by every measure the build reported, it had succeeded.

It came out when a site lead rang the service desk about three contractors who could not raise a requisition, one of them waiting a fortnight. Forty one workers were missing by then, and several existed twice because supervisors had keyed them in by hand. The integration never failed. It carried nothing, and nothing in the design had an opinion about that.

Zero rows should page somebody too

Any scheduled integration that moves business records should carry an expected volume, even a crude one. A feed that normally moves two hundred rows a night and moves zero has told you something, and the run outcome should say so instead of reporting success. A lower bound of one beats nothing, and a rolling average with a floor under it is better.

The stronger check sits on the receiving side and asks when the last record arrived. If Fusion has not seen a worker from the Workday feed across two working days, somebody should be woken. That catches failures the sender cannot see, including the feed disabled during a release weekend and never switched back on. The same gap sits behind the quiet failure in an employee process.

Both checks are cheap, and neither existed on that feed. They get skipped because nobody asks what the integration should have done, only whether it did what it tried to do.

An alert with no name on it teaches people to ignore alerts

The last piece is what the alert says. Most report that job forty seven failed at 02:14 with status 500, which sends the reader at two in the morning into three tools before they know whether anything is at risk. An alert should name the business process, what did not happen, how many records are affected, whether a rerun is safe and where the parked records sit.

Safe to rerun is the line people leave out, and it decides whether the person on call acts now or waits for the owner at nine. If the operation carries a key that makes repetition harmless, say so in the alert text and in the runbook, next to whatever else the first thirty minutes of an incident asks for.

Then name a recipient. An alert routed to a distribution list is an alert nobody owns, and after the third false page from a feed that recovers itself, people stop reading the channel. Give every alert one accountable team and a rota, and take any alert that fires twice a month with nobody acting on it to a fix or to the bin.

Open the last thirty days of run history for your busiest interface and count the runs that reported success while moving fewer records than usual. Then ask the receiving team when they last saw something arrive. If the run records cannot answer either question, your error handling has never been tested by anything except an error.