maesn
For developers

How to integrate with Abacus: Three values you cannot hardcode

Abacus runs on the customer's server, either their own or a hosting provider's. That single fact decides more than it sounds like. There is no shared host to send a request to, the annual version decides which endpoints exist, and the closest thing to an event is a mailbox that deletes itself if you stop collecting from it.

Lennart Svensson, CTO and Co-Founder at Maesn
Lennart Svensson
CTO and Co-Founder · · Updated
Illustration for How to Integrate with Abacus: Host, Version and Mandant
The context

One Swiss ERP, and no two installations are alike

Abacus is a Swiss ERP for small and mid-sized businesses, covering financials, payroll and project management, and it reaches the parts of the market that never moved to a browser-only product: fiduciaries, construction firms, manufacturers. If you sell software to Swiss companies, it turns up early in the integration backlog.

It is also a system where the question where does it run has no single answer. Abacus is installed, either on the customer’s own server or on one belonging to a hosting provider, and that is not a deployment detail. It is the reason the next four sections exist.

Everything a platform normally settles for you is settled per customer here. Which endpoints exist, how many requests you may send, what you can subscribe to, even where the logs end up.

If you would rather have those differences absorbed than handled, the Abacus API page is the object by object view of what a connection through us carries. The rest of this guide is the consequences themselves, in the order you meet them.

Two routes in, and you pick one

Abacus can be reached through its REST endpoints or through the SOAP service AbaConnect, and Maesn carries both as separate target systems, abacus and abaconnect. Most core resources such as contacts and projects exist on both, while some module objects exist only on AbaConnect, Ababau for the construction industry among them.

Our own recommendation is to choose one and stay with it, because the two authenticate differently and a customer authorising twice is a customer asking why.

Target systems, the recommendation and the module split from our Abacus documentation and its AbaConnect counterpart, checked 13 August 2026.

The problem

The base URL comes from the customer, one at a time

Start with the smallest thing that usually goes without saying. To call an endpoint you need a host, and for Abacus there is no host to hardcode. Each installation answers on its own address, and where a provider hosts several customers it can differ per Mandant as well.

So the base URL becomes an onboarding field. It travels as a query parameter on the authentication call and is stored against that customer, which keeps it out of your configuration once you have more than one of them.

The Mandant is the second value of the same kind. Abacus calls it the Mandant on both routes; environmentName is our own name for that layer, and it is what the REST route asks for and what the environments endpoint returns. On AbaConnect the vendor’s word stays and the value is fixed at login.

Before the first requestThree values, all from the customer
  1. 1The base URLbaseUrl

    Sent as a query parameter on the authentication call and stored per tenant.

    Depends on whether Abacus is installed locally or hosted by a provider.

  2. 2The Abacus version{abacus-version}

    Part of the login redirect URL your service user is configured with.

    A new major release every year, each with its own endpoint catalogue.

  3. 3The MandantenvironmentName

    Read from the environments endpoint, then sent on every business call.

    One installation can hold several. Abacus calls it the Mandant; environmentName is our name for it.

None of the three has a default. They are properties of one customer’s installation, which is why they are collected once during authentication rather than configured once in your code.

Where each of the three values is obtained. Fields and parameter names from Maesn's Abacus and AbaConnect documentation.
Authenticating a customer whose host you learn at runtimeHTTP
GET /auth/abacus?baseUrl=https://acme.abacus.ch HTTP/1.1
Host: api.maesn.dev
x-api-key: <your maesn api key>
 
GET /accounting/contacts?environmentName=7777 HTTP/1.1
Host: api.maesn.dev
x-api-key: <your maesn api key>
x-account-key: <the account key for this customer>

The base URL is supplied once, at authentication, and stored per tenant. After that the Mandant travels as environmentName on the business call.

The baseUrl query parameter, the mandatory environmentName and the environments endpoint from our Abacus documentation, checked 13 August 2026. Its prerequisites are worth reading in the same pass: an administrator on the instance, and for production a user permitted to reach the mandant-provisioning endpoints.

One consequence lands outside your code. Because Abacus runs on the customer’s machine, so do its logs. Turning on request logging means editing a properties file in the installation and setting a level such as ch.abacus.eapi.level = FINER, after which the entries appear in that server’s log folder. Support conversations here start with somebody else opening a file.

Log levels and the properties file from Abacus’s troubleshooting page, checked 13 August 2026. Four levels exist and each one contains the previous, with request and response bodies appearing at FINER.

The problem

A new major version every year, and the catalogue moves with it

Abacus ships a major release annually, and the interface ships with it. The developer portal does not publish one endpoint overview, it publishes one per version, with its own changelog beside it. A customer on 2024 and a customer on 2026 are running the same product and offering you two different catalogues.

