maesn
Product insight

One unified webhook model for every system you connect

Some systems push events. Many push nothing at all. Maesn gives you one webhook to subscribe to, one payload shape to handle and one signature to verify, and does the polling itself wherever a system stays silent.

Two kinds of systemone event
System pushes eventsnative webhook
System stays silentno native webhook
maesn emits the event
The same event body
{
  "eventType": "CREATED",
  "resource": "INVOICE",
  "resourceId": "bca91f06…"
}
Trusted by winning software teams
HubSpotTipaltiPaywiseRallyQredNordhealthFindityFintoClockinHEROHolviLanes & PlanesAgicapHubSpotTipaltiPaywiseRallyQredNordhealthFindityFintoClockinHEROHolviLanes & PlanesAgicap
The concept

What are unified webhooks

A webhook is the difference between asking and being told. Instead of calling an API on a schedule to see whether anything changed, you register a URL once and the system calls you when something does. For integrations that keep data in sync, that is the difference between minutes of staleness and a few seconds, and between constant traffic and traffic only when there is news.

The catch is that webhooks are the least standardised part of any API. Support is uneven, the payloads disagree, and the vocabulary for a created object differs from system to system. Unified webhooks collapse that into one subscription model and one event body, delivered through the same normalised data model as the rest of the Unified API. You write one handler, and it keeps working as your customers bring new systems.

The problem

Events are the least standardised part

Every system answers the same four questions differently, starting with whether it will tell you anything at all. Build against them directly and each integration needs its own listener.

Does it push at all
native eventsnothingpartial
What arrives
full payloada ping onlyan array
Event vocabulary
creatednewobject.add
How you verify it
signatureshared secretnothing
The two models

App-based or user-based, and it changes your code

Where webhooks do exist, ERP and accounting vendors have settled on two incompatible models. Which one a system uses decides how much routing and isolation you own.

One endpoint for everyone

App-based

  • All events from all connected users arrive at a single endpoint
  • Each payload carries a user identifier you have to route on
  • Common where a vendor runs a large app marketplace
  • Puts routing, validation and user isolation on your side
QuickBooks OnlineXero
One subscription per customer

User-based

  • A subscription is created per user and per event type
  • Delivery target is explicit rather than shared
  • Common in ERP and mid-market systems with stricter isolation
  • More to manage, clearer boundaries between customers
Exact OnlineBusiness Centralweclappand more

Payloads split the same way. Some systems send a ping and pull signal: the event says which object changed and you fetch the object itself in a second call. Others put business data straight into the payload, which saves a call and grows the thing you have to validate. In practice even generous payloads tend to miss a field a real use case needs, so the second call happens anyway.

Maesn standardises on ping and pull for every event, native or not. The event names the object, you read it through the Unified API, and the payload never becomes a place where customer data sits in transit. That is a deliberate security property as much as a consistency one, and it is what makes one handler viable across both vendor models.

System support

Which systems push events, and which stay silent

For ERP and accounting systems that support custom webhooks and triggers: a minority do, and the majority never will.

Native webhooks: 10 of 27 systems
Exact OnlineUser-basedPing and pull
Business CentralUser-basedPing and pull
XeroApp-basedPing and pull
QuickBooks OnlineApp-basedEvent payload
Lexware OfficeUser-basedEvent payload
FreshBooksUser-basedEvent payload
MoneybirdUser-basedEvent payload
QontoUser-basedEvent payload
Visma e-conomicUser-basedEvent payload
weclappUser-basedEvent payload
Selected special cases
FortnoxWebsockets instead of webhooks

Events arrive over a persistent connection rather than as discrete HTTP requests, which has to be kept alive, reconnected and scaled per customer. Maesn holds it and emits ordinary unified events from it.

XentralBeta, with a model of its own

Support exists but follows Xentral's own conventions and is still in beta, so the shape can change without notice. A change on their side is ours to absorb rather than yours to track.

No native webhooks: 15 of 27
AbacusbexioBuchhaltungsButlerDATEV RechnungswesenDATEV Unternehmen OnlineFreeAgentHoldedodooPennylaneSage AccountingSage ActivesevdeskSnelstartTwinfieldVisma eAccounting

This is the majority, and it includes the systems most in demand in the German market. Which is why “does system X support webhooks” is the wrong question to build an architecture on: the answer is usually no, and it changes without telling you.

All 27 deliver unified events through Maesn, in one model with one payload shape, whether the system pushes natively or Maesn polls it and emits the event itself.

The two special cases, Fortnox and Xentral, reach you as ordinary unified events like everything else. What each system publishes underneath, and why a per-vendor estimate does not survive contact with this table, is covered system by system in our webhook landscape article.

