Hi @TimRodman, great question, and you’ve put me into sleepless night!
To find the answer, I decompiled the actual binaries of build 26.100 (PX.Data.dll, PX.LicensePolicy.dll, PX.PushNotifications.dll, PX.Api.Webhooks.dll and friends) with dnSpy and traced both the push notification pipeline and the license enforcement engine end to end, then cross-checked against the official docs.
TL;DR: Processing data with Push Notifications does not increment any of the counters on the License Monitoring Console (SM604000) - not Web Services API Requests per Minute, not Commercial Transactions, not ERP Transactions. The data change that triggers a notification is licensed on its own merits, exactly as it would be with push notifications turned off; the notification machinery itself adds zero license consumption. A few nuances are worth knowing, though.
1. Web Services API Requests per Minute — not consumed
The per-minute limit is enforced by site-wide OWIN middleware that classifies inbound HTTP requests purely by URL prefix (PX.Licensing.ApiRequestHelpers.IsApiRequest). Only these paths count:
~/entity/… — contract-based REST/SOAP (with carve-outs for swagger.json, the endpoint list, long-operation status polls, and DeviceHub)
~/soap/… — screen-based SOAP (non-GET)
~/Api/… (except ~/Api/commercehook)
~/Webhooks/…
~/CustomizationApi/…
A push notification travels in the opposite direction: it is an outbound HttpClient POST executed by a background dispatcher thread (NotificationPrimaryQueueDispatcher - HttpClientPushNotificationSender) with no HttpContext, no session, and no API sign-in. It never enters the inbound pipeline where the counter lives, so there is nothing to count. The dispatcher runs under an internal system login scope that consumes no Concurrent User or API User seat either, and SignalR/mobile-type targets are in-process broadcasts - same story.
Two related facts that fall out of the same classifier:
OData paths (/odata, /odatav4, /t/{tenant}/api/odata/dac) are absent from the list — code-level confirmation of your OData point. Dmitrii Naumov has confirmed the same on the community ("It's indeed the case that the API requests per minute limit is not applied there"). One nuance from the official I300 course: an OData v3 sign-in is treated as a conventional user sign-in, so it can occupy a Concurrent Users seat rather than an API-user seat.
Inbound Webhooks (SM304000, ~/Webhooks/…) DO count against API Requests per Minute. So in an architecture like Acumatica - push notification - your app - callback into Acumatica REST, the push leg is free but the callbacks are licensed as normal API calls.
2. Maximum Number of Commercial Transactions - not consumed
The commercial transaction counter increments in exactly one place in the entire codebase: a hook on a successful physical database INSERT into a hard-coded whitelist of eight document tables - SOOrder, SOShipment, POOrder, POReceipt, APInvoice, ARInvoice, APPayment, ARPayment. Push notification processing writes only to its own plumbing (the notification queue/broker plus PushNotificationsFailedToSend, PushNotificationsErrors, DispatcherSettings, and statistics tables). None of those are whitelisted, so the count is zero. And of course pure reads - the GI/BQL queries the pipeline runs to build payloads - touch no counter at all.
3. Maximum Number of ERP Transactions - not consumed by the pipeline
ERP transactions are registered exclusively at PXGraph.Persist time, and only when the graph maps to a visible screen with a primary DAC (system screens like the License Monitoring Console itself, import scenarios, schedulers, etc. are explicitly excluded). The push pipeline's own bookkeeping uses raw PXDatabase.Insert/Update/Delete calls, which never register ERP transactions, and the couple of true graph persists it performs (dispatcher statistics, the optional dead-message log) fail the counting filters because those graphs have no screen or the DAC isn't any graph's primary DAC.
So the ERP transaction is the entity change that triggered the notification — one count, attributed to whichever channel made it (UI screen or API endpoint) — and the notification adds nothing on top.
Two honest footnotes: manually saving a definition on SM302000, and manually re-sending failures on Process Push Notification Failures (SM502000), register ERP-transaction statistics like any other screen save (SM502000 registers one per re-sent notification). That's user-initiated configuration/recovery activity, not notification volume — and keep in mind ERP transaction figures on SM604000 are monitoring statistics, not a hard-enforced throttle.
4. The one place licensing actually touches push notifications
The payload processors cap the number of rows a notification query may return at the license's SerialsPerDocument constraint × 5 (10,000 rows when the license doesn't define it). It's a read-only lookup — no counter is incremented - but if a data change would produce a bigger result set, the notification fails with "Data for the push notification could not be selected because the data set is too large." One more reason to keep push notification GIs narrow, which Acumatica's performance guidance recommends anyway (the trigger-side capture runs inside the transaction of every save against tracked tables).
5. "Not counted" doesn’t mean "invisible"
For SaaS customers, the Acumatica Licensing Guide (October 2024, p. 15, "Acumatica SaaS Fair Use Policy") explicitly lists push notifications among monitored resource-usage factors: "Acumatica monitors customer resource usage patterns across many factors, including, but not limited to, ERP and commercial transactional counts, API integrations, batch processes, business events, push notifications, import/export scenarios, customizations, 3rd party solutions, and other interfaces." There's no hard counter on SM604000, but truly abusive volume is visible to Acumatica and can trigger a fair-use conversation. (The fact that the policy lists push notifications alongside transactional counts is itself a nice confirmation that they aren't inside those counts.) The practical constraint is app-server CPU and the health of your dispatcher queue, not licensing.