An invoice payload commonly contains one header and many items. Writing the header first and then looping through items appears simple, but failure on the final item can leave half an invoice. Sound design begins with a nested contract, stable keys, money rules, and a transaction boundary the database actually supports.
Define the nested contract before mapping
A header typically carries invoice number, customer, date, currency, and total. The item array carries line number, product, quantity, price, tax, and subtotal. Define required fields, nullability, length, precision, timezone, and item-count limits. Separate sender-provided fields from destination-generated values such as primary keys and timestamps.
| Part | Example rule | Risk if ambiguous |
|---|---|---|
| Invoice ID | Unique per source/tenant | Duplicate or collision |
| Currency | Agreed code | Values cannot be compared |
| Amount | Precision decimal and scale | Different rounding |
| Items | Object array, at least one | Header without detail |
| Line key | Unique within invoice | Duplicate line on retry |
Do not accept a free-form object and hope mapping repairs it. Child-path validation should identify a location such as items[2].quantity. This helps the sender correct a record without revealing internal database structure.
Separate the database primary key from the idempotency key
The database can create an internal ID, but integration still needs source identity to recognize a retry. Combine source system, tenant or organization, and invoice number/version according to business rules. Items need a stable line ID; relying on array position is risky when a line is inserted.
- Determine whether invoice numbers can repeat across companies or periods.
- Store external and correlation IDs with the record or in an integration table.
- Use a unique constraint as the final defense, not the entire deduplication design.
- Decide whether a retry returns the previous result, performs an upsert, or produces 409.
- Distinguish an invoice revision from delivery of the same payload again.
Validate structure and business rules
Schema validation checks field types and presence. Business validation ensures the customer exists, the product is active, currency is allowed, dates fall within the appropriate period, and totals reconcile with lines under approved rounding rules. Avoid binary floating point where precise decimal money is required.
Separate calculation from authority. A workflow may calculate a subtotal or compare totals, but tax rules, approval, and accounting posting must come from process owners and authoritative systems. Reject or route mismatches for review; do not silently replace sender values without a trace.
Understand the atomic transaction boundary
When the header and all items write through the same database connection, send one transactional batch: begin, insert header, insert items, commit. If an item violates a constraint, rollback removes the uncommitted header. This prevents a partial invoice in that database.
ACID atomicity does not automatically extend to another database or an external API. For multiple databases, NitroNet uses a journaled Saga and compensation; compensation can fail and need recovery. HTTP and email actions are at-least-once and cannot roll back with the database. A state such as pending_external plus reconciliation is often more honest than claiming every step is one transaction.
Make error responses useful to the sender
- 400/422 contract.
Return a field path and safe reason without SQL or secrets.
- 409 duplicate.
Explain that the key was processed and whether the old result can be retrieved.
- 404 reference.
Use when a required customer or product is absent under the API contract.
- Temporary 500.
Separate retryable internal failure from permanent errors.
- Correlation.
Return an identifier an operator can match to logs.
A header–item blueprint in NitroNet
NitroNet Receive Data can model a root object with an item object array. Header mapping runs once; For Each carries selected parent fields into every item. Write DB operations for header and detail on the same agent and database can be grouped into one atomic batch. Success, duplicate, failure, Catch Error, and Response paths return status from real database results.
Test one item, many items, duplicate lines, missing references, precision, and final-item failure in Sandbox and a test database. Verify that the whole batch rolls back. Read the ERP–tax-system pattern and order-to-fulfillment workflow. NitroNet composes the mechanisms; schema and invoice rules must still bind to real systems.