@cfxlabsinc/b2b-services
    Preparing search index...
    WireEmailReconcileOutcome:
        | {
            bankTransactionId: string;
            ffcMemo: string
            | null;
            status: "RECONCILED";
            via: "posted" | "matched" | "enriched";
        }
        | { reason: string; status: "UNRESOLVED" }
        | { reason: string; status: "SKIPPED" }

    The single entry point the lambda calls once an email is stored: attach this wire to a bank transaction, whatever that takes for the bank it came from.

    Two banks, two shapes, one result:

    • burling has no transaction feed at all, so the notification IS the record — postWireEmail resolves the account from the quoted suffix and creates the row (or finds the one an operator hand-keyed).
    • fib has rows written by a transaction feed, and they arrive carrying nothing that identifies the wire. enrichWireEmail finds the row by the sequence number in its statement description; when the feed has not delivered it yet, the wire stays UNRESOLVED until the feed's row arrives.

    Both end the same way: a bank transaction whose methodData holds the IMAD/OMAD, the sender, and — the part that matters downstream — the FFC memo. Returning one outcome type is what lets the caller run one deposit path and lets that path stay ignorant of which bank a wire came from. The old subsystem could not do that: it only ever handled fib, through a Hookdeck webhook that no longer exists.

    Type Declaration

    • {
          bankTransactionId: string;
          ffcMemo: string | null;
          status: "RECONCILED";
          via: "posted" | "matched" | "enriched";
      }
      • bankTransactionId: string
      • ffcMemo: string | null

        The customer memo this wire quoted, when it quoted one.

      • status: "RECONCILED"
      • via: "posted" | "matched" | "enriched"

        How the bank transaction was obtained, for the log line.

    • { reason: string; status: "UNRESOLVED" }
    • { reason: string; status: "SKIPPED" }