Accounting
Merit Bridge
Invoices posted straight to Merit Aktiva, with a retry path that cannot double-post.

Merit Aktiva · accounting system
- Invoice finalised
- Queued
- Reference check
- Post to Merit
- Store reference
Invoices belong in the accounting system, not retyped on the way there.
Merit Bridge posts invoices straight to Merit Aktiva. An invoice is raised in the application, and once it is finalised it turns up in the accounts without anyone copying across the number, the customer or the total.
That is the easy half. The hard half is that an accounting system is the one place you cannot post the same invoice twice. A retry that creates a second document does not just put a wrong number on a screen, it puts a wrong number in the books, and someone then has to find it and reverse it. So the integration was built around the failure path first.
Posting is a state on the invoice, not an event.
Every invoice carries its own posting state, shown in the invoice list beside the payment status. Next to that state sits the reference returned for the document created on the accounting side.
| Merit state | Meaning | What happens next |
|---|---|---|
pending |
Finalised, not yet accepted by Merit Aktiva | A queued job picks it up |
posted |
Reference stored against the invoice | Nothing — posting is closed for good |
failed |
An attempt did not go through | The recorded reason decides the next step |
That reference is the guard. It is written the moment a post succeeds, and every path that could post an invoice checks for it first, the retry path included. Posting runs as a queued job which re-reads the invoice immediately before sending, so two workers holding the same invoice cannot both reach the API.
Not every failure means the same thing.
failed is a state you can act on rather than a dead end, because the bridge stores what
came back with the attempt. The reason sits on the invoice instead of in a log file.
| Failure | Cause | Handling |
|---|---|---|
| Transport | Timeout, refused connection, error on the far side | Retried by the queue with backoff |
| Rejection | Data the accounting system will not accept | Surfaced with its message, no auto-retry |
| Sent, never answered | No way to know whether the document exists | Held for confirmation by a person |
Rejections are a data problem — a customer with no counterpart in the accounts, a tax rate that does not map, a period already closed. Retrying changes nothing, so they wait for a correction. The third row is the awkward one: from the outside there is no way to tell whether a document was created before the connection dropped, and guessing either way is worse than asking, so those attempts are never retried on their own.
Retrying is the same path, not a second one.
A corrected invoice is retried from the invoice list. The retry has no posting code of its own — it re-enters the same queued job through the same guard. That is the only reason retrying is safe to offer as a button.
- 01failed
- 02Read the recorded reason
- 03Correct the data
- 04Retry
- 05Reference check
- 06Posted once
Features
One Document Per Invoice
A stored reference closes posting permanently, whatever calls it afterwards.
Visible Posting State
Pending, posted and failed sit in the invoice list next to the payment status.
Queued Posting
Finalising an invoice never waits on the accounting system to answer.
Classified Failures
Transport problems, rejections and unanswered requests are handled differently.
Retry From The List
A corrected invoice is re-sent through the guarded path, not a shortcut around it.
Recorded Attempts
Each attempt keeps its response, so a failure can be read without opening the logs.
Technical Highlights
- Laravel queued jobs
- Reference-based posting guard
- Re-read before send
- Backoff on transport errors
- Failure classification
- Recorded request and response
- Retry from the invoice list
- Merit Aktiva API client
Laravel to Merit Aktiva invoice integration
Merit Bridge is a Laravel integration that posts sales invoices to Merit Aktiva, the Estonian accounting software, and writes the result back against the invoice. The design question in an integration like this is never the successful call, it is what the system does with the calls that fail: how a retry is made safe, how a rejection is told apart from a timeout, and how a person is given enough to act on without reading a log.
Topics