FINANCE
Writing to the ledger without breaking the books
Idempotency, validation, closed periods and approvals. A practical checklist for invoice, bill and journal entry write-back to accounting systems.
By Unify Int team5 min read
- write-back
- accounting
- idempotency
Reading from an accounting system is forgiving. If a read fails, you retry. If it returns stale data, the next sync fixes it.
Writing is different. A duplicate invoice goes to a real customer. A bill posted to the wrong account distorts a P&L. A journal entry in a closed period can upset an accountant's month-end. Your customers' accountants will notice, and they will blame your product.
This post is a checklist for write-back: creating and updating invoices, bills, payments and journal entries in your customers' ledgers.
1. Make every write idempotent
Networks fail mid-request. Your worker crashes after the ledger accepted the write but before you recorded the result. You retry, and now there are two invoices.
The fix is an idempotency key: a unique value per intended write, sent with the request. If the same key arrives twice, the second request returns the original result instead of creating a new record.
curl -X POST "https://api.unifyint.com/v1/finance/invoices" \
-H "Authorization: Bearer $UNIFY_API_KEY" \
-H "Unify-Connection-Id: conn_xro_3m9d7" \
-H "Idempotency-Key: 7d1f0c2e-invoice-1048" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_01JC8P9X4R",
"issue_date": "2026-11-01",
"due_date": "2026-12-01",
"currency": "USD",
"line_items": [
{ "description": "Install service", "quantity": "1", "unit_amount": "450.00", "account_id": "acc_01JC7Z1H2B" }
]
}'Rules for good keys:
- Derive the key from your own record, such as your invoice id plus an action. Random keys per attempt defeat the purpose.
- Store the key before you send. If you crash, you retry with the same key.
- Keep keys long enough to cover your retry window, at least a day.
Most accounting APIs don't offer native idempotency. Some offer partial protection, such as unique document numbers. Don't rely on that alone; a ledger may allow duplicate numbers or let users change them.
2. Use decimal strings for money
Floating-point numbers can't represent most decimal amounts exactly. 0.1 + 0.2 is not 0.3 in binary floating point. Over thousands of lines, rounding errors become visible differences.
Send and store amounts as decimal strings ("1931.04") or integer minor units. Convert to a decimal type in your code, never to a float.
3. Validate before you submit
Every ledger rejects bad writes, but the error messages vary from helpful to cryptic. Validating first gives you clear errors and fewer partial failures.
Check at least:
- Accounts exist and are active. Customers deactivate accounts. A cached chart of accounts goes stale.
- Account types fit the line. An income line on a bill, or an expense account on an invoice, is usually a mistake.
- Tax codes exist and apply to the company's region and settings.
- Currencies are enabled. Multi-currency is often a setting the customer must turn on.
- Customers and vendors exist, or create them first, idempotently.
- The period is open. See the next section.
- Totals add up when you send both line amounts and totals.
4. Respect closed periods
Accountants close periods after month-end so the numbers stop moving. Most ledgers can lock dates; some warn, some block, some let admins override.
Your product should never quietly post into a closed period. Options:
- Read the lock date where the platform exposes it, and refuse writes before it with a clear error.
- Post to the next open period if your customer has agreed to that rule, and record that you did.
- Ask a person. Surface the conflict in your product.
5. Handle concurrency
Someone may edit a record while your product updates it. QuickBooks Online uses a SyncToken for this. Other platforms use timestamps, ETags or nothing at all.
- Read before update and send the version the platform expects.
- On conflict, re-read and re-apply your change, or ask the user. Never overwrite blindly.
- Prefer partial updates so you don't erase fields you didn't mean to touch.
6. Add approvals where risk is high
Not every write needs a human, but some do. Common rules:
- Writes above an amount threshold.
- Writes to sensitive objects, such as journal entries or vendor bank details.
- Writes initiated by an AI agent rather than a user.
- The first writes for a new customer, until mappings are trusted.
An approval step pauses the write, notifies a person, and proceeds only when they approve. The idempotency key ties the approval to exactly one write.
ERPs often have their own approval workflows for vendor bills and journal entries. Respect them. A bill should usually enter the ERP as pending approval, not bypass the customer's controls.
7. Keep an audit trail
When an accountant asks "who created this bill?", you need an answer. Record, for every write:
- The actor: user, API key, workflow or agent.
- The object and action.
- Values before and after.
- The request you sent and the platform's response.
- The timestamp and idempotency key.
Make the log searchable by customer and exportable.
8. Plan for partial failures
Some operations span several calls: create a vendor, then a bill, then attach a PDF. Any step can fail.
- Order steps so each one is safe to retry.
- Make each step idempotent on its own.
- Record progress so a retry resumes rather than restarts.
- Surface stuck writes to your support team with the platform error.
9. Test against reality
Sandbox companies are clean. Real ledgers have inactive accounts, duplicate customer names, custom tax setups and years of history. Before launch:
- Test every write path in a sandbox for each platform.
- Test against a real, messy file with permission.
- Test retries by killing workers mid-request.
- Test closed periods and missing accounts on purpose.
The checklist
| Check | Why |
|---|---|
| Idempotency key per intended write | No duplicates on retry |
| Decimal strings for amounts | No float rounding |
| Validate accounts, tax, currency, period | Clear errors, fewer rejections |
| Respect lock dates | Don't move closed books |
| Handle version conflicts | Don't overwrite edits |
| Approvals for risky writes | A person signs off |
| Audit every write | Answer "who did this?" |
| Resumable multi-step writes | Recover from partial failure |
How Unify Int fits
The Unify Int write path is designed around this list: idempotency keys on every write, validation of accounts, tax codes, currencies and periods before submit, optional approvals, and an audit log with before-and-after values.
See Finance, the Accounting API and Permissions & Audit.