How Maesn handles it

Subscribe once, handle one event

One endpoint creates a subscription for one customer. From then on the events arrive at your callback URL in the same shape, whichever system they came from.

callbackUrl

Where events go

The endpoint on your side that receives every event for this subscription, as a POST request.

eventType

What to hear about

Subscribe to objects being created, updated or deleted, using one vocabulary on every system.

resource

Which objects

The resource you want to watch, an invoice or a customer for example, named the same everywhere.

One webhook type

A single unified webhook covers every system, so you write one handler instead of one per integration.

Synthetic webhooks

For systems that push nothing natively, Maesn polls on your behalf and emits the same event, so the gap never reaches your code.

Subscriptions per customer

Each subscription belongs to one customer, so their events arrive through their own subscription and stay separable.

Signed events

Every event carries an HMAC-SHA256 signature over the raw body, so you can prove it came from Maesn before you trust it.

subscribe.http
POST /webhooks
 
x-api-key: YOUR_API_KEY
x-account-key: CUSTOMER_ACCOUNT_KEY
 
{
"callbackUrl": "https://your-app.com/events",
"eventType": "CREATED",
"resource": "INVOICE"
}
Synthetic webhooks

Events from systems that push nothing

This is the part that does not exist without a unified layer. When a system has no native webhooks, or only covers a fraction of its objects, the usual answer is that you build a poller: a schedule, a cursor per customer and per resource, change detection, and a queue to smooth out the load. It works, and it becomes permanent infrastructure you own forever.

Maesn runs that poller instead, and turns the result into a normal event. You subscribe the same way you would to a native webhook, and you receive the same body with the same eventType, resource and resourceId. Nothing in your handler distinguishes the two, which is the point: whether a system pushes events stops being an input to your architecture. The polling that makes it work is paced by asynchronous processing, so it stays inside each system's limits without you scheduling around them. You will also see these called virtual webhooks; it is the same idea under a different name.

The gap they close is not only whole systems, it is objects. Native webhook support is usually widest on the sales side, invoices and sales orders, and thinnest on purchasing, where a purchase order often has no event at all. That leaves you running webhooks and polling side by side inside the same integration, for the same customer, with two code paths that fail differently. Synthetic events remove that split: an object without a native event arrives the same way as one with.

A system with no events
1No native webhook to subscribe to
2So you poll on a schedule yourself
3Every tenant, every resource, forever
4And you still learn late
maesn polls it for you
A synthetic webhook

You subscribe once and receive events. The polling, the scheduling and the change detection stay on Maesn's side, and the event body is the one you already handle.

same eventType · resource · resourceId
Event security

A callback URL is public, so every event is signed

Delivering events everywhere solves coverage. It does not solve trust, and the endpoint you just published is reachable by anyone who learns the URL.

An unauthenticated webhook endpoint is an open door: anyone who learns the URL can post a forged event to it, and a handler that acts on what it receives will happily book it. Every event Maesn delivers, native or synthetic, carries an X-MAESN-SIGNATURE header computed over the raw body, so your endpoint can establish that the event came from Maesn before it does anything with it.

In your handler, in this order
1

Recompute over the raw body

Use the secret you received when the webhook was created, and do it before you parse the JSON. Reserializing first changes the bytes and the signature will never match.

expected = hmacSha256(webhookSecret, rawBody)
2

Compare in constant time

A plain equality check can leak information through timing. Comparing both hex strings in constant time is a small, free hardening step.

isValid = timingSafeEqual(expected, header)
// not: expected === header
3

Key the work idempotently

Networks retry, so the same event can arrive twice. Key your processing so that handling it again is harmless, and a duplicate delivery stops being a duplicate booking.

key = `${eventType}:${resource}:${resourceId}`

Taken together these are one posture rather than three features. Ping and pull means a delivered event carries identifiers and not business data, so even an intercepted event exposes very little. The signature means you can trust an event before acting on it. And because Maesn keeps no copy of the data it routes, the platform in the middle is not a second place your customers' records live. For anyone who has to answer how this is secured, the answer is the same at every layer, which is also the point of the wider security posture.

Asynchronous responses

Told when the API call actually finishes

Not every system answers a write immediately. Some accept the object, queue it and produce the outcome later, which normally leaves you polling for your own result.

Your app
Post the object
Where you would otherwise be polling
The system
Queues it, no result yet
maesn
Tracks the outcome
Your app
Event: success or failure

Maesn can close that loop with a webhook: when the system produces the outcome, you get an event telling you whether the write succeeded or failed. It exists because customers asked for it, which is the honest reason most of this layer exists. Every request, response and event along the way is recorded, so when something does go wrong you can see what was sent and what came back through logging and monitoring.

