In short: Choose between planning system integration patterns by asking what a source restatement does to each, because that is the case where file, event and API feeds behave completely differently. A finance correction that reverses postings dated across three months will move a forecast with nobody having touched the planning system. Idempotency reduces to two design choices, a stable business key and a deterministic apply rule, so that applying the same message twice leaves the same state. Replay rebuilds planning state from source data as of a chosen point, and it only works if you kept the source data and the effective dates that went with it.
Finance closes the quarter and reverses a batch of misposted shipments dated across three months. On Monday the demand forecast for two dozen items has moved, nobody changed anything in the planning system, and the planner who notices spends a day proving it was not her.
That event, a source system correcting history after the fact, is the most useful thing to design an integration against. Latency gets all the attention in integration design and T4 covers it properly. Restatement gets almost none, and it is the case where the three common patterns behave completely differently. Choose on that basis and you will usually get the latency answer right as a by-product.
The three patterns as contracts
Treat each pattern as a contract with specific guarantees rather than as a technology choice.
Scheduled batch. The source produces a file or a query result on a schedule, in full or as a delta. It guarantees a consistent snapshot at a point in time and it makes no promise about anything that happened since. Ordering within the batch is whatever the extract produced. Completeness is guaranteed for a full extract and depends on the source's change detection for a delta.
Event stream. The source emits a message per change as it happens. It guarantees, at best, that every change is eventually delivered at least once. It does not by itself guarantee ordering across partitions, nor that you have a complete picture at any instant, because completeness is only achievable by replaying from the beginning.
Synchronous API. The consumer asks a question and gets an answer computed against the source's current state. It guarantees freshness at the moment of the call and stores nothing, so there is nothing to restate. It gives no history and it puts load on the source in proportion to how often you ask.
Those three contracts are what you are actually choosing between. The words batch, streaming and API describe implementations; the guarantees are what determine whether a given feed works.
What a restatement does to each
Now run the finance correction through each pattern.
A full batch refresh handles it without any special design. The next extract contains the corrected history, the planning side replaces what it had, and the change propagates. This is the strongest argument for full refresh on any entity where history matters, and it is why full refresh remains the right default for master data and for anything reasonably small.
A delta batch keyed on a change timestamp is where it breaks. The corrected records have a change timestamp of today and a transaction date three months ago. If the delta query filters on transaction date, the correction is missed entirely and the planning history stays wrong indefinitely. If it filters on change timestamp, the correction is picked up, and then it has to be applied to a historical period, which requires the target to support upserts keyed on the business date rather than appending.
An event stream needs the source to emit a compensating event, or a tombstone followed by a corrected record. If the source only emits creations and never corrections, the stream cannot represent a restatement at all. This is worth checking before choosing the pattern, because it is an attribute of the source rather than of the messaging technology.
A synchronous API has nothing to restate because it holds nothing, which is its advantage and its limitation. The question is answered correctly today and there is no history to be wrong.
There is a fifth case worth naming because it is common and nobody plans for it. Some sources restate by deleting and reinserting rather than by updating, which means the correction arrives as a delete followed by a create with a different identifier. A pipeline keyed on the source identifier sees a record vanish and an unrelated record appear, and the demand history acquires a gap and a spike in the same period. Ask the source team how a correction is physically performed before designing the feed, because the answer changes the key you need.
The catch-up arithmetic
The reason to think about this before build rather than after is that the recovery cost scales badly, and the arithmetic makes it concrete.
Take a shipments feed of 12,000 item-location-day rows a day, moving as a delta of roughly 40,000 rows in a typical run because it covers several days of activity and adjustments. Finance restates three months. That is 90 days times 12,000, or 1.08 million rows that have changed.
If the restatement is flagged and the pipeline can process a bulk correction, this is one run of 1.08 million rows, which on any reasonable infrastructure is minutes. If it is not flagged and the delta mechanism only picks up a bounded number of changed rows per run, the correction propagates at 40,000 rows per day and takes 27 days to fully land. During those 27 days the planning history is partially corrected, which produces forecasts computed on a history that is internally inconsistent, and that is worse than either the old state or the new one.
The design conclusion is specific. Any feed carrying history needs a bulk correction path that is separate from the incremental path, and it needs testing. Building it takes a day. Discovering you need it in production takes a month.
Idempotency in concrete terms
Idempotency means applying the same message twice leaves the same state. It sounds like a distributed systems abstraction and in practice it reduces to two design choices.
The first is a natural key that identifies the business fact rather than the message. For a shipment line that is something like source system, document number, line number and, where relevant, the effective date. A message identifier is a poor key because a retransmission gets a new one.
The second is upsert semantics keyed on that natural key, so that a repeat write updates in place. Append-only handling of a feed that can retransmit produces duplicated volume, which in a demand history shows as a demand spike that nobody can explain.
Add a version or sequence number on the source record where the source can supply one, so that an out-of-order delivery does not overwrite a newer value with an older one. Event streams make this necessary rather than optional, because ordering across partitions is not guaranteed.
Replay, and what it requires
Replay means rebuilding the planning state from the source data as of a chosen point, and it is the recovery mechanism when something has gone wrong in a way you cannot unpick.
Three things have to be true for it to work. The landing layer has to be immutable, so that what arrived is still available exactly as it arrived. The processing has to be deterministic, so that reprocessing the same inputs produces the same outputs, which rules out transformations that read the current date or a mutable lookup table. And the target has to be rebuildable, so that you can clear a period and reload it without leaving orphaned records.
Test replay during the build. Take a week of landed data, clear the target for that week, reprocess, and compare against what was there before. Any difference is a determinism problem and you want to find it while somebody still remembers how the transformation works.
Where a synchronous call earns its place
Synchronous APIs get used badly in both directions: for bulk loads where they are slow and fragile, and not at all where they are the only correct answer.
They are the right pattern in three situations. An availability or promise check during a live customer interaction, where the answer has to reflect current commitments and a stale answer creates a broken promise. A scenario or calculation a user triggers and waits for. And an acknowledgement on a write-back, where you need to know that the receiving system accepted the value rather than assuming it did.
They are the wrong pattern for anything that moves a large number of rows, because the call count, the timeout behaviour and the load on the source all scale in ways batch does not. A nightly load implemented as 200,000 individual API calls is a recurring incident waiting for a slow night.
The arithmetic makes the boundary obvious once anyone does it. At 80 milliseconds per call with 20 calls running in parallel, 200,000 calls take 200,000 times 0.08 divided by 20, which is 800 seconds, so about 13 minutes on a good night. Raise the response time to 400 milliseconds under load and the same job takes 67 minutes. A batch extract of the same 200,000 rows is one query. The pattern that degrades gracefully under load is the one to pick for anything on a critical schedule.
The outbound direction needs the same discipline
Most integration design attention goes to data arriving. Data leaving the planning system, parameters and planned orders written to execution systems, gets less and causes more damage when it goes wrong, because the receiving system acts on it.
Three requirements. An acknowledgement, so the planning system knows what landed. An effective date, so the change applies at a controlled boundary rather than mid-run. And a reversal path that has been tested. AA4 covers where the boundary between the two systems belongs and what the write-back contract has to contain.
Where this stops
Choosing on restatement behaviour gives you a defensible answer for most feeds and it does not settle everything. A feed can be restatement-safe and still too slow for the decision it supports, which is where the latency analysis in T4 has to run alongside this one. Do both, per feed, and write the answers in the same table.
The determinism requirement for replay is also harder than it sounds in a real pipeline. Transformations that join to a current master data table are not deterministic, because the master data has changed since. Making them deterministic means versioning the master data as well, which is the conformance work AA5 describes, and it is a larger commitment than it first appears. A reasonable middle position is to make the feeds deterministic and accept that a replay uses current master data, as long as that choice is written down and understood by whoever reads the replayed result.
And no pattern rescues a source that cannot tell you what changed. Where a system has no change timestamps, no event emission and no reliable full extract, the honest answer is a full comparison against the previous snapshot, which is expensive and correct. Say so during design rather than building a delta mechanism that silently misses corrections.
Start by taking your three largest feeds and answering one question for each: if the source restated three months of history tomorrow, would the planning system find out, and how long would the correction take to land. Write the answer next to the feed name. If nobody can answer for a feed, that feed is the one to fix first.