maesn

Holded API Integration

Holded is a cloud ERP for Spanish small and mid-sized businesses, and Visma acquired it in 2021. Invoicing, accounting, inventory, CRM, projects and HR sit in one product, so the accounting data you need is a section of something considerably larger. Connect it through Maesn and you reach it with the same REST API and the same data model as every other supported system.

YOUR PRODUCT+26
Trusted by winning software teams
HubSpotTipaltiPaywiseRallyQredNordhealthFindityFintoClockinHEROHolviLanes & PlanesAgicapHubSpotTipaltiPaywiseRallyQredNordhealthFindityFintoClockinHEROHolviLanes & PlanesAgicap
What it takes

Holded hands you a key, not a redirect

Three things about Holded land on your engineers before the first record moves: the credential is a static key your customer creates, the permissions sit on that key per endpoint, and the accounting data you want is one part of a product that also runs inventory, CRM and HR. Here is what each one is, and what Maesn takes off the table.

Holded: The customer creates the key

The handover runs on our page or none

Holded authenticates with a static API key that your customer generates in their own account, and Holded shows it exactly once. Maesn takes that key on a page you can style as yours, or you collect it yourself and post it to one endpoint that returns the account key directly. There is no OAuth redirect to build either way.

Holded: Scopes sit on the key

A refusal that arrives per call

Every Holded key carries scoped permissions, and an endpoint that needs a scope the key lacks answers 403 rather than failing at connect time. So a connection can look healthy while one call is refused. Maesn returns that refusal in the same error shape as every other system rather than a Holded-shaped branch in your code.

Holded: Accounting is one module of many

The accounting slice, already mapped

Holded publishes 338 endpoints across eleven domains, from sales and inventory through CRM, projects and HR. What Maesn exposes is the accounting-shaped part of it, mapped into the same objects you already read from every other system, so your code never learns where Holded keeps things.

What your code holds is an API key of ours and an account key per customer. The Holded key, the scope failures and the mapping out of an eleven-domain product stay on this side, and the next system answers on the same interface.

A short call is usually enough to tell whether Maesn fits your use case.

Customer voice

HubSpot moves party data into whichever ledger a customer keeps

HubSpot syncs client data out of the CRM and into the accounting system each of their customers runs, which is exactly the object set Holded has switched on: contacts, customers and suppliers. Today they reach DATEV and sevdesk that way.

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.
Aron Gosselin
Partnerships Manager, HubSpot
Why companies choose Maesn

Three reasons to reach the Holded API through Maesn

Connecting Holded directly is cheap to start and expensive to keep. The key handover is simple. Everything after it is Holded-specific: the scope model, a call budget that belongs to your customer's plan rather than to you and an object layout that looks nothing like the next system you add.

01

The Holded-specific work stays here

Key collection and storage per tenant, the scope refusal translated into one error contract and the accounting objects mapped out of a product that also runs inventory and HR. None of the three reaches the code you write.

02

What is switched on, in writing

Read and write are the same list here, so an object that arrives from Holded can be corrected in Holded without a second mapping. Everything beyond that list is available on request rather than absent, so the question is which objects your flow needs and not whether the connection can reach them.

03

The next system costs nothing

Once Holded runs, every other supported accounting and ERP system answers on the same interface with the same data model. Your second integration is a configuration step rather than a second key model.

The object set is what makes this a narrow integration rather than a small one. Contacts, customers and suppliers are the records two systems argue about most often, because both sides think they own the company they are describing, and keeping customer and supplier data in step is its own problem before it is a Holded one. What Holded adds is a spend limit on how often you are allowed to ask.

Authentication

One Holded API key, held on our side

Holded is one of the systems with no OAuth redirect at all. It authenticates with a key, so the only question is how that secret gets from your customer to us without passing through your backend. There are two answers, and both end with an account key.

Auth method
API key
One key per customer, generated in their own Holded account

One integration, every customer. Maesn holds the key per tenant, and what your code carries is an account key.

What Maesn holds for you
The key, stored per tenant
Your customer submits it once and Maesn stores it against their connection. Every later call carries your API key plus their account key, the same two headers you already send for every other system, and the Holded credential never sits in your database.
The page, or no page at all
Maesn provides the key entry page and it takes your branding, from the logo to the button and error colours. If you would rather run your own screen, one POST to the accounts endpoint returns the account key in the response and the page is skipped entirely.