The gap is visible even in a list nobody would expect it in. Abacus 2026 exposes an Absences entity set; the 2024 catalogue starts one entry later in the alphabet, because that entity does not exist there. Multiply that by a few hundred rows and you have the reason a feature can be ready for one customer and impossible for another.

The documentation says not to trust the documentation

Abacus is unusually direct about this, and the sentence is worth taking literally: “This documentation is automatically generated from pre-release Abacus Versions. To be sure that an interface is available on a particular Abacus Version please check the Abacus APIs on the installation itself.”

The authority is the customer’s server rather than the portal, which is the same statement the base URL makes in a different register.

Per-version catalogues and the quoted caveat from Abacus’s developer portal, checked 13 August 2026. AbaConnect goes one step further: where an interface description is missing from its documentation, the vendor tells you to export it from program 625 inside the installation.

The version even reaches the parts of the setup you would call boilerplate. The login redirect you register for a service user carries it in the path, so the callback is version-specific rather than tenant-specific, and adding support for a new release is a configuration step rather than a code change.

This is the point where a normalised model stops being a convenience. When two customers answer differently to the same question, something has to decide what your product sees, and doing that once is what the common data model is for.

How it works

Two target systems, and they authenticate differently

The REST route uses OAuth, with the authorization code and client credentials grants, both returning a JWT. What it does not use is an ordinary user. Abacus wants a service user, created in the integration wizard, and the definition is strict: “The specified Service-User can only access Abacus via the available interfaces (access to the Abacus UI is not possible).”

Two details around that will cost you a day each if you meet them in production instead of reading them here. The first is a deadline: “Please note that the integration has to be approved within 10 days otherwise it will be deactivated.” An integration your customer creates and then leaves for a fortnight is gone.

The second is an error code that does not mean what it says. A token request without the form content type comes back as 404, naming the token path, rather than the 400 or 415 you would reach for first. Abacus documents the exact body, so the misleading part is documented too.

The header whose absence produces a 404HTTP
POST /oauth/oauth2/v1/token HTTP/1.1
Host: acme.abacus.ch
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64 of client-id:client-secret>
 
grant_type=client_credentials

Omit the Content-Type and the documented answer is {"status":404,"title":"Not Found",...}. The token that comes back is valid for 600 seconds, and Abacus asks you to reuse it for that whole window rather than fetching one per request.

Grants, the service user, the ten day approval window, the 404 and expires_in from Abacus’s authentication guide, checked 13 August 2026. The token endpoint itself is discovered through the openid-configuration document, which the same page asks you to read rather than cache.

The two target systems side by side
PropertyabacusAbaConnect
AuthenticationOAuth, service userUsername and password
ProtocolRESTSOAP
CompletionAnswers directlyA task id to poll when not immediate
ConcurrencyRate limited per userOne active request at a time
MandantenvironmentName per callFixed at login
Headless account keyNot availableAvailable

Authentication and the AbaConnect restrictions from our AbaConnect documentation, the headless list from our authentication guide, both checked 13 August 2026. Where a customer selects a Mandant themselves, note that the interactive selection screen covers fourteen named target systems and Abacus is not one of them today, so the value is collected in your own flow or passed through.

How it works

200 calls a minute, and 400 from 2026

Abacus publishes its limits, which is not universal: our own rate limiting reference records four systems that enforce a ceiling without publishing the value. Abacus names three windows and applies them at the same time, a minute, an hour and a day. Exceed any of them and you get a 429 with a small JSON body naming the servlet and the url that was refused.

The number, though, is a property of the version rather than of the product. That is the third time the same sentence turns up in this guide, and here it lands in your scheduler: a sync sized for a customer on 2026 will spend the afternoon backing off at a customer on 2025.

Documented request ceilings, by Abacus version
WindowUp to 2025From 2026
Per minute200400
Per hour12.00018.000
Per day30.00040.000

Both sets of figures, the three windows and the 429 body from Abacus’s rate limiting page, checked 13 August 2026. The limits are counted per user, which is why the service user is also the unit your budget is measured in.

Three documented ways to spend less exist, and the third changes the arithmetic. A $expand pulls related entities in the same request, a deep insert writes them in one, and a batch request counts as a single request against the limit however many operations it carries.

Read against that budget, one more number matters: a response returns at most 100 records, and you walk further with the @nextlink the response carries. A first sync of twenty thousand contacts is therefore two hundred calls before anything else you wanted to do that minute. Paging behaves differently on nearly every system, which is why unified pagination and filtering exists as its own layer.

How it works

The change feed is a mailbox you have to empty

