Magento ERP Integration: The Shape That Holds for Any ERP, and Where It Breaks
indexation: WAIVER unset
The short version
- A Magento ERP integration is three decisions, not one: which Magento record becomes which ERP record, how the data moves, and what the receiver does when the ERP stops answering.
- Orders and order lines land as sales orders and sales order lines on essentially any ERP. Customers, credit memos, stock sources and store views are the rows where projects lose their schedule.
- There are three transports and no fourth: a file dropped on SFTP, a scheduled API poll, or an event push. Anything sold as an integration platform is one of those three with operations wrapped around it.
- Your Magento edition decides whether you already have a sender. The webhooks framework and Adobe I/O Events are Adobe Commerce features; Magento Open Source ships neither, so on Open Source the sender is an observer plus a module, or the message queue.
sales_order_place_afterfires before the order is persisted, and the module snapshots the order at that moment rather than at delivery time.entity_idis null there andincrement_idis not, soincrement_idis the value you carry into the ERP.- Two sales orders in the ERP against one Magento order is the normal result of at-least-once delivery reaching a receiver that creates without looking first. One indexed reference field on the receiving side is the whole fix.
- Priced as a single project, the published implementation bands for this category start at $5,000 plus licensing. Priced as transport plus receiver, the transport is a one-time module and the receiver is developer time on your side.
What actually moves between Magento and your ERP?
Start at the record map, not the transport. ERP integration with Magento fails here first, and it fails quietly: the data flows, the cron jobs stay green, and six weeks later finance finds credit memos posting against nothing.
The first two columns hold for almost any ERP. The third column is where your timeline actually lives, and nobody publishes it, because in most rows the honest entry is go and find out.
| ERP COUNTERPART | WHERE IT STOPS BEING UNIVERSAL | |
|---|---|---|
| ORDER | Sales order | Whether the ERP will accept an order for a customer it has never seen. Some open the account on the fly, some reject the document outright. The answer decides whether customer sync has to run ahead of order sync. |
| ORDER LINE | Sales order line | Line-level discounts. Many ERPs have no line discount concept and want a net unit price, so you either send net and lose the discount audit trail, or send a discount line and reconcile totals by hand. |
| CUSTOMER | Customer, business partner, debtor, AR account | The widest naming gap in the table, and the reason a generic connector is hard to write. One Magento customer can also fan out into several ERP records: a master record, an AR account, and one or more ship-to addresses. |
| PRODUCT | Item, material, article | Which system owns the SKU. If the ERP owns it, Magento is a mirror and product creation runs one way only. If Magento owns it you need a create path into the ERP, and most pre-built connectors do not ship one. |
| INVOICE | AR invoice | Nearly always a transform of the sales order rather than an independent create. Ask whether your ERP will post an invoice with no order behind it. Several will not. |
| SHIPMENT | Delivery, fulfillment, goods issue | Whether posting a shipment moves inventory or only records tracking. If it moves stock, pushing a Magento shipment into the ERP can decrement the same quantity twice. |
| CREDIT MEMO | Credit note, sometimes preceded by a return authorization | Whether a return authorization has to exist before the credit can. It is an ERP-by-ERP answer, and it decides whether a Magento refund can post at all without a human in the loop. |
| STOCK SOURCE | Warehouse, location, plant | Magento stock sources rarely map one to one. Several physical locations behind a single ERP plant is normal, and the quantity that comes back is then a rollup, so Magento allocates against a number that does not exist anywhere. |
| CUSTOMER GROUP | Price list, price level, customer category | Whether price lives in the ERP or in Magento. If the ERP holds contract pricing, the group is only a pointer and the real price has to be fetched per customer somewhere in the funnel. |
| STORE VIEW | Usually nothing | The row that surprises people. Many ERPs have no store view concept at all, so it becomes a user-defined field or it disappears, and multi-store merchants find out which at reporting time. |
Ten rows, ten questions. Take them into your ERP vendor's documentation, come back with ten yes-or-no answers, and the project is scoped. Any mapping written before the ERP is named is a questionnaire, and presenting one as solved is how a connector gets built against one ERP's assumptions and then meets a return-authorization rule in production.
What are the three ways to connect Magento to an ERP?
Three transports, and every integration product is one of them with operations and a bill attached. Choose on the shape of the data movement. A vendor's category name describes its packaging.
| HOW THE DATA MOVES | LATENCY | WHAT IT REALLY COSTS TO OWN | WHEN IT IS THE RIGHT ANSWER | |
|---|---|---|---|---|
| FLAT-FILE DROP | CSV or XML written to SFTP on a schedule | The batch window, whatever it is set to | Cheapest to build, most expensive to run. Nobody watches a folder, and a malformed file stays silent until somebody asks where Tuesday's orders went. | Low order volume, a batch window the business can live with, and an ERP that genuinely cannot do anything else. |
| SCHEDULED API POLL | Cron asks Magento what changed since the last run | Equal to the poll interval, by definition | You pay for every empty poll, and the interval is your only lever on lag as well as your only lever on cost. | The ERP can only pull, or you have no endpoint it can call. |
| EVENT PUSH | Magento sends the record when the event fires | As fast as the receiver answers | A receiver that replies fast and works asynchronously, plus a defined place for failed deliveries to go. | A real-time requirement, high order volume, or an ERP that will accept an inbound call. |
Flat-file drop
Pick this when the ERP's integration surface is a folder. Against a system that will not talk, the alternative is a middleware license bought purely to do the translating, and the file is cheaper.
Flat files are primitive next to an API, and they have the worst failure ergonomics of the three: the drop succeeds, the ERP's import job chokes on row 412, and neither side raises anything. Budget for the monitoring you were planning to skip. A file that lands and is never read costs more than a delivery that fails loudly.
Scheduled API poll
Pick this when the ERP can only initiate outbound calls and your store has no endpoint it can reach, which is ordinary for an on-premise deployment behind a corporate firewall. In that shape, polling is not a design choice. It is the only direction the packets travel.
The cost is arithmetic. A five-minute interval is 1,440 minutes divided by five, so 288 requests a day per entity type, and on a normal weekday almost all of them come back empty while your store authenticates and serializes a response with nothing in it. Halve the interval to cut the lag and you double the bill. Leave it at five minutes and your warehouse hears about a rush order up to five minutes after the money moved.
Event push
Pick this when the ERP, or a receiver in front of it, can accept an inbound HTTP call. Magento fires the event, the payload leaves once, and there is no polling floor to argue about. The longer version of why a push beats a pull for state changes sits in webhooks vs API.
Push moves the operational burden onto you. It does. You now own a receiver that has to answer quickly, queue the work, survive an ERP outage and recognize a replay. It is still the right trade once the poll interval costs you time the business notices, because the alternative is paying the poll tax on every entity type forever and staying minutes behind.
The same three shapes apply to every system you have to feed, not only the ERP. The Magento integration hub takes it wider.
Which method is right for your store?
The network decides this before the business does. Work the last column of the table above from the top, then read these two panels against each other.
PANEL A - scheduled poll
[ ERP or middleware ] --(1) cron fires every N minutes--> [ Magento REST API ]
<--(2) "anything changed since T?"---
<--(3) nothing. nothing. nothing. one sales order.
The interval is the lag. There is no second lever on it.
Your store pays for the question whether or not there is an answer.PANEL B - event push
[ Magento ] --(1) sales_order_place_after fires--> [ queue ]
--(2) POST the persisted order--------> [ your receiver ]
|
(3) answer 200 first, process after
|
v
[ generic ERP: sales order ]
keyed on the ERP's external-reference field
Failed delivery: the sender retries a fixed number of times, then stops.
The replay path is yours to build.
ERP offline: checkout still completes, the queue grows, nothing is lost.One question the table cannot answer for you: does anything downstream care about minutes? Warehouse cutoffs, credit holds and available-to-promise care. Nightly financial posting does not. If nothing downstream cares about minutes, a nightly batch is the cheaper system to operate and choosing it is not a failure of ambition.
What does a Magento order look like when it reaches your ERP?
sales_order_place_after fires before the order is saved, and plenty of integrations are built on the opposite assumption.
Magento\Sales\Model\Order::place() dispatches it. That method fires sales_order_place_before, runs payment placement, fires sales_order_place_after, and returns without persisting anything. The repository save happens somewhere else entirely, in Magento\Sales\Model\Service\OrderService::place(), which dispatches no event at all.
So at the instant the event fires, entity_id is unassigned and so is every per-item item_id. increment_id is populated, because the sequence assigns it during quote-to-order conversion. Key off entity_id inside the observer and you key off null.
The module does not leave that to inference. Its payloads carry the entity as it stood at the moment the event fired, snapshotted when the observer runs rather than re-read when the delivery goes out. That is the documented mechanism behind the key choice: increment_id exists at the instant of the snapshot and entity_id does not.
Build the receiver around what a snapshot means. Minutes can pass between the fire and a successful post, and if the order moved in that window, the body that lands describes where the order was, not where it is. Nothing refreshes it in flight. triggered_at is the timestamp your ERP reconciles against, and a receiver written on the assumption that every payload holds current state will pull the two systems apart one field at a time.
The block below is the OCM Labs module's stock Order Placed template rendered against an invented order. Two things about it surprise people who expect a serialized entity. It is a curated subset rather than a dump of the sales order table, and entity_id is not among the fields it emits, so a receiver keyed on entity_id is running against a template somebody edited rather than against the product. Field names are real Magento 2 sales order fields; the values are made up. The template language, the modifiers and the full variable list are in the payload template reference.
{
"event": "sales_order_place_after",
"triggered_at": "2026-07-31T09:41:16+00:00",
"order": {
"increment_id": "000000731",
"grand_total": 1045.20,
"currency": "USD",
"customer_email": "purchasing@example.com",
"items": [
{
"sku": "CTN-1210-KRAFT",
"name": "Kraft Shipping Carton, 12 x 10 x 6 in",
"qty": 200,
"row_total": 618.00
},
{
"sku": "TAPE-PP-48-CLR",
"name": "Polypropylene Carton Tape, 48 mm, Clear",
"qty": 48,
"row_total": 427.20
}
]
}
}Six scalar fields and a line array of four each. That is fewer than most integration plans assume, and more than most of them end up using. Two of the six decide whether the ERP side reconciles. increment_id is what you write into the ERP's external-reference field, because it is the number your customer service team reads back to you off a ticket. currency is the code the customer actually paid in and grand_total is denominated in it, so a merchant selling in more than one currency has to settle on the receiving side whether the ERP converts on arrival or stores the transaction currency as sent. Getting that decision wrong is quiet: the documents post, and the revenue report is wrong by an exchange rate.
What is not in the body at all is the part that proves the delivery. The signature and the delivery id travel as headers, with three more alongside them worth writing to your log:
| WHAT IT CARRIES | |
|---|---|
| X-WEBHOOK-SIGNATURE | sha256=<hmac>, computed over the exact bytes of the request body. Omitted entirely when the webhook has no secret configured. |
| X-WEBHOOK-DELIVERY-ID | <uuid>. One logical delivery keeps the same value across the original send and every retry of it, which is what makes it usable as a dedupe key. |
| X-WEBHOOK-EVENT | the event name |
| X-WEBHOOK-ID | the subscription id |
| X-WEBHOOK-ATTEMPT | the attempt number |
Compare the signature in constant time, hash_equals() rather than ==, and do that before you look at the delivery id, never after. Verifying the signature on the receiver is documented with the reasoning.
Does Magento Open Source get webhooks, or is that Adobe Commerce only?
Adobe Commerce only, and on an ERP project this is the first fork in the road. The transport argument assumes you can already send. Whether you can send without building the sender is an edition question, and it gets settled before the architecture diagram rather than after.
Both the webhooks framework and Adobe I/O Events are Adobe Commerce features. Magento Open Source ships neither, and Adobe's own installation documentation for eventing states that Magento Open Source is not supported. Inside Adobe Commerce the split is finer than most integration plans allow for: the eventing modules load automatically from 2.4.6, while the webhooks framework is still a Composer install you run yourself.
| PACKAGE | WHAT IT NEEDS TO RUN | |
|---|---|---|
| WEBHOOKS FRAMEWORK | magento/commerce-webhooks | Adobe Commerce 2.4.4 or higher, Cloud or on-premises, installed with Composer. It reached 1.0.0 in October 2023, so it is younger than most of the integration advice written about it. |
| ADOBE I/O EVENTS | magento/commerce-eventing | Adobe Commerce 2.4.4 or higher. A Composer install on the earlier 2.4 releases, loaded automatically from 2.4.6. |
| EVENT PUSH ON OPEN SOURCE | Nothing to install. You write it. | Any 2.4 release. An observer bound to the event plus a module that delivers, or a publisher onto the message queue and a consumer that posts. |
Forward that documentation line to whoever wrote your integration plan before the build starts.
Here is why the edition bites on an ERP project specifically. The plan gets written from documentation that says Magento while describing Adobe Commerce, and the ERP half of it is written by people who have never had reason to learn the difference. The design reviews fine. The sequence diagram is right. Then the build lands on an Open Source instance and the box marked webhook turns out to be a module nobody scoped, discovered in the week the ERP consultant is already booked.
Adobe Commerce gives you the sender and stops there, and that is the part an upgrade pitch leaves out. Something at the far end still has to accept the call, answer fast, map the record onto your ERP's document, recognize a replay and keep working while the ERP is down. No edition of Magento does any of that for you, and that half is where the project time goes.
What breaks in production?
Not the design. The design is usually fine. What breaks is the running system at 2pm on a Tuesday, and it breaks in a small enough set of ways to tabulate.
| WHAT THE MERCHANT OR THE CUSTOMER SEES | WHAT YOU OWN | |
|---|---|---|
| RETRIES | Nothing, until a delivery gives up quietly and one order never reaches the ERP | Which status codes count as temporary, and where a delivery goes after the last attempt |
| DUPLICATE DELIVERY | Two identical sales orders in the ERP, both picked, both invoiced | Nothing on the sender side. The second delivery is the guarantee working as specified |
| IDEMPOTENCY | Whether that second delivery becomes a second document | The lookup the receiver runs before it creates, and the field it looks in |
| ERP DOWN MID-CHECKOUT | Nothing, if you built it right. A storefront that cannot take money, if you did not | Checkout's independence from the ERP, and the drain rate when the ERP comes back |
| CONSUMER PROCESS DEATH | Nothing for days, then a merchant asking why the ERP has seen no orders since Tuesday | A supervisor on the process, plus an alert on queue depth and on message age |
| OUT-OF-ORDER DELIVERY | A shipment or an invoice sitting in the ERP with no parent, reconciling to nothing | Whether the receiver buffers the orphan or rejects it and waits for the retry |
Retries
Retry a 5xx, do not retry most 4xx, and carve out the 429 before you ship either rule. A 5xx says the receiver's own side broke, and the same request may well land on the next attempt. Most 4xx responses say the request itself is wrong, so a second identical request buys a second identical rejection. A 429 makes a different claim: you hit a ceiling, the payload was fine, and the delivery needs another attempt behind a real backoff, because a concurrency or rate ceiling is what an ERP runs into during a flash sale. Write a blanket never-retry-a-4xx rule into your receiver and you have written a rule that discards orders on your busiest day of the year.
Find your ERP's ceiling before you write the policy, and find it in a sandbox company or a test account rather than on the production instance. Push more concurrent posts at it than it is provisioned to take and read what comes back. Some answer 429 with a wait hint. Some answer 503. Some accept every call and drop the overflow without saying so, which is the worst of the three, because your receiver logs a success for an order the ERP never kept. Run that test against live finance data and you create the corruption this section is about.
Then decide where the retries stop on your side, because the sender's schedule and your policy are two different objects and only one of them is yours to set. It is three pieces of architecture: a dead-letter destination, an alert on the depth of it, and a replay path keyed exactly the way your dedupe path is keyed. On a multi-store install that means store_id plus increment_id. Skip the destination and the queue grows without a ceiling, and a queue that only grows keeps working right up until the broker decides the matter for you. Skip the replay path and you can name yesterday's failed deliveries without being able to resend them. Some ERPs help here by handing back a document number or a validation message you can route on. Plenty tell you nothing useful about why a post failed, so you define the ending yourself.
What counts as a failure is worth pinning down first, and it is not only what comes back in the response. Each attempt gets 10 seconds. A receiver still working at second eleven has failed that attempt exactly as a refused request would, and the attempt is spent. On an ERP integration that is the ordinary case rather than the edge one: post into the ERP inline, before you answer, and the ERP's own document-create latency now sits inside a ten-second window that belongs to your endpoint. Panel B answers 200 before it does the work, and the ten seconds is why.
The OCM Labs module publishes its own numbers, so here they are rather than a paraphrase. A failed delivery is rescheduled on delay = retry_base_delay * 2^(failed_attempt - 1), the base defaults to 60 seconds, and the ceiling is 3 attempts with the original send counted. Miss once and the next go is a minute out. Miss again and it is two. Miss the third and nothing further is scheduled. Add those up and the sender is done with that delivery inside about three minutes, so size your receiver's availability against three minutes and not against an afternoon. A cron sweeps once a minute and republishes whatever has come due, which makes each interval a floor rather than an alarm clock. Timeout, base and ceiling are all editable at Stores > Configuration > OCM Labs > Webhooks > Delivery, so a receiver fronting a slow ERP has somewhere to go. One class of failure is excluded on purpose: a template that will not render fails identically on every attempt, so the module spends no attempts on it.
Then it ends, and the ending is blunt. There is no dead-letter queue, no alert and no automatic replay. The delivery is marked Failed in the Delivery Log and the module has finished with it. Nothing is going to hand that order back to you later, which is why the three pieces above live on your side and not on the sender's. The full mechanics are in the module's delivery documentation.
Duplicate delivery
At-least-once delivery is the contract you are running under, and what it promises is that the same event can reach your receiver more than once. Not on some unlucky afternoon: it happens on every request that times out after the receiver already finished the work and the acknowledgement failed to get home. From where the sender sits, "it never arrived" and "it arrived and the reply was lost" look identical, so it does the only safe thing open to it and sends again.
No setting turns that second delivery off, and none should. What turns a second delivery into a second sales order is a receiver that creates without checking first. Then the ERP holds two documents against one Magento order, both legitimate to a picker, both invoiced, and the person who notices is usually the customer signing for the second shipment. The redelivery is the sender behaving correctly. The duplicate record is yours.
Idempotency
Write the Magento increment_id into the ERP's external-reference field, whatever your system calls it, and query or upsert against that field before you create. A replayed delivery then updates the sales order that already exists instead of adding a second one. That is the fix, and it is one field.
Settle the scope of the key before you trust it. increment_id is unique per store sequence, not globally, so on a single-store install it stands on its own and on a multi-store install it does not. There either key the receiver on store_id plus increment_id, or confirm that the increment prefix on every store view is genuinely distinct before you treat the number as unique across the estate. The stock template carries no store, and {$meta.store_id} exists for exactly this, so add it to the template and give your own schema a column to put it in.
Some ERPs have no external-reference field on the sales order at all. Then you hold the mapping yourself, and the order of the writes is the entire mechanism, because a local database transaction cannot reach across an HTTP call:
- Insert your mapping row first, under a unique constraint on
increment_id, or onstore_idplusincrement_idwhere you need the scope. Status pending, ERP document number empty. - Post to the ERP.
- Update the row with the document number the ERP handed back, and mark it done.
A replay now collides with that unique constraint at step 1, and the collision is information rather than an error: a create was already attempted for this order. If the row reads done, acknowledge the delivery and create nothing. If it still reads pending, the previous attempt died between the post and the update, so nobody knows yet whether the ERP kept the document. That one goes to reconciliation. Query the ERP by reference if it can be queried, and park it for a human if it cannot. What you do not do is post it again, which is exactly what a receiver that only asks the ERP will do when the ERP has nothing to be asked.
Decide this before the first order goes through either way. Retrofitting deduplication onto an ERP that already holds duplicates is a data-cleanup project with an accountant attached.
The ERP is down mid-checkout
What does the customer see? Nothing, and that requirement does not bend: checkout has to complete without the ERP's participation. Couple the two and every patch window your finance team books becomes an hour the storefront cannot take money. You get to have that conversation with a merchant exactly once.
So the order is placed, the confirmation goes out, and the delivery lands in a queue that retries against a receiver with nothing answering behind it. The part that decides whether you survive is what the queue does meanwhile. Say the ERP is offline from 2pm to 6pm and 300 orders land in that window. Those messages accumulate. When the ERP comes back, the whole backlog releases at once into a system that has just restarted and is slower than usual, and the recovery becomes the second outage.
Drain rate is an architecture choice, so make it one. Run a fixed number of consumers rather than as many as the box will start, and give the receiver a hard concurrency limit on its calls into the ERP so the queue absorbs the difference instead of the ERP absorbing it. Bounding each consumer run with --max-messages hands the process back to your supervisor on a known cadence instead of letting one long-lived worker sprint through the backlog. Send the backlog in an order that makes sense too, because an ERP receiving Thursday's orders ahead of Wednesday's will not complain, it will just number them wrong. For catch-up work in the other direction, Magento's asynchronous bulk endpoints under /async/bulk/V1/ take the write off the request thread, so a stock-level resync after an outage does not queue behind live traffic.
The consumer dies at 2am
bin/magento queue:consumers:start is a process, and processes die. With nothing supervising it and nothing alerting on queue depth, the first anyone hears is a merchant asking on Thursday why nothing has reached the ERP since Tuesday. This is the least sophisticated failure on the list and the one most likely to be yours. Put the consumer under a supervisor, alert on queue depth and on the age of the oldest message, then check that the alert has ever actually fired.
Out-of-order delivery
The shipment arrives before the order it belongs to. In a queued system the receiver has to decide that in code: hold the child until its parent turns up, or reject it and let the retry bring it back once the parent exists. The same choice lands one level higher when an order arrives before the customer record it points at. Row one of the mapping table asks whether your ERP will take that order at all, and this is the same question answered as receiver behavior rather than as a vendor capability. Holding is usually right and is always more code than whoever scoped it allowed for. Choose on purpose, because the behavior you get by not choosing is an orphan record that nothing will ever reconcile.
Which ERP are you connecting?
The ERP you run does not change the three transports. It changes how many rows of the mapping table are answered before you start.
- NetSuite. The record carries a standard external-reference field,
externalId, so idempotency is close to a solved question and the real decisions are which ingress API you post to and how you stay inside its request limits. Full treatment on Magento NetSuite integration. - SAP. S/4HANA, ECC and Business One are three different integrations sharing one brand, and which one you are on decides nearly everything downstream of it. Full treatment on Magento SAP integration.
- Microsoft Dynamics 365, Sage, QuickBooks. Different surfaces, same three transports. Put the ten questions to whoever administers the system today, starting with unknown-customer handling, line-level discounts, and whether a credit needs a return authorization in front of it.
- Anything else, including the in-house system that appears on nobody's supported list. Ten answers and you know the size of the job.
For the other systems that feed Magento 2 across these same three transports, start at the Magento integration overview.
What does Magento ERP integration cost?
Price a Magento 2 ERP integration in two parts: the transport that carries the record, and the receiver that accepts it. The published implementation bands below price both together, as one project.
| PUBLISHED BAND | WHAT YOU STILL OWN | |
|---|---|---|
| PRE-BUILT CONNECTOR | $5,000 to $30,000 plus license | The ERP-side mapping decisions, and the license for as long as you run it. |
| IPAAS BUILD | $15,000 to $80,000 plus subscription | The mapping, the flows built on top of it, and a subscription that tracks your volume. |
| INTEGRATION STARTER KIT | $20,000 to $60,000 | The receiving side and every ERP-side decision, same as the others. |
| CUSTOM API-LED | $50,000 to $250,000 and up | All of it, in a codebase your team did not write. |
Those are published implementation bands for the category. They are not quotes and they are not ours. The lowest band in that set buys the transport plus somebody else's opinion about the mapping.
Now the push-based version, with the arithmetic shown.
The transport is a module. The OCM Labs Magento 2 Webhooks module is $250, one time. Three years on it is still that same one-time figure, because nothing about it is metered or renewed.
The receiver is developer time, and it is the line that actually varies. It is an HTTP endpoint that answers fast, writes to a queue, maps the order onto your ERP's sales order and upserts on the external-reference field. The queue can be whatever broker your stack already runs. Its failed deliveries need a terminus you own: a table holding the raw body, the response the ERP gave, and a retry column somebody can query. If you already run a service that can host that, you are adding an endpoint to something that exists. If you do not, you are standing up and operating a small service, and that number depends on your team in ways nobody outside it can price.
Weighing a monthly connector instead: the middle of three published monthly tiers, $99, $249 and $699, works out to $249 x 12, or $2,988, in the first year, and $249 x 36, or $8,964, across three years, with nothing owned at the end of it.
So the comparison against custom middleware is a comparison of what you end up owning. A custom API-led build puts the transport, the mapping, the retry logic and the operations into one bespoke codebase, from $50,000 up. The one-time module takes the transport off the bill, and a receiver you host takes the subscription off it. The mapping, the receiver's operations and whoever carries the pager stay on the bill exactly where they were before you shopped.
When is a connector subscription the better buy?
When you have no endpoint and nobody to build one. That case is common and it is not a lesser case.
A monthly connector includes the receiving side. Somebody else operates the mapping, and somebody else gets paged when the ERP returns 429 during a flash sale. If your team is one developer who is already the reason releases ship, buying that is the right call, and the module is the wrong product for you.
The same holds if your ERP sits on-premise with no inbound path and no appetite for opening one, or if what you actually need is a person who knows your ERP and not a delivery mechanism.
Where it flips: the moment you have a service that can host an endpoint and a developer who can maintain a mapping, the subscription is renting the cheapest part of the system. The difficulty lives in the mapping and the failure handling, and no connector removes either. It moves whose name is on them.
The test is cheap and it is not technical. Name the person who owns the receiver on the Monday after launch. If that name does not exist, buy the subscription.
We have built one of these for a major beauty supplier that moved off a series of spreadsheets. The name is not ours to print, so the sector and the outcome are what you get.
Tell us which ERP you are on and what it currently refuses to do. You get back the ten questions your ERP vendor has to answer before any of this can be scoped, and a straight answer on whether a connector subscription beats anything we sell.
✦ QUESTIONS MERCHANTS ASK