The Acumatica mobile app is not a separate application you develop — it's a generic shell that renders screens from metadata your instance serves. That single fact explains everything good and everything frustrating about customizing it. Good: you never touch Swift or Kotlin, your customizations deploy with the customization project, and any screen can become a mobile screen. Frustrating: you're writing MSDL (Mobile Site Map Definition Language), a declarative DSL with its own quirks, and the debugging loop is "edit, publish, pull-to-refresh, squint."
Here's how the pieces fit and the patterns I use after customizing mobile screens for field service, expense capture, and warehouse-adjacent flows.
How the mobile app actually works
When the app connects to your instance, it downloads the mobile site map — a tree of screens defined in MSDL that references the same containers, fields and actions the server-side screen exposes. The app renders natively from that metadata and talks to the server through the mobile API. This means the server remains the single source of truth: field visibility, validation, and business logic all execute server-side through the same graphs as the browser UI. A field your graph extension disables is disabled on mobile too, for free.
Customization happens in the customization project under Mobile Application: you add an update to an existing screen or define a new one, writing MSDL that patches the base definition.
MSDL in five minutes
The vocabulary is small: sitemap nodes add screens; a screen contains containers (mapping to the screen's data views); containers contain fields and layouts; screens and containers expose actions. Updates use add, update and remove against the base definition. A realistic example — trimming the expense receipt screen to essentials and surfacing a custom field:
update screen EP301020 {
update container "ExpenseReceipt" {
update field "Date" { displayName = "Receipt Date" }
add field "UsrSiteVisitID" { displayName = "Site Visit" }
remove field "ReferenceNbr"
remove field "Workgroup"
}
update container "ExpenseReceipt" {
add recordActionsToExpand { actions = ["Submit"] }
}
}
Note the custom field: UsrSiteVisitID only appears here because the DAC extension already added it server-side and it's present in the screen's web service/mobile metadata. MSDL places things; it never creates data.
Every successful mobile customization I've shipped was subtractive. The default mobile projections include far too many fields, and a technician filling a form on a phone abandons anything over roughly seven inputs. Start by removing; add only what the person in the field genuinely needs.
Attachments, signatures, scanning and location
The shell exposes device capabilities declaratively:
attachmentson a container enables the camera/photo flow — photos land as file attachments on the record's NoteID, same as browser uploads.- A signature is an attachment container with the signature flag; standard on field service appointments and easy to add to custom screens.
- Barcode scanning: mark a field with the scanner special type and the keyboard gains a camera-scan button that fills the field — enough for asset lookups and simple issue flows without dedicated scan-gun screens.
- GPS capture works through fields designated for location, which the app fills from the device — the backbone of any "prove the tech was on site" requirement.
The development loop and its gotchas
The loop is: edit MSDL in the customization project, publish, then in the app use the main menu's refresh (or log out/in) to pull the new site map. Gotchas that have cost me time:
- Container names come from the screen's mobile metadata, not the view names you see in C#. Check WebServices/mobile metadata via the screen editor's preview when a container name refuses to resolve.
- Actions must be visible on the server screen. An action your workflow hides in the current state won't render on mobile either — correct, but confusing when you forget workflow governs it.
- Version skew. After an Acumatica upgrade, base mobile definitions change; your updates usually merge fine, but screens occasionally get restructured (containers renamed), and your MSDL update targeting the old name silently no-ops. Re-verify mobile screens as part of every upgrade test pass.
- Offline is limited. The app needs connectivity for most operations; queued/offline behavior exists only for specific scenarios. Design flows assuming a connection, and handle dead zones procedurally (see my offline-mode post).
When to stop customizing the shell
MSDL covers field-level shaping of existing flows superbly. It does not do custom interaction patterns — a bespoke picking UI, an offline-first inspection checklist with conditional sections, anything with client-side logic. When requirements head there, the honest answer is a small companion app (I've used Flutter and plain PWA) against the contract-based REST API, reserving the official app for what it's good at. Fighting the metadata shell to fake a custom UX costs more than building the small app.
Wrapping up
Mobile customization in Acumatica is server-defined, declarative, and deploys like any other customization — learn the MSDL vocabulary, lean on the server for all logic, subtract aggressively, and wire in camera/signature/scan/GPS where they remove friction. Know the ceiling: field-shaping yes, bespoke interaction no, and a REST-based companion app when you cross that line.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.