Abacus sends you nothing. There are no webhooks and no callbacks, and our own coverage data records no native event for any object. What it offers instead is easy to misread as push, because it is called a subscription: you register a topic, and Abacus records what changed until you come and collect it.

The loop is three calls. SubscribeChanges registers a topic under a name you choose, ConsumeChanges returns everything not yet acknowledged, and AcknowledgeChanges clears the batch with the key that came back. The mandant sits in the path of all three, so a customer with three Mandants runs the loop three times.

Subscribe once, then consume and acknowledge on a scheduleHTTP
BASE = /api/entity/v1/mandants/7777
 
POST {BASE}/SubscribeChanges
{ "Subscription": "acme-sync", "Topic": "Subject" }
 
GET {BASE}/Subscriptions('acme-sync')/ch.abacus.df.ConsumeChanges()
 
POST {BASE}/Subscriptions('acme-sync')/ch.abacus.df.AcknowledgeChanges
{ "AcknowledgeKey": "fa63c83f-e50d-11ee-877d-0050568d67ed" }

The prefix is abbreviated the way Abacus abbreviates it in its own endpoint table. The subscription name is yours to pick, has to survive being put in a URL and is capped at 23 characters. One user may hold ten subscriptions.

Three rules turn that from a convenience into a job with a deadline. Consume at least once a day. Acknowledge at least once a day when there is anything to acknowledge. And “subscriptions that are not used to consume messages for more than 3 days will be automatically removed”, which means a weekend outage can end with the subscription gone rather than a backlog waiting.

What arrives is a list of identifiers rather than records, so a read follows each message. And one sentence in the reference changes how you use the timestamp: “Only 1 message per changed data record is registered. If multiple changes have been made to a data record on the Abacus Server, the ‘CreatedOn’ timestamp references the time of the first change.”

It is when the record was first noticed, not when it last moved, and a watermark built on it will sit earlier than the truth.

Acknowledgement is all or nothing as well: one key clears the whole batch. Fail halfway through processing and your choice is to acknowledge work you did not finish or to consume it all again.

Subscription topics availableCounted per version
Abacus 2023
13
Abacus 2024
15
Abacus 2025
17
Abacus 2026
29

Twelve of the topics exist only in 2026, and all twelve are document topics from the property and real estate side of the product.

No topic covers an invoice, a bill, a journal entry or a payment. The list tracks master data and shop orders, so an accounting integration polls for the rest whichever version its customer is on.

Subscription topics available per Abacus version, counted from the topic table on Abacus's developer portal.

The three endpoints, the daily requirements, the three day removal, the name limit and the first-change timestamp from Abacus’s subscription documentation, checked 13 August 2026. The counts were parsed from that page’s topic table twice, through its rows and through its text, with the same result: 29 topics, none of them for an invoice, a bill, a journal entry or a payment.

What that leaves for an accounting integration

Master data can be tracked and documents cannot. For everything the topics do not cover you read on a schedule you choose, and since the ceiling is generous and the payload is small, the interval is a product decision rather than a constraint. How one event model is put over systems that push, systems that queue and systems like this one is on the unified webhooks page.

How it works

One session, one Mandant, one request at a time

AbaConnect is the route you need when a customer runs a module whose objects the REST endpoints do not carry. It logs in with a username, a password and a Mandant, and returns a login token. From there it behaves in a way that is worth reading before you design around it.

Every data request comes back with an IsFinished flag, and that is not the same as every request making you wait: many return it true on the first call, while others need one or more polling loops with the RequestID you were given, 100 to 250 milliseconds apart. Writes are the ones that usually wait and reads sometimes do, and which of the two it will be is not something you can tell before you send it. While the flag is false that request is pending, and “a pending request will block other requests in the same user session, until the RequestID is cleared”.

Losing the RequestID blocks the customer, not the request

If the connection drops mid-poll, the id stays open on the server and further calls are refused with error 8040, which prints the blocking id. The documented remedy is narrow: “The only correct way to clear the current RequestID on the ABACUS Server is to ensure the ‘IsFinished’ requests run until ‘IsFinished’ = ‘true’.”

It can be picked up again with the same id once the connection is back, which is the argument for persisting that id rather than holding it in memory.

Two more properties compound it. A user gets one session, and “multiple User Sessions for the same User are not possible”. And a session holds one Mandant: switching means logging out and logging in again. Serial requests, one client, one Mandant at a time.

That is also the real reason behind the advice to create a dedicated integration user. It is not tidiness. A person logging into the Abacus menu with the same account can terminate the web service session, after which your calls fail with an invalid session, and since Abacus V2018 the usernames permitted to log in this way have to be listed in a server configuration file anyway.

