How to integrate with Visma e-conomic: The subscription decides what you can write
Most of what decides a Visma e-conomic integration is not in your code. The subscription decides whether you can write, the agreement decides which modules answer and the vendor has frozen the surface that still carries the one call telling you both. All three are readable before you build anything.


The customer's subscription decides what you can write
Before any question about tokens, objects or field names, this system asks a commercial one: which plan is the customer on. The answer decides whether your integration can write at all, and it is not something your code can influence.
e-conomic publishes the mapping as a table. Internal agreement types and the Basis subscription support read operations only through the public interface. Full create, read, update and delete begins at Plus.
| Agreement | Capabilities |
|---|---|
| Internal agreement types | Read only |
| Basis | Read only |
| Plus and above | Create, read, update and delete |
A write against one of the first two is not a permission you can request or a scope you forgot. The response says what it is: “The license is read-only. Some e‑conomic subscriptions are not allowed to change data via public APIs. Only read operations are allowed.”
The commercial side of the same rule is written for end customers instead of developers, and it is blunter: a Basis subscription has to be upgraded to at least a Plus package before integrations work at all, because API access is part of the package rather than of the product.
So the qualifying conversation carries a technical question now. Which objects are enabled on a connection through Maesn, and which sit on request, is listed object by object on the Visma e-conomic API page. Which of them a given customer may be written to at all is decided by the plan on their agreement.
- Internal agreement types
- Read only
- Basis
- Read only
- Plus and above
- Create, read, update, delete
The vendor’s own demo agreement is one of the read-only cases. It reports subscriptionPlanLevel as internal, so a first integration built against it can never post anything, however correct the code is.
Plan levels, the 403 body and the recommendation to check before writing from e-conomic’s guide to GET /self, checked 14 August 2026. The upgrade requirement from its support article on missing integration access.
Two tokens, and one call that tells you what they can do
e-conomic does not speak OAuth. Every request carries two headers instead: X-AppSecretToken, which identifies your application and is the same for all your customers, and X-AgreementGrantToken, which identifies one customer’s agreement and differs for each of them.
You can see both work without registering anything, because the vendor publishes a demo agreement and documents the two literal values that reach it. Only GET is allowed there, which is enough to read an agreement, its plan level and its modules.
GET /self HTTP/1.1Host: restapi.e-conomic.comX-AppSecretToken: demoX-AgreementGrantToken: demo# 200, and the four fields that matter:# agreementNumber 1583064# company.companyIdentificationNumber 00052507# agreementType.subscriptionPlanLevel internal# application.appNumber 3162
Measured on 14 August 2026. The plan level on the demo agreement is internal, so the account the vendor hands new developers is one of the read-only cases.
e-conomic asks for this call first, and describes what it is for in one sentence: “GET /self tells you who you’re connected to, what you’re connected to, and what your integration is capable of doing.” Every integration should call it immediately after obtaining an agreement grant token.
The reason is that the grant token is anonymous. It proves that some agreement authorised your application and says nothing about which one, so verifying that the customer connected the company you expected means reading the agreement number and the company identification number back.
It is also the cheapest way to tell a dead connection from a broken request. A failed credential returns a specific pair rather than a generic rejection:
- 401 with error code E02250, message “Token does not correspond to a valid grant.” The stored grant token should stop being used at that point, and the customer reconnects.
- The module list of the agreement, in the same response. The demo agreement carries nine, among them Projektstyring, Dimension (afdeling/kvantum) and Subscription and Direct Debit, so a feature that depends on one can be gated before it is offered.
Through Maesn none of the two tokens reaches your runtime: the app token is submitted once and the per-customer grant is obtained during the connection, which is the same shape unified authentication gives every other system in the catalogue. e-conomic appears on neither the interactive nor the headless list, so the flow is a plain redirect and your customer is never asked to pick a company.
Purpose, the recommended ordering, the 401 body and the capability fields from e-conomic’s guide to GET /self, checked 14 August 2026. Module names and field values measured against the demo agreement the same day. Which credential Maesn asks you for is on the connect guide, which names exactly one: the app secret token.
REST is frozen and OpenAPI carries the version in the path
Descriptions of this system usually say it has two interfaces and leave you to pick one. The vendor is more specific than that, and the specifics change what you build: “Our RESTAPI is still supported, but it is currently in feature freeze. This means that existing functionality will continue to work and be maintained, but new functionality will generally be developed for our OpenAPIs instead.”
So this is a migration rather than a fork. For new work the recommendation is OpenAPI, there are still areas that exist only in REST, and running both at once is explicitly fine because “Authentication between Open- and RESTAPI is the same.” The same app token and agreement grant token reach either surface.
What differs is how a version reaches you. The OpenAPIs are published per resource, each with its own semantic version, and the version sits inside every URL rather than in a header or an account setting.
/{resource-api}/v{major}.{minor}.{patch}/{resource-name}# for example:GET /subscriptionsapi/v1.0.0/subscriptionsGET /customersapi/v3.1.0/customers# the older surface has no version segment at all:GET https://restapi.e-conomic.com/customers
The vendor states plainly that “you should configure the specific version in each API call”, so the version is a decision per request rather than per integration.
That buys predictability and it costs housekeeping. A major version change keeps the previous version alive for at least twelve months, and the transition is named as the partner’s responsibility rather than as an automatic upgrade. With fourteen separately published resource interfaces, that is fourteen clocks rather than one.
And the two halves are tangled in a way worth knowing before you plan a clean build on the newer surface: GET /self, the call the vendor asks every integration to make first, is “currently only available via our RESTAPI”. The plan level, the agreement identity and the module list are all on the frozen side.
None of this reaches your code through Maesn, which routes to whichever surface answers a given resource. It does reach your planning if you are weighing a direct build, and it is the same question for the other systems in the Visma group, where each product runs its own interface on its own schedule.
Feature freeze, the recommendation, the identical authentication and the twelve-month window from e-conomic’s guidance on choosing an interface, checked 14 August 2026 and last updated by the vendor the same day. Version format and the list of published interfaces from the OpenAPI reference and its own index.
Sorting and completeness sit on different endpoints
Reading a large collection here means choosing between two paging models, and the choice is made in the URL because they live on separate endpoints. One of them cannot be sorted and the other cannot be finished.
Cursor paging is the recommended one. You ask for a collection, get a first set of typically 1.000 records and a cursor, and pass that cursor back for the next set. When no cursor comes back there are no more records. The cursor is the identifier of the first item of the next set rather than a count of what remains.
Page-number paging is the other endpoint, reached by adding /paged and passing skippages and pagesize. The vendor recommends it only for table and grid interfaces, for a documented reason: “classic pagination is limited to only returning 10.000 results, everything else is ignored”.
And the restriction that turns this into a design decision: “sorting is only available with classic pagination… Sorting is not supported when using cursor-based pagination, as the cursor points to a specific record with a fixed ordering.” A complete pass and a sorted pass are therefore two different requests.
- Reaches
- The whole collection
- Page size
- Usually 1.000
- Sorting
- Not supported
- Reaches
- First 10.000 results
- Page size
- You choose
- Sorting
- Supported
A complete pass and a sorted pass are two different requests. The vendor recommends the cursor for anything large and keeps sorting on the capped mode, because a cursor points at a record in a fixed order. The older REST surface offers neither cursor: it pages by skipping pages, up to 1.000 records at a time.
The older REST surface has neither cursor. Its collections answer with a pagination block that skips pages, and it publishes its own ceiling in every response, which is the fastest way to see the model:
"pagination": {"skipPages": 0,"pageSize": 2,"maxPageSizeAllowed": 1000,"results": 5,"resultsWithoutFilter": 5,"firstPage": ".../customers?pagesize=2&skippages=0","nextPage": ".../customers?pagesize=2&skippages=1","lastPage": ".../customers?pagesize=2&skippages=2"}
Measured on 14 August 2026. results and resultsWithoutFilter come back on every page, so the size of the job is known from the first response.
Three paging models across one system is the kind of difference one way to filter and page exists to remove: the request shape stays the same, and which of the three answers it is decided on this side.
Cursor and classic paging, the 10.000 limit, the sorting restriction and the cursor semantics from the OpenAPI reference, checked 14 August 2026. The REST pagination block measured against the demo agreement the same day.
A token bucket per agreement, and every call has a price
Rate limiting here is not one number. It is a budget per customer that different endpoints drain at different speeds, and both halves are visible in the response headers.
The model is a token bucket. A bucket holds a number of tokens, every call takes some out, and the bucket refills at a steady rate: you can spend fast until it is empty, then only as fast as it fills. The unit is the part worth reading twice: “Your application receives a separate token bucket for each agreement it serves”. A backfill for your thousandth customer therefore does not slow down your first, which is the opposite of how a per-application quota behaves.
What each request costs depends on the endpoint. The vendor publishes examples and the measured values line up with them, so the header is worth reading rather than estimating:
| Request | Documented | Measured |
|---|---|---|
GET /accounts/{n} | ~1 token | not measured |
GET /self | ~5 tokens | 5 |
GET /accounts | not published | 3 |
GET /invoices/booked/{n} | ~13 tokens | not measured |
Two headers carry the state. X-CallCost reports what the request you just made consumed, and X-RateLimiting reports the remainder against the total. On the demo agreement that reads token-limit-10000-per-60-seconds: 9995/10000 after a single GET /self, which is the arithmetic in one line.
When the bucket empties the answer is 429 with a Retry-After header, and the vendor asks for that header to be preferred over a calculated wait. Two more instructions describe a worker instead of a retry policy: requests should go “one at a time, sequentially, rather than firing multiple requests in parallel”, and a 429 should be followed by exponential backoff instead of a tight loop.
One exemption sits in that budget, and it is e-conomic’s own: “Requests using an Idempotency-Key that is already known to the server cost 0 tokens and are not affected by rate limiting.” It applies to whoever holds the connection to e-conomic, which through Maesn is Maesn, so it shapes what a retry costs here rather than something you set per call.
The sibling product in the same group meters on a completely different axis: the Visma eAccounting API allows 600 requests a minute counted per client and per endpoint, with no per-call cost at all. One vendor, two products, two rate limit models, which is what one error and retry model is for.
Token bucket, per-agreement scope, call costs, headers, the sequential recommendation and the idempotency exemption from e-conomic’s rate limiting guide, checked 14 August 2026. Burst capacity and refill rate are given there as examples rather than as guarantees, so they are not quoted as figures here.
Cache window and X-ResultFromCache from the OpenAPI reference. Header values measured against the demo agreement.
References first, and a booked invoice is final
Where the plan allows a write, the write still needs company. A document you post here points at master data that already exists in that customer’s agreement, and each kind lives in its own collection: the layout, the payment terms, the VAT zone, the currency and the ledger account.
That is five lookups whose results belong to one specific agreement, which is why a direct build usually discovers them during onboarding rather than during development. A missing reference fails the request instead of degrading it.
The lifecycle is the second thing to design around. Invoices live as drafts and as booked documents, and once booked the document is final: a correction is a credit note rather than an edit. The operation list of a resource carries create, read, update and delete with no partial-update verb, so an update sends the whole document rather than the changed fields.
For reading only what moved, the OpenAPIs expose a lastUpdated property and a filter on it. Two caveats come from the same page and both matter for a delta sync:
- Not every resource has it. “Please bear in mind that not all resources have a LastUpdated property.” Where it is missing, incremental reading is not available on that resource.
- It moves when a child moves. “sometimes the LastUpdated property is modified by changes to a different model, as is the case with sales documents. For example, when an invoice line is created, the LastUpdated field on the invoice is modified.”
GET /customersapi/v3.1.0/the_resource?filter=lastUpdated$gt:2024-02-24# the filter grammar is property, operator, value:# $eq: $ne: $gt: $gte: $lt: $lte: $like: $in: $nin:# chained with $and: and $or:?filter=name$eq:Joe$and:city$like:*port
Filtering is enabled on collection endpoints but not on every property: a property that allows it carries x-filterable in the schema, and filtering on one that does not returns 400.
What a connection reads today is the ledger side: accounts, customers, journals and suppliers, with booking proposals and payments enabled for writing. The rest of the matrix is available on request, which means it is switched on when a use case needs it, and asking at design time costs a message.
Records here are not amended in place, so keeping figures in step means writing a correcting document rather than editing the original. That is how the platform models accounting rather than a restriction of this connection, and it is worth settling before the first write.
The consequence pairs with the next section rather than standing alone. With no update enabled and, as it turns out, no creation event to subscribe to, keeping a record in step is a read you schedule and a comparison you make, which is precisely the work one shared data model is there to make identical across systems.
Reference chain and the draft-to-booked lifecycle from Maesn’s material for this system. The five collections are listed in the REST resource index: layouts, payment terms, VAT zones, currencies and accounts each appear as their own collection there.
lastUpdated, its two caveats and the filter grammar from the OpenAPI reference, checked 14 August 2026. Object counts from the generated coverage data behind the integration documentation.
Twenty event types, and none of them says created
e-conomic can push events. It publishes a webhooks interface of its own, version 1.0.0, whose changelog dates the webhooks and eventTypes endpoints to March 2026, and the vocabulary is readable without registering: the demo agreement answers GET /eventTypes with the full list.
Twenty types come back, and the shape of the list decides more than its length. Eighteen of them are updates. The other two are bookings. There is no type for a record being created and none for one being deleted.
| Kind | Types | Examples |
|---|---|---|
| Updated | 18 | CUSTOMER_UPDATED, SUPPLIER_UPDATED, VAT_CODE_UPDATED |
| Booked | 2 | INVOICE_BOOKED, JOURNAL_BOOKED |
| Created | 0 | no type |
| Deleted | 0 | no type |
The payload is the second surprise. An event does not carry the record, it carries merge fields. For most types those fields are two numbers: [OLDNUMBER] and [NEWNUMBER]. So CUSTOMER_UPDATED tells you that a customer number changed and what it changed to, and a read follows if you need to know anything else.
If the identifier itself can change on an update, a sync keyed on the customer number has to treat a rename as a rename rather than as a new record. JOURNAL_BOOKED is the more generous case, with a daybook number and a serial number range.
One constraint from the same schema shapes the deployment rather than the code: “only one webhook per event type per agreement is allowed”. A webhook carries a URL, a content type, a display name and up to five custom headers you define, and the published schema contains no signing secret, so authenticating the callback is what those headers are for.
Events on this system are switched on per object when a use case needs them, so what a connection delivers today is a scheduled read and what it can deliver is a subscription you ask for. The event model is where that distinction lives, and it is the same distinction on every system in the catalogue.
Until an event is enabled for an object, freshness comes from a scheduled read on lastUpdated, with the two caveats that property carries.
Interface version, changelog date, the one-per-agreement rule and the webhook fields from e-conomic’s webhooks reference, checked 14 August 2026. The twenty types and their merge fields were read from GET /eventTypes on the demo agreement the same day. What is enabled on a Maesn connection comes from the object matrix in the integration documentation.
What Maesn covers, and what stays with you
What we take off the list:
- The two tokens. One app secret token submitted once, the per-customer grant obtained during the connection, and one account key in your code.
- Which surface answers which resource. The frozen REST endpoints, the versioned OpenAPIs and the routing between them stay on this side.
- Three paging models reduced to one. Cursor, page-number and the REST block all answer as ordinary offset paging.
- The five master-data lookups in front of a write. Layout, payment terms, VAT zone, currency and ledger account are fetched and matched for that customer’s agreement before a document is posted, so your write carries business data rather than five ids you had to go and find.
- A test account. Maesn’s sandbox documentation marks Visma e-conomic as having a free trial and as being provided by Maesn, so both routes are open.
What stays with you, and the first two are commercial rather than technical:
- The plan on your customer’s agreement. No layer turns a read-only subscription into a writable one. If your product posts anything, Plus is a requirement to raise in the sales conversation rather than a bug to debug later.
- The modules a use case depends on. The list is readable from the agreement, so gating a feature on it is a decision you can make up front.
- The polling interval. With no event enabled today, how fresh your product needs to be is your choice, against a token budget that refills per agreement.
Sandbox availability from the sandbox overview, which lists a free trial and a Maesn-provided environment for this system. Credential and connection steps from the connect guide.
Frequently asked questions
Why does my Visma e-conomic write return 403?
What does GET /self return, and why call it first?
Should I build on the e-conomic REST endpoints or on the OpenAPIs?
How do I page through a large collection in Visma e-conomic?
What are the Visma e-conomic rate limits?
Does Visma e-conomic send webhooks?
How do I sync only what changed in Visma e-conomic?

QuickBooks Online Webhooks: Events, Retries and Recovery
QuickBooks Online webhooks cover 29 entity types and expect HTTP 200 in three seconds. Why Intuit still asks you to poll change data capture.
Lennart Svensson · 25 Aug 2026
Lexware Office Pagination: The 406 and One Page Size
Lexware Office validates the page size and rejects a bad one with 406, the same code it uses for an unsupported media type. What that means for your read loop.
Lennart Svensson · 20 Aug 2026
How to Integrate with DATEV Rechnungswesen: One Connection
One connection carries reading and writing in DATEV Rechnungswesen, on a two-year token. Which objects travel in which direction is the real decision.
Lennart Svensson · 17 Aug 2026Build once on the Unified API.
Visma e-conomic prices every call and refills per agreement, Visma eAccounting allows 600 a minute per client and endpoint, and the two belong to the same vendor. Build against one interface and each of those becomes a row in a table rather than a project.