Sync customer and supplier data without guessing which records match
Both sides already hold the company. Your product knows it as one record, the accounting system knows it as another, and nothing in either of them says the two are the same. Maesn delivers what each side holds, in one shape, so your rules can decide.
- vatId2 of 20
- email20 of 20
- companyName20 of 20
Only the first one is an identifier, and it is documented on 2 systems. On the rest you are matching companies by name and email.
Two records, one company, and no key to prove it
Moving contact data between two systems is a solved problem. Knowing which contact is which is not, and the documented flow spends its middle step on exactly that.
Every other use case in this family starts with a document. This one starts with an identity question, and it is the reason contact sync is usually the first integration a team builds and the one they revisit longest. An invoice references a customer. An expense references a supplier. If the two sides disagree about which company that is, everything downstream is wrong in a way that is quiet rather than loud.
The documented flow names three fields to reconcile on, and they are not equivalent. A VAT identifier is registered, so two companies cannot share one, and matching on it is a lookup. An email address is often a shared inbox and a company can have several. A company name is a string somebody typed, where a legal form written out in full and the same one abbreviated are the same company to a human and two different values to a comparison. The identifier is the one that would settle it, and it is in the documented field list of two of the connected systems.
So on almost every system this is a matching problem with a tolerance rather than a key lookup, and the tolerance is a product decision. Match too loosely and two customers merge into one. Match too strictly and your users get a second copy of a company they already have, in a system where the next invoice will then reference the wrong one. Neither failure announces itself.
One connection, both routes to a contact
Contact records do not arrive the same way everywhere, and which route a system needs is knowable in advance rather than at the first empty response.
Two lists, kept apart the way most accounting systems keep them.
One list, on the systems that hold customers and suppliers together.
The routes are not a preference, and the second one is not the small version of the first. On five systems the combined endpoint is the only way in — bexio, Moneybird, odoo, Procountor, SnelStart — so a product that implements the obvious route and stops there loses them without an error to notice. Across both routes the reach is twenty-five of the twenty-nine connected systems.
Whichever route a system uses, what comes back is the same normalized record: a company name, an address, a contact type, an email, under the same field names on every system your customers run. That is what makes a matching rule something you write once instead of once per vendor, and it is the substance of the phrase one connection, since the customer authorises all of it once.
Keeping the two sides aligned afterwards is the half that usually turns into a project, and here it is a query parameter. Asking only for what changed since your last run means a nightly sync stays cheap as your customers' contact lists grow, and where a system emits events, one webhook subscription tells you rather than you asking. The documentation names the systems that support events, and it is a minority of them, so the read is the mechanism that always works and the event is the one that makes it prompt.
Where a system carries something the shared model has no field for, and on contact data that happens often because identifiers differ by country, reaching past the model is documented rather than improvised. What none of this does is decide what counts as a match. A layer that merged two records on its own judgement would be making a call about your customers' data that it has no standing to make.
A round trip that goes as far as the system allows
This is the one use case in the family where data genuinely moves in both directions, so the version that holds of the promise is a measurement rather than an adjective. Each step below is a subset of the one above it.
On five systems you can create a customer and never update one: Abacus, Pennylane, Sage Active, Twinfield, Xentral. The record lands once and then the two copies drift apart, because the correction the guide describes has nowhere to go. That is worth knowing before you promise a customer that their data stays aligned, and it is not visible from the create count alone.
Reading a customer record is the best covered operation in the entire model, which is why this use case is a reasonable place to start an integration rather than a hard one to finish. Writing narrows it, correcting narrows it again, and deleting is not available on any object. That last one reads worse than it is: accounting systems expect a customer with history to be deactivated rather than removed, and corrections to arrive as new documents. It does mean a duplicate you create is not something you can quietly delete afterwards, which puts the weight back on the matching step.
What you get from the connection, then, is a full picture of what the other side holds and the ability to act on it as far as that system permits, without a per-vendor project behind each verb. What you do not get is a decision. The guide describes resolving conflicting information as your work, and the numbers above are the reason that framing is right: on a system that cannot update, the resolution has to happen before the write rather than after it. Once the contacts agree, creating an invoice and posting an expense both reference a company that means the same thing on both sides, which is the whole reason their guides open here.
One company, the same company in both systems
HubSpot keeps client records aligned with DACH accounting systems.
“We integrate German accounting systems and rely on Maesn as a trusted enabler - ensuring seamless data flow, full compliance, stronger partnerships, and efficient, reliable financial processes that support our growth.”


