Reconciliation rules
A 202 Accepted proves delivery, not success. A reconciliation rule states what should be true in your system after an accepted event, so Seamward can detect the failures that transport monitoring is structurally blind to: a record that never appeared, appeared twice, does not add up, moved in an impossible order, or arrived late.
The first rule type, the expected outcome, asks whether a record appeared: "every accepted payment.settled event produces a ledger-entry within 15 minutes". Five more types ask the other questions. The list is closed: a rule is always one of these six, never custom logic.
Anatomy of a rule
Every rule has the same three names, a type, and the settings that type needs:
| Field | Meaning | Constraints |
|---|---|---|
sourceEventType | The event type of the triggering observation | Stable, low-cardinality name like payment.settled |
expectedObjectType | The businessObjectType of the record the rule is about | Domain type like ledger-entry |
maxDelayMs | How long a record may reasonably take; also how long the rule waits to judge | 1 second to 24 hours |
kind | The rule type; fixed once the rule exists | One of the six below |
config | The settings for that type | Validated for the type; defaults are filled |
Create rules from the integration's Rules tab: choose a rule type, then fill in the settings it asks for. Disable a rule to stop it participating in evaluation without losing its history. The same event and record type can carry one rule of each type.
The six rule types
| Rule type | The question it asks | Incident it raises |
|---|---|---|
| Expected outcome | Does each accepted event lead to a record within the delay? | missing-business-outcome |
| No duplicates | Does any event lead to more than one record? | outcome-duplicated |
| Count match | Over a window, do events and records agree in number? | count-mismatch |
| Amount match | Over a window, do the amounts you received and recorded agree? | amount-mismatch |
| Allowed sequence | Do a record's states follow the order you declared? | illegal-transition |
| Lateness band | Do records arrive within their usual delay, well before the deadline? | outcome-late |
No duplicates
A retry without an idempotency key can create the same record twice while every delivery looks healthy. This rule groups records by the source event that led to them, using the keyed hashes your collector already sends, and raises a finding when one event has more than one distinct record.
- Needs:
correlation.sourceEventIdand the outcome'sbusinessObjectIdon the observation that reports the record. - Settings: how far back to look (default 24 hours, up to 7 days).
- Example finding: "30 payout.paid events each produced more than one payout record (30 extra records)".
The same record reported twice is one record. Only distinct record identifiers count.
Count match
Some records cannot be tied to one event, such as shipments created in batches from orders. This rule counts accepted events and reported records over the same window and compares the totals.
- Needs: the event observations and the record observations; no shared identifier.
- Settings: the window, a tolerance as a percentage of the event count (default 0), and a minimum number of events below which the rule raises nothing (default 20).
- Example finding: "1,000 order.created events but 940 shipment records in the window (60 fewer)".
The window ends one maxDelayMs before now, so records still on their way are not counted as absent. Events near the edge of a window can still fall on either side of it, so set a small tolerance on busy connections.
Amount match
This rule sums the amount your application reports on the event side and on the record side, in one unit, and compares the totals.
- Needs:
outcome.amounton both observations: what you received on the event, and what you wrote on the operation that records it. If one observation reports both sides, the totals are always equal and the rule can never raise anything. - Settings: the unit (required; only amounts in that unit are summed), the window, a tolerance as a percentage of the received total, and a minimum number of events.
- Example finding: "Recorded payout amounts are 4,210 GBP above the payout.paid amounts received in the window".
A retried event or a re-reported record counts once, at its latest amount. A difference between totals does not say how many records are wrong, so an amount incident shows the totals and no affected-record count.
Allowed sequence
This rule reads the states your application reports for each record, oldest first, and checks them against the order you declare, such as created, paid, shipped.
- Needs:
outcome.stateand the outcome'sbusinessObjectIdon each observation that reports the record. - Settings: the states in order (2 to 12), and the window.
- A finding is: a step backward (
paidtocreated), a skipped step (createdtoshipped), or a state you did not declare. - Not a finding: repeating a state, or a record first seen part-way along.
- Example finding: "12 order records moved between states outside the declared order, most often paid to created".
States are read from every accepted observation that reports the record type. The rule's source event is a label here and does not filter anything.
Lateness band
An expected-outcome rule raises nothing until a record is past its deadline. This rule warns earlier: when records are still arriving, but later than usual.
- Needs: the same reporting as an expected outcome.
- Settings: the usual delay (required, and shorter than the deadline in
maxDelayMs), the share of late records that raises a finding (default 20%), the window, and a minimum number of events. - Example finding: "45 of 120 fraud_check records arrived more than 30 seconds after order.created (37.5%)".
A record that never arrives is not late; it is missing, and an expected-outcome rule on the same pair reports it.
What the five window rules share
Reporting amounts and states
Amount match and allowed sequence need your collector to report
outcome.amount and outcome.state. Those fields ship in the next collector
releases; you can send them today through the REST API on envelope 0.6. The
other rule types work with what collectors already send.
- They evaluate when new observations arrive and on the same one-minute sweep as expected outcomes.
- One rule has at most one open incident. A later evaluation updates its totals instead of opening another.
- When the rule finds nothing, the incident enters a verification period of
maxDelayMsor five minutes, whichever is longer, and resolves with a recordedrecoveredreason only if the rule is still clean when it ends. A new finding during that period reopens it. - After you resolve an incident yourself, the rule looks only at observations after that moment, so it does not immediately raise the same evidence again.
- They read counts, totals, state names and keyed hashes. No payload is read, and the incident shows only those values.
Independent mode definitions
You define a rule once. Creating it in one mode gives the other mode the same rule switched off, and you switch it on in each mode separately. Evidence and history stay independent.
An edit follows to the other mode only while the rule is off there. A rule that is on keeps the definition it was switched on with, so an edit made in Test can never change what Live checks; edit it in Live when you want Live to change. Deleting a rule removes the other mode's copy only when that copy is off and has no incident history.
For automation, list and manage connection rules with a workspace API key. Reads return the connection revision; writes require the exact revision you reviewed and a retained idempotency key. Explicitly enabling a rule affects only that mode connection. A rule with incident history can be disabled, but cannot be detached while those references remain. Retained operation receipts prove a mutation committed; they do not prove its current configuration.
How an expected outcome is evaluated
Seamward evaluates rules against the observations your collector already sends; there is nothing extra to instrument beyond the outcome contract.
- An observation qualifies as a source when its
eventTypematches the rule, its outcome was accepted, its status code was below 400, and it carries asourceEventIdHash. - The rule's delay window must fully elapse first. Nothing is ever "missing" before
maxDelayMshas passed, so slow-but-fine results do not page anyone. - A source is satisfied when a later observation carries the expected
businessObjectTypeand a matching identifier hash. Matching happens entirely on keyed hashes; Seamward never sees the underlying identifier. - Unsatisfied sources become a
missing-business-outcomeincident: "3 ledger-entry outcomes missing after successful payment.settled events". When every source is satisfied again, the incident enters a verification window of at least five minutes, or the rule's longer delay. A sustained healthy window auto-resolves with a recordedrecoveredreason and timestamp. New missing evidence resets verification.
Evaluation runs when new observations arrive and on a sweep every minute, so a rule whose window expires quietly (no new traffic) is still caught within a minute.
When an approved pull-request repair is involved, the merge event alone is not recovery evidence. The collector reports deployment metadata automatically. Seamward identifies the first observation whose commitSha matches the merged pull request, then evaluates only source and outcome pairs received from that deployment boundary onwards. At least one complete post-deployment pair is required before the sustained verification window begins.
Writing rules that work
- Name the outcome in the language of the team that owns the workflow; the incident summary is built from these names.
- One rule per distinct business result. "Payment settles" and "receipt email sends" are two rules, not one.
- One rule type per question. To check that a payout is recorded, recorded once, and for the right amount, add three rules on the same event and record type.
- Report an amount in the smallest unit you account in, and name the unit. A rule sums only its own unit, so
GBPandGBP_minornever mix. - Set
maxDelayMsfrom the workflow's real latency plus slack, not from hope. A window that is too tight creates noise; too loose delays detection. - The triggering handler must report
correlation.sourceEventIdand the fulfilling handler must report the matching identifier on its outcome, or nothing can link them.
Boundaries
- Rules evaluate observable facts; they never infer an unrecorded result.
- An unknown correlation stays unknown; it is never counted as confirmed impact.
- Seamward does not retry the business operation or touch your system of record.
Evidence limits
The affected-record count includes every missing source retained for the rule.
The incident's inline evidence is a deterministic sample of at most 1,000 records
and 8 MiB of compact serialized JSON, including repeated identifiers. totalMissing, sampledRecords and truncated describe the sample.
All missing sources remain linked to the incident, including records outside the
inline sample. Recovery evaluates every eligible source, so sampling does not
change whether an outcome is missing or a verification window succeeds. Evidence
pagination and search cover every currently missing linked record. The response's
snapshot object describes the inline sample; its paginated total includes all
matching current records. Historical links remain after recovery but do not appear
as currently missing. Older incidents keep their original timeline until their
next reconciliation pass.
Replay datasets and repair generation require the complete current evidence set
within the MVP limit of 1,000 fixtures. More than 1,000 returns 409 with
replay_evidence_capacity_exceeded, limit and total. The request creates no
partial dataset or proposal. Existing dataset selection does not bypass this check.
See replay limits.
Next steps
- Detect silent business failures: the full workflow this enables.
- Incidents: what a missing outcome becomes.
