Detect silent business failures
A successful API response proves that transport completed. It does not prove that the record was created, the ledger updated, or the entitlement granted. Silent business failures live in that gap, and transport monitoring is structurally blind to them: every status code is green.
The scenario
Your payments service receives payment.settled webhooks and acknowledges each with a 202. Downstream, a worker is supposed to write a ledger entry for every settlement. A deploy introduces a filter bug: settlements from one provider region stop producing ledger entries. Nothing fails: the webhooks are still acknowledged, the worker still runs, the dashboards stay green. Finance notices three weeks later during reconciliation.
Transport success is not business success
Both facts belong in the record. The successful response explains why conventional monitoring stayed quiet; the missing business result explains why it was still an incident. Seamward keeps both by treating them as two observable events that should correlate: the settlement that was accepted, and the ledger entry that should follow it.
Define the outcome
Two things wire this up. First, your handlers report business outcomes and correlation, which they can do because they are where the business context lives:
Code example
// The settlement handler: reports what should happen next.return { statusCode: 202, eventType: "payment.settled", outcome: { accepted: true }, correlation: { sourceEventId: event.settlementId },};
// The ledger worker's observation: reports that it happened.return { statusCode: 200, outcome: { accepted: true, businessObjectType: "ledger-entry", businessObjectId: entry.settlementId, },};Both identifiers are keyed-hashed inside your process; matching happens on the hashes, and Seamward never sees a settlement id.
Second, an expected-outcome rule states the promise in three values:
| Field | Value in this scenario |
|---|---|
sourceEventType | payment.settled |
expectedObjectType | ledger-entry |
maxDelayMs | 900000 (15 minutes: the workflow's real latency plus slack) |
What Seamward shows you
Nothing is "missing" until the delay window has fully elapsed, so slow-but-fine results never page anyone. Once eligible settlements have no matching ledger entry, an incident opens:
Code example
3 ledger-entry outcomes missing after successful payment.settled eventsThe incident lists each affected source observation with its correlation evidence and updates in place as the count changes. When the bug is fixed and every source is satisfied again, Seamward starts a verification window of at least five minutes, or the rule's longer delay. Sustained recovery resolves the incident with a recorded recovered reason and timestamp; new missing evidence resets verification. Evaluation runs on new traffic and on a sweep every minute, so a window that expires quietly is still caught.
If the repair was delivered in a pull request, Seamward first waits for an observation whose deployed commitSha matches the merged commit. It then requires at least one complete correlated source and outcome pair after that deployment boundary. Historical failures stay attached to the incident while the post-deployment evidence proves recovery.
Other ways a result can be wrong
A record that never appears is one silent failure. The same settlement flow can also go wrong while every record does appear, and each case has its own rule type:
| What went wrong | Example by industry | Rule type |
|---|---|---|
| A retry created the record twice | Payments: a webhook redelivery charges a card twice | No duplicates |
| Fewer records than events, with no shared identifier to match them | Logistics: 1,000 orders received, 940 shipments created | Count match |
| The amounts recorded differ from the amounts received | Marketplaces: payouts recorded total more than payouts received | Amount match |
| A record reached a state it should not have reached yet | Order management: an order marked shipped before it was paid | Allowed sequence |
| Records still arrive, but later than they used to | Fraud checks that land after the order has already shipped | Lateness band |
For the settlement flow above, three rules on the same event cover the common cases: an expected outcome for a missing ledger entry, no duplicates for a double entry, and an amount match once both handlers report what they received and what they wrote:
Code example
// The settlement handler: what was received.outcome: { accepted: true, amount: { value: event.amountMinor, unit: "GBP_minor" },},
// The ledger worker: what was written.outcome: { accepted: true, businessObjectType: "ledger-entry", businessObjectId: entry.settlementId, amount: { value: entry.amountMinor, unit: "GBP_minor" },},Code example
Recorded ledger-entry amounts are 4,210 GBP_minor above the payment.settled amounts received in the windowReporting an amount or a state needs a collector that sends them: @seamward/collector 0.1.0-alpha.29 or later, or the PHP and Laravel collectors at 0.1.0-alpha.2 or later.
Investigate a missing outcome
- Confirm the triggering operation completed and belongs to the expected integration.
- Check the correlation identifier and the evaluation window.
- Look for a late, duplicate, or differently correlated result before declaring it absent.
- Record the affected records and the remediation decision using only supported evidence.
Impact analysis turns the affected sources into a defensible affected set for reconciliation.
Operating boundaries
- Outcome rules evaluate observable facts; they never infer an unrecorded business result.
- Hashed correlation supports matching without exposing the original identifier.
- Unknown or incomplete correlation stays unknown rather than being counted as confirmed impact.
- Seamward does not retry the business operation or modify your system of record.
Related: Reconciliation rules for rule mechanics, and Observe webhooks for the outcome contract.
