← All work

Accounting

Merit Bridge

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

Role
Developer
Year
2026
Stack
Laravel · PHP · Merit Aktiva
Status
Client project
Merit Bridge, rendered as the L1 · Schema layer of the Veltix module

Merit Aktiva · accounting system

pendingawaiting post
postedreference stored
failedneeds attention
  1. Invoice finalised
  2. Queued
  3. Reference check
  4. Post to Merit
  5. 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.

  1. 01failed
  2. 02Read the recorded reason
  3. 03Correct the data
  4. 04Retry
  5. 05Reference check
  6. 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

  • merit aktiva integration
  • merit aktiva api
  • laravel accounting integration
  • invoice posting
  • idempotent integration
  • duplicate invoice prevention
  • laravel queued jobs
  • invoice sync
  • estonian accounting software
  • erp integration
  • retry safe api
  • accounting automation

Contact

Build something that lasts.

Got a Laravel project, a WooCommerce mess or an integration that needs untangling? Tell me what's breaking.

© 2026 Veltix · Kristen LõokeGitHub · Tallinn