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:

FieldValue in this scenario
sourceEventTypepayment.settled
expectedObjectTypeledger-entry
maxDelayMs900000 (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 events

The 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 wrongExample by industryRule type
A retry created the record twicePayments: a webhook redelivery charges a card twiceNo duplicates
Fewer records than events, with no shared identifier to match themLogistics: 1,000 orders received, 940 shipments createdCount match
The amounts recorded differ from the amounts receivedMarketplaces: payouts recorded total more than payouts receivedAmount match
A record reached a state it should not have reached yetOrder management: an order marked shipped before it was paidAllowed sequence
Records still arrive, but later than they used toFraud checks that land after the order has already shippedLateness 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 window

Reporting 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

  1. Confirm the triggering operation completed and belongs to the expected integration.
  2. Check the correlation identifier and the evaluation window.
  3. Look for a late, duplicate, or differently correlated result before declaring it absent.
  4. 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.