The part worth designing around is the scope model rather than the key itself. Holded issues each key with scoped permissions and documents the permission every endpoint requires, so a key can authenticate perfectly and still be refused on a single call with 403. That failure arrives in production, on the one endpoint whose scope was missed, rather than at connect time when someone is watching. Maesn normalises it into the same error contract as every other system, which is the difference between one branch in your error handler and one per system.

How your customer connects
  1. 1

    They generate the key in Holded

    An administrator opens the developer settings in their Holded account, creates a key with a description and copies it. Holded shows the value once and never again, so your onboarding text has to say that before they close the dialog.

  2. 2

    They hand it over, or you do

    They paste it into the Maesn page that carries your branding, and there is a cancel path for the case where they close it halfway. If you collected the key earlier in your own flow, you post it instead and skip the page.

  3. 3

    You work with one account key

    Maesn stores the connection and returns an account key. From there the requests look identical to every other system in the catalogue, and nothing in your code is shaped like Holded.

Every system in the catalogue authenticates differently, and Holded is the case where the handshake is trivial and the permission attached to it is not. There is no token to refresh and no environment to select, so the work moves to a single question your onboarding text has to answer: which scopes does this key need before the first call. Unified authentication is where the storage per tenant and the branded entry page live, once for every system rather than once per system. When a scope is missing anyway, the 403 comes back in the shape you already handle, because one error contract covers every system rather than each one having its own.

Staying in sync

Holded publishes events, and you still pull

Holded documents a full webhook system of its own, and none of it is switched on through Maesn today. Both halves are true at once, and the second one decides how you build: change detection here is something you run rather than something that arrives.

What Holded publishes

Eighteen event groups and 58 events, from invoice.create to stock.update, signed with HMAC-SHA256 and retried with backoff. Subscriptions are configured inside a Holded account, and none of them reaches you through Maesn today.

What you run instead

One scheduled read, with the same filter and the same pagination you use for every other system. The code that detects change on Holded is the code you already wrote, pointed at a different account key, so nothing about the loop is Holded-shaped.

The interval is a commercial decision here as much as a technical one, and the budget it spends is not yours. Holded meters API calls against a monthly quota that belongs to your customer's plan. It counts per account rather than per key, so issuing a second key does not buy more room, and the lowest plan in its published table allows 500 calls a month. A polling loop therefore spends from a budget your customer owns and can exhaust, which is a conversation worth having while you size the interval rather than after the first 429.

Two paths exist in the Unified API and only one of them is available on Holded today. What arrives through the event model and what you fetch on a schedule stay separate concerns, and a system with nothing enabled puts all of the weight on the second. That is where a per-system integration is most expensive: you write a change-detection loop for Holded, then another for the next system with its own idea of what a page is, so one way to filter and page removes the part of the work that is not about Holded at all.

What that leaves is a loop whose frequency is a business input rather than a technical one, and it has one property working in its favour. A scheduled read catches up on whatever it missed while a service was down, where an event that was never delivered is simply gone. Sizing it is therefore a question of how often a call is worth spending, and the answer sits in the plan your customers are on rather than in your own code.

Before you start

What a Holded integration needs first

Most of this is settled before any code runs, and two of the four are the kind that surface after go-live rather than during the build. Four things are worth having decided first.

API access on the account, plus an administrator
The key is created inside your customer's account, and generating it needs administrator rights. Holded displays it once and never again, so the person who creates it has to be the person who hands it over. That is one conversation at onboarding rather than a support ticket a week later.
Which objects your use case actually needs
Contacts, customers and suppliers are switched on and go in both directions. Invoices, bills, credit notes, payments and expenses are marked on demand, which means a request rather than a switch you flip yourself. If your flow starts with a document, raise it before you scope the work rather than after.
The scopes the key has to carry
Holded documents the permission each endpoint needs, and a key issued without one is refused on that endpoint alone. Deciding which scopes the connection asks for is part of the onboarding text your customer reads, not an implementation detail.
An environment to build against
Holded offers a free trial you can register today, and Maesn provides a sandbox populated with test data on request. Either gets you building before the customer conversation is finished, and the trial is the faster of the two to start.

We go through the key, the scopes and the test account with you.

The data model

One list for read and write, and one shape at the end

What Holded gives back and what it takes are the same list, which is rarer than it sounds and decides how a two-way sync behaves.

Everything switched on goes both ways

Contacts, customers and suppliers are readable, creatable and updatable, the same three objects each time. Two systems here are shaped that way, and no document object is enabled yet.

No booking route runs through here

