App Backends
How an app registers a backend and takes over a role in the ERP.
Most apps only read and write ERP data. The apps in this space do something more: they take over a role in the ERP. To do that, an app registers a backend — a record in the ERP that says "for this domain, I am responsible".
The pattern
┌───────────┐ 1. install: register backend ┌───────────┐
│ Your App │ ──────────────────────────────► │ VARIO ERP │
│ │ │ │
│ │ ◄────────────────────────────── │ │
└───────────┘ 2. ERP calls back / notifies └───────────┘From then on
The ERP routes work in that domain to you — either by calling a URL you registered, or by firing a webhook.
Two things make the record yours:
appId
your appIdentifier
The ERP knows which app owns this backend, and you can find it again
type / backendType
APP
Distinguishes an app-provided backend from a built-in one
What each domain registers
Sales channel backend + sales channels
POST /community/{version}/erp/sales-channels/backend
The framework provides migration helpers for sales channel, bank and finance backends. Carrier types and POS payment backends are created with a plain API call.
How the ERP reaches you
This is the part that surprises people: for several domains the ERP calls into your app, synchronously. You register those URLs on the backend record itself.
Shipping
createShipmentSyncUrl, createLabelSyncUrl
A shipment is created / a label is requested
Banking
appTransactionUrl, appPaymentUrl, appStatusUrl
Transactions are fetched / a payment is sent / status is polled
Your app must be publicly reachable for these to work. The URL is built from your app's public host at install time — which means if that host changes, the stored URLs are stale. Every shipping app ships a later migration that rewrites them for exactly this reason.
What you register vs. what the customer creates
A frequent misunderstanding. Your app registers the technical infrastructure. The customer creates their own business objects on top of it:
Carrier type ("DHL App")
Carriers and shipping methods that use it
Bank backend ("PayPal")
Their banks, which select your backend
POS payment backend
The payment methods that point at it, and their assignment to registers
Sales channel backend ("Shopware 6")
Usually created by your app — one channel per store
So a shipping app never creates a carrier, and a banking app usually never creates a bank — it makes itself selectable. Don't create customer master data on install; the customer owns names, IBANs and BICs.
Deactivate, don't delete, on uninstall. Customer objects that reference your backend must survive an uninstall so they can be reactivated on reinstall. Deactivate in dependency order (children before parents) — the ERP validates references to inactive parents.
Migrations
Backends are created in a migration that runs on install. Two things you must understand:
Use always(key, callback) instead of setMigration for steps that must re-run on every install — webhook registration is the usual case.
A failed migration does not fail the install. Errors are caught, reported to your error handler and written to the app log — the install still reports success. If a backend seems to be missing, read the app log. Failed steps are not recorded as done, so they retry on the next install.
Order matters. Later steps that look up what earlier steps created will fail if the order is wrong — for example, an EAV group must exist before it can be read or changed, and a bank cannot reference a backend that isn't there yet.
Sandbox vs. production
There is no platform-level sandbox flag. Two approaches are used:
A second backend record whose label is suffixed
(Sandbox)— the shipping apps do this, and detect the mode from the label.A configuration value the user toggles — PayPal does this.
Configuration values round-trip through storage as strings. The string "false" is truthy in JavaScript, so compare explicitly (value === 'true') rather than relying on truthiness. This has caused real incidents where a sandbox flag sent live traffic to production.
Finding your backend again
You rarely keep the id. Query it back by your own app identifier — e.g. via VQL:
The framework's findSalesChannelBackend() does exactly this.
Checklist
Manifest declares the permissions your domain needs
Migration registers the backend with your
appIdand typeAPPCallback URLs point at your public host — and a later migration can refresh them
Webhooks registered with
always(), notsetMigration()Uninstall deactivates (never deletes) customer objects, children first
Sandbox mode explicit, with string-safe boolean checks
App log checked after install — failures are silent
Last updated
Was this helpful?