Why it matters

One listener, every system

Building it yourself
  • Learn each system's event model
  • Poll the systems that send nothing
  • Turn pings into real payloads
  • Verify each scheme differently
With Maesn
  • One event model to handle
  • Silent systems polled for you
  • Normalised payloads either way
  • One signature check everywhere

Webhook support is the thing most likely to be missing from the system a customer wants next, and the thing most likely to force a rewrite when it is. Because the event model is Maesn's rather than each vendor's, that question stops mattering during a sales call: you support the system, and how its data reaches you is an implementation detail on our side. The handler you wrote for the first integration is the handler for the last one. Which system a prospect runs is then a question go-to-market teams can answer without checking with engineering first.

Unified Webhooks FAQ

Common questions

How do I set up a webhook with Maesn?

Create a subscription with a POST to the /webhooks endpoint, passing a callbackUrl, the eventType you care about and the resource you want to watch. The request carries your API key and the account key of the customer the subscription belongs to. Deleting a subscription is a DELETE to /webhooks/{webhookId}.

What does an event look like?

A small, consistent body: the eventType, the resource, the resourceId of the object that changed and a filterDate. Because it is the same shape everywhere, one handler covers every system. For a few systems the notification arrives as an array of objects rather than a single one, which the docs call out per system.

What are synthetic webhooks?

Events for systems that have no native webhooks, or only partial ones. Maesn polls the system on your behalf, detects what changed and emits the same normalised event you already handle. You subscribe once and receive events, without knowing or caring whether the system underneath pushes anything.

Which ERP and accounting systems support custom webhooks and triggers?

A minority of them. Of the 27 systems assessed, 10 offer native webhooks: Exact Online, Microsoft Dynamics 365 Business Central, Xero, QuickBooks Online, Lexware Office, FreshBooks, Moneybird, Qonto, Visma e-conomic and weclapp. Fortnox streams events over websockets instead, Xentral has a model of its own in beta, and 15 systems including DATEV, sevdesk, Pennylane and Sage offer nothing. Through Maesn all 27 deliver the same unified event, because the ones that stay silent are polled and their events emitted for you.

What is the difference between app-based and user-based webhooks?

An app-based model sends every event from every connected customer to one endpoint you configure for the whole application, with a user identifier in the payload that you have to route on. QuickBooks Online and Xero work this way. A user-based model creates a subscription per customer and per event type, which is more to manage but keeps customers cleanly separated. Exact Online, Business Central, weclapp and Qonto work this way. Maesn exposes one user-based model regardless of which one the underlying system uses.

What are virtual webhooks?

Another name for synthetic webhooks. Where a system has no native webhook for an object, Maesn polls it, detects the change and emits a unified event with the same body a native event would have. Nothing in your handler distinguishes the two, which is why whether a system pushes events stops being an architectural decision for you.

Websockets or webhooks for ERP integrations?

Webhooks, for anything that spans systems. Websockets give very low latency over a persistent bidirectional connection, but that connection has to be kept alive, reconnected and scaled per customer. A webhook is a stateless HTTP request that is easier to secure and to operate at volume. Fortnox is the one connected system that streams over websockets, and Maesn turns that stream into ordinary unified events.

How should I harden my webhook handler?

Verify the signature first, then two things on your side. Compare the signature in constant time, for example with crypto.timingSafeEqual, so the comparison cannot leak information through timing. And make processing idempotent by keying on the resource id and the event type, because networks retry and the same event can arrive twice. Without that, a duplicate delivery becomes a duplicate booking.

How do I verify that an event really came from Maesn?

Every event includes an X-MAESN-SIGNATURE header holding an HMAC-SHA256 signature. Recompute it over the raw request body using the webhook secret you received when the webhook was created, then compare the two values. Always verify against the raw body, before parsing the JSON.

What happens when a system processes a request asynchronously?

Some systems accept a write and only produce the result later, which normally means polling for the outcome. Maesn can notify you by webhook instead, once the result is known, including whether it succeeded or failed. It was built because customers asked for it.

Should I use webhooks or polling?

Webhooks suit reacting to individual changes as they happen. A scheduled delta pull with the lastModified filter suits reconciliation and catching up after downtime. Many integrations run both, using webhooks for immediacy and a periodic delta poll as a safety net.

Which events can I subscribe to?

Objects being created, updated and deleted, on the resources the target system exposes. The vocabulary is the same across systems, so subscribing to a created invoice looks identical whichever system your customer runs. The required parameters per system are documented on each target system page.

Build once on the Unified API.

See how unified webhooks works for your integration, or dive into the technical reference.