Journal entries are outside what Maesn exposes for Holded, in every operation, and not an object you can ask to have switched on. Plan for that if your product posts bookings.

No object deletes by default

No object has delete enabled. Accounting practice expects corrections and cancellations as new documents anyway, so plan them that way rather than around a delete you would have to request.

What Holded hands back it also takes: the objects you can read are the objects you can create and update, so a company that arrives from Holded can be corrected in Holded. That is the part that usually breaks when two systems both believe they own the customer record. What is not there is any document. No invoice, bill or credit note is enabled today, so a product that syncs companies finds a full round trip here and a product that posts invoices finds a conversation.

The party objects come back in the shared common data model, so a supplier from Holded has the same shape as a supplier from any other supported system, and the mapping code you write here is the code you reuse for the next one. That matters more than usual on a product this wide: Holded runs sales, inventory, CRM, projects and HR alongside the ledger, and none of that layout reaches you.

Everything beyond that list is available on request rather than unavailable, which means not switched on by default. Holded has belonged to Visma since 2021 and keeps its own API, its own key model and its own quota, which is why it has a page of its own here. Tell us which objects your use case needs, or browse every system in the catalogue to see how the same question is answered elsewhere.

Tell us which objects your use case needs and we will confirm what is possible.

Holded FAQ

Holded API questions

What is Holded?

Holded is a cloud ERP used by small and mid-sized businesses in Spain, and Visma acquired it in 2021. One product covers invoicing, accounting, treasury, inventory, CRM, projects and HR, which is the part that matters for a software team: the accounting data you want is one domain of eleven, and Holded publishes 338 endpoints across all of them.

What is the Holded API?

A REST API on a single base URL, authenticated with a static API key in an Authorization header. Resources sit under their own paths, and each key carries scoped permissions, so an endpoint whose scope is missing answers 403. Through Maesn you reach it as one interface with the same objects and the same error contract as every other supported system, and the key stays on our side.

Where is the documentation for a Holded integration?

Holded publishes its own developer documentation for the raw API, and the Maesn docs cover what runs through us: every object and operation, the authentication flow, the sandbox and what is enabled today. If the open question is whether the integration is feasible at all, the object coverage decides it faster than either.

How does Holded authentication work?

With a key rather than a redirect. Your customer's administrator generates an API key inside their Holded account, and Holded shows the value once. Holded is one of seven systems in Maesn's headless authentication flow, which means you can either let them submit the key on a page carrying your branding or collect it in your own interface and post it to one endpoint that returns the account key directly.

Does Holded support webhooks?

Holded documents them and none is enabled through Maesn today. Its own developer documentation lists 18 event groups and 58 events, signed with HMAC-SHA256 and retried with backoff, configured inside a Holded account. What you build against instead is a scheduled read using the same filter and pagination as every other system, and a pull has one advantage over a push: it catches up on whatever it missed while a service was down.

Which data can I read and write in Holded?

Three objects are enabled today and each one goes both ways: contacts, customers and suppliers can be read, created and updated. The rest of the object list is switched on when a use case needs it, so tell us which objects your product works with and we enable them for your connection. Journal entries are outside what Maesn exposes here.

What are the Holded API rate limits?

Holded meters two counters in parallel, a per-minute burst and a monthly call quota, and whichever fills first returns 429. The quota is the one that shapes an integration. It belongs to your customer's plan and counts per account rather than per key, so extra keys do not buy more room, and the lowest plan in Holded's published table allows 500 calls a month. Size a polling interval against that number rather than against the per-minute figure.

Is a sandbox available for Holded development?

Yes, and there are two routes. Holded offers a free trial account you can register yourself, which is the faster one, and Maesn provides sandboxes populated with test data on request. Either lets you build before the commercial conversation is finished.

Holded belongs to Visma. Does that change the integration?

Not on the technical side. Visma acquired Holded in 2021 and the product keeps its own API, its own key model and its own quota, which is why it has its own page here rather than sitting under the Visma pillar. What the group relationship does change is the picture further out: several Visma products are supported through the same Maesn interface, so a customer base that grows into the group does not become a second integration project.

Why integrate Holded through Maesn instead of directly?

Because the key handover is the cheap part. Direct means owning three things for one system in one country: the scope model and the 403 it produces on one endpoint at a time, a call budget that belongs to your customer's plan and resets monthly, an object mapping out of a product that also runs inventory, CRM and HR. Through Maesn you build once against one data model and every other supported accounting and ERP system is then reachable on the same interface.
Ship your ERP and accounting integrations. Connect once.
Book a demo