HubSpot is this use case at the scale where the matching question stops being theoretical: client data, invoices and payment reminders moving between a CRM and the accounting systems their DACH customers actually run, for hundreds of those customers. The systems their case study names are sevdesk, Lexware Office and DATEV, three systems that disagree about almost everything except that a customer record has a name and an address. That disagreement is what a shared model absorbs, and it is why the interesting work stays with the rules rather than the plumbing.
Every operation on a contact, counted per system
Customers and suppliers on all three operations, then the combined endpoint, counted from the per-system documentation.
| What you need | Today | On demand | System cannot |
|---|---|---|---|
| Customers, reading | 20 | 2 | 7 |
| Customers, writing | 16 | 4 | 9 |
| Customers, updating | 11 | 8 | 10 |
| Suppliers, reading | 19 | 2 | 8 |
| Suppliers, writing | 14 | 4 | 11 |
| Suppliers, updating | 11 | 7 | 11 |
| Combined list, reading | 8 | 4 | 17 |
The table is the answer to the question that decides how much of a sync you can promise, which is not whether contact sync works but how deep it goes on the systems your own customers run. Customers sit ahead of suppliers on reading and on writing, and the two are level on updating, which matters if your product is built around the payable side: the first half of the feature is slightly thinner there, and a bill still needs its supplier to exist first.
The last row is the one people misread. A low count on the combined endpoint is not a coverage gap, it reflects how the systems themselves are built: most keep customers and suppliers as separate lists, so a merged route only exists where the underlying system merges them too. Counting the routes together rather than separately is the only number that describes reach honestly, which is why the section above does it that way.
We deliver the records, you decide what matches
On this use case the boundary is unusually sharp, because the documented flow puts the decision in the middle and names it as yours.
- Both routes to a contact record
- One shape on every system
- Writes and corrections back
- Only what changed since last time
- Which records are one company
- How close a match has to be
- Which side wins a conflict
- When the sync runs
Everything on the left is the connection: two documented routes into a contact list, the same record shape whichever system the customer runs, writes and corrections in the other direction as far as that system allows, and a cheap way to ask what changed. That is the part which would otherwise be one integration per accounting platform, each with its own idea of what a customer is.
Everything on the right is your product, and on this use case it is the substance rather than the trimming. Whether two records are one company, how close a match has to be before you act on it, which side wins when both have changed, and how often any of it runs are decisions that depend on your data and your users' expectations. The documentation calls this resolving conflicting information and leaves it with you, which is the right place for it.
One thing belongs here because the page it replaces put it in a headline. Maesn does not promise that a record can never be written twice. What prevents a duplicate is the reconciliation step, done by your product before the write, and the lastModified filter is what keeps the copy you match against current enough for that decision to be a good one.
Matching a counterparty before the write is plumbing, and it is the kind owned by product and engineering teams, not by the accounting system on either end.
Common questions
What does customer and supplier data sync actually involve?
Should we read /contacts or /customers and /suppliers?
How do we decide that two records are the same company?
Can we correct a record in the customer's system, or only create one?
Do we get told when something changes on their side?
How do we avoid re-reading everything on every sync?
Can we delete a contact through the API?
What stops the same record being written twice?
What if a system holds a field the shared model does not carry?
Is this the same contact sync the other use cases refer to?
Build once on the Unified API.
See how contact sync works for your integration, or dive into the technical reference.










