Faith Forge LabsNorway / Remote development

Reject a duplicate recipient field before creating the invoice draft

A request can look like ordinary JSON and still give two receivers different answers about its recipient. If the same object contains a member name twice, one parser may keep the last value while another behaves differently. An integration that immediately converts that input into a normal object can lose the evidence that there were two values to begin with.

For a Norway-connected invoice workflow, the issue is not solved by checking that both organisations exist. The sending application must express one authorised billing decision, and the receiving bridge must handle ambiguous input before creating a document. This fictional inspection follows that boundary without issuing an invoice or moving money.

Keep the raw request as evidence

A supplier's isolated test bridge receives request Q-61 for an unissued invoice draft. The approved test instruction identifies recipient account R-14. The producer combines a current billing selection with an older default and emits both using the same member name:

{
  "request_reference": "Q-61",
  "recipient_reference": "R-14",
  "recipient_reference": "R-29",
  "amount_minor": 48000,
  "currency": "NOK"
}

These are fictional internal account references and a test amount. They are not real organisation numbers, payment credentials or a statement about invoice requirements.

The inspected parser keeps R-29 when it constructs its ordinary object. Subsequent validation sees one well-formed reference, finds that account and creates test draft D-61 for the wrong recipient. The amount and currency remain correct. A total-only reconciliation therefore misses the fault.

The reviewer retains the original request bytes alongside the parsed representation and draft. Comparing only two screens after parsing would hide the repeated name that caused the ambiguity.

Choose an explicit bridge contract

RFC 8259 section 4 recommends unique names within a JSON object and describes differing receiver behaviour when names repeat. It does not establish that every JSON parser rejects all duplicates. This bridge adopts a stricter, documented rule: any repeated member name within one object is rejected before business interpretation or document creation.

That rule also rejects a repeated name whose two values happen to be identical. Otherwise, the producer's faulty merge would remain accepted until one of the values changed. An array containing several separate objects may legitimately give each object a recipient_reference; that is different from repeating the name inside the same object. The chosen detector checks each object's members in its own scope, including nested objects.

The team verifies that its parsing path can expose the sequence of members before collapsing it into a dictionary or map. A check run on the already collapsed object is too late. Changing the preferred winner from last to first would merely select a different interpretation of the same ambiguous request.

Separate containment from the corrected producer

First, the receiver's duplicate detection is exercised with Q-61's captured input in isolation. It refuses the request with a useful duplicate-member error. The test records no new draft, no outbound invoice submission and no payment operation. The existing wrong test draft D-61 is separately marked withdrawn in the test history; it is not silently reassigned to another account.

Next, the producer's assembly rule is repaired. It obtains the recipient from the approved billing selection and emits that member exactly once. The old default remains available only for a separate, explicit selection process; it cannot add another recipient member while serialising the chosen request.

A fresh request Q-62 is then created from the approved instruction. It contains one recipient_reference with R-14. The bridge validates it and creates new unissued test draft D-62. The reviewer opens D-62 and checks its recipient reference, amount, currency and source request. This demonstrates the producer repair rather than simply editing a screen that was already wrong.

Prove both rejection and an ordinary successful path

Input or record Required result
Original Q-61 with R-14 and R-29 under the same member name Rejected before a new draft is created
Duplicate recipient_reference repeated with R-14 twice Rejected under the same strict contract
A nested object containing a repeated member Rejected; nesting does not bypass the rule
Q-62 with one approved R-14 member New unissued D-62 contains R-14 and the intended test values
A separate authorised request for R-29 Its own draft correctly retains R-29
One unique but unknown recipient reference Normal recipient validation refuses it
Two separate valid line objects sharing a legitimate field name Accepted or rejected by their real schema, not misclassified as one duplicate object

The R-29 control matters. The problem was not that R-29 was inherently forbidden; it was that Q-61 did not communicate one unambiguous authorised selection. The unknown-reference control also shows that removing ambiguity does not replace account validation.

The final record distinguishes withdrawn D-61 from corrected new D-62. Both remain test documents. No conclusion about financial settlement, tax treatment, electronic-invoicing eligibility or delivery to a real recipient follows from this parsing exercise.

Apply the lesson to the actual connection

An invoice integration needs more than a clean JSON object. The organisation must confirm its own identity, delegated account access, receiving arrangements and the meaning of acknowledgements from the actual service. A transport receipt may establish that something arrived without establishing acceptance as a business document. Those meanings belong in the integration's review criteria.

For the concrete defect here, acceptance is complete when the ambiguous input is refused before effects, the producer emits one approved recipient, a fresh draft reads back correctly and independent valid and invalid requests still receive their proper treatment. Keep that evidence with the producer and bridge versions so a later library change does not quietly reintroduce winner-takes-all parsing.

The invoice connection service examines the wider boundary. To prepare for it, bring a redacted raw request, its receiving result and the approved recipient decision through the project brief. Do not send live invoice documents or private account access in an initial enquiry.