Sessions themselves are not on a fixed clock. The default is that a session lives until logout, with the server closing one that has been inactive for a while, in the documentation’s own words “e.g. 15 – 20 minutes”. Two useful small operations sit beside all of this: Ping tests reachability without logging in, and IsAlive tests whether a session is still there. Through Maesn neither is your job: where a session has been closed, the next call logs in again before it runs.

One line from the same family belongs in a security review rather than a build plan. The connection runs over http or https depending on the URL you were handed, so transport encryption here is a property of how that server was set up.

The IsFinished procedure, the polling interval, error 8040, the session and Mandant rules, the configuration file and the inactivity window from Abacus’s AbaConnect web services documentation, checked 13 August 2026, which is also the source our own rate limiting reference cites for the single active request. The protocol note comes from the AbaConnect download page linked further up.

Through Maesn the shape stays but the bookkeeping moves. Asynchronous work returns a taskId, and GET /asyncTask/{taskId} reports progress and hands over the result when it is ready.

Our own list of asynchronous endpoints includes GET as well as POST on contacts, projects and journal entries, which is what a read-side wait looks like from the outside, and the same queue sequences requests per tenant. What that buys beyond neatness is on the asynchronous processing page.

Where our part ends

What Maesn covers, and what stays with you

Two interfaces, one of them SOAP, one catalogue per year and a change feed with a deadline. What we take off the list:

  • The three values become one onboarding step. The base URL arrives as a parameter during authentication and is stored per tenant, the Mandant travels with the call, and neither ends up in your configuration.
  • Both routes arrive in one shape. Whether a customer is served over the REST endpoints or over AbaConnect, a contact reaches you as a contact. The SOAP envelope stays on our side and so does the IsFinished bookkeeping, including the part where a request turns out not to need any: what reaches you is a task id when there is something to wait for.
  • Asynchronous work has one interface. A task id and a status endpoint, the same for the systems that queue and the ones that answer immediately.
  • Sandboxes exist even where the vendor has no free trial. Abacus and Ababau are two of the systems where a trial cannot simply be registered, and we provide the environment.

What stays with you, and two of these are decisions rather than tasks:

  • The polling interval. We do not turn Abacus into an event source, because it is not one. The subscription topics cover master data, everything else is a read on a rhythm you choose, and how fresh your product has to be is a product question.
  • Which version your customers are on. It decides what exists, and it is worth asking during sales rather than discovering during implementation.
  • The setup inside the installation. The application installed with its AbaConnect option enabled, a service user with an approved integration, and a Mandant enabled for the interface. Abacus notes pricing implications for the number of clients enabled, and the detailed conditions live in a knowledge base article open only to its sales partners.

Application access, the service user’s licence footprint, client enablement and the partner-only knowledge base article from Abacus’s licensing page, checked 13 August 2026. The same page lists two licensing models and marks the marketplace one as work in progress, which is why this guide describes no listing requirements.

FAQ

Frequently asked questions

Does Abacus support webhooks?

Nothing calls you. Abacus offers subscription topics instead, which you poll: you register a topic, collect changes with ConsumeChanges and confirm them with AcknowledgeChanges. Both belong in a daily job, because a subscription that goes three days without being consumed is removed automatically.

What are the Abacus rate limits?

Three windows at once, per user, and they move with the version. Up to Abacus 2025 it is 200 requests a minute, 12.000 an hour and 30.000 a day. From Abacus 2026 it is 400, 18.000 and 40.000. A batch request counts as one.

Why does my Abacus token request return 404?

Because the content type is missing. Abacus documents that a token request without the header Content-Type: application/x-www-form-urlencoded normally fails with a 404 naming the token path rather than a 400 or a 415. The token itself is then valid for 600 seconds.

What is the difference between the Abacus REST endpoints and AbaConnect?

Two interfaces with two authentications. The REST endpoints use OAuth and answer directly. AbaConnect is a SOAP service that uses a username and password, runs one request at a time and reports completion separately. Some module objects, Ababau among them, exist only there.

Why does AbaConnect say a request is already pending?

A previous request was never polled to completion. On AbaConnect a request can come back pending rather than finished, writes usually and reads sometimes, and which of the two you are getting is not something you can tell before you send it. While IsFinished is false the RequestID blocks the session. Error 8040 names the blocking id, and the only documented way to clear it is to poll that same id until IsFinished is true.

Does connecting to Abacus cost my customer anything?

It can, and the amount is not public. The application has to be installed with its AbaConnect option enabled, the service user counts towards that application's user licences, and clients have to be enabled by Abacus Research AG, which states pricing implications for the number enabled.

Build once on the Unified API.

Abacus hands you a different host, version and ceiling per customer, DATEV asks for two approval meetings and Xero meters what you read. Build against one interface and each of those becomes a row in a table rather than a project.