Delivered remotely for a business based in Oslo, Norway. Names withheld by agreement.
Project Overview
Industry: Environmental and utility field services — scheduled inspection and maintenance of property-based treatment systems.
Type of solution: A field service management platform: a REST API with a multi-tenant administrative back office, and a cross-platform mobile application built for offline use by field technicians.
Business context: Properties with on-site treatment systems require inspection on a recurring cycle, and each inspection must produce a technical record that goes to the property owner and to the recipients the operator is obliged to inform. The operator holds a book of such properties spread across a large service area. Historically the work ran on printed job sheets and handwritten forms: a technician drove to a property, completed the form by hand, photographed the installation, and returned to an office where somebody re-keyed everything and sent the report on. It worked, but it was slow, it produced no reliable evidence that a visit had occurred, and it broke down entirely when the office and the field disagreed about what had been done.
General users: Field technicians (mobile only), operator administrators managing scheduling, dispatch and reporting, and platform administrators with visibility across operator tenants. Property owners participate without accounts — they receive appointment reminders and finished reports directly.
General purpose: To move the entire inspection cycle onto one system — scheduling, dispatch, evidence-backed field capture that works without connectivity, and automatic report generation and distribution — so that the record of an inspection is created once, at the property, by the person who did the work.
The Business Challenge
The work happens where the network is not. Properties requiring these inspections are disproportionately rural. Mobile coverage at the point of work ranges from poor to absent. Any application that assumes it can reach a server at the moment a technician presses submit will fail at the only moment that matters — and the technician, having lost half an hour of form entry, will go back to paper permanently.
There was no proof anybody attended. A completed form asserts that an inspection happened. It does not evidence it. When a finding is disputed months later, the operator needs something better than a signature on a page that could have been completed anywhere.
The form is long and the conditions are bad. A full inspection covers equipment condition across several components, treatment-quality readings, whether remedial work was carried out, written observations, multiple photographs and a technician signature. It is completed outdoors, often standing, sometimes in weather, on a phone.
Reports were produced by hand and arrived late. Each completed inspection has to reach several parties: the property owner, the operator's own records, and the recipients associated with the property's service area. Assembling and sending that by hand is slow, inconsistent, and the first thing to slip when the office is busy.
Wasted journeys cost real money. Technicians regularly arrive to find a locked gate or loose animals and cannot carry out the inspection. Every such visit is a return journey across a rural service area for nothing.
Scheduling lived in spreadsheets. The operator's forward schedule was maintained in spreadsheets, as most field operators' schedules are. Demanding that it be re-entered by hand into a new system would have guaranteed the new system was ignored.
One platform, more than one operator brand. The product had to serve multiple operator tenants — each with its own branding, its own technicians, its own service areas and its own recipients — without maintaining a separate mobile application per operator.
Our Approach
Build the mobile application offline-first, not offline-tolerant. We did not bolt a retry queue onto an online form. The device carries a real relational database whose tables mirror the inspection record, the arrival confirmations and the photo attachments. A technician can start a form, leave it, come back, edit it, add photographs and a signature, and submit hours later from a car park with signal — all without the application having contacted a server once. A dedicated screen lists everything captured but not yet transmitted, so nothing sits invisibly in a queue.
Verify arrival, but do not block the technician. The application compares the device's position against the property's geocoded coordinates. Within the expected proximity, arrival is confirmed automatically and silently. Outside it, the technician is not prevented from working — instead they are required to capture a photograph, which is stored with coordinates and a server timestamp as evidence of the visit. This matters because rural address geocoding is genuinely unreliable, and a hard geofence would have stopped legitimate work at real properties. Every visit produces an evidence record either way.
Make the report the deliverable, not a portal. A property owner needs one document, once a cycle. Building them an account to log into would have been building something nobody would use. On submission, the platform renders the completed inspection into a formal PDF and emails it to a recipient list derived from the job itself — the property owner's addresses, the operator's own records, and the recipients configured for that property's service area.
Meet the operator where their schedule already lives. Rather than requiring manual entry, the back office ingests the operator's scheduling spreadsheets directly, normalising inconsistent date formats, matching technicians by name, resolving service areas, detecting duplicates against existing work, and geocoding each imported property address to coordinates so the map and arrival verification work immediately.
Reduce wasted journeys with automated reminders. A scheduled daily process messages the customers whose appointments fall the next day, asking them to unlock gates and secure animals, with opt-out wording and a guard preventing duplicate messages against the same job. Technicians can also trigger a reminder on demand. Failed sends are logged with the provider's error, so a customer who says they were never notified can be answered from data rather than memory.
Make branding a data record, not a build. Each operator tenant carries a theme record covering the application's interface colours and logo. The mobile client fetches it at launch and applies it globally. Onboarding a new operator brand is configuration, not a release.
Design for degradation everywhere it can degrade. Media uploads to cloud object storage record, per file, where the file actually landed — and fall back to local disk if the upload fails rather than failing the technician's submission. The read path branches on that marker. A storage incident becomes a background problem instead of a field problem.
The Solution
Work order management. Jobs carry property owner details, full address, service area, contact number, deadline, assigned technician and notes, with addresses geocoded to coordinates automatically on creation and update.
Bulk schedule ingestion. Operator spreadsheets are imported directly, with date normalisation across formats, technician and service-area matching, duplicate detection against existing work, and per-row geocoding.
Dispatch and assignment. Technicians are assigned at job creation, individually from the job list, or in bulk across many jobs at once, with reassignment triggering an immediate notification to the technician now responsible.
Operational queues. Separate administrative views for upcoming, completed and overdue work, with a dashboard summarising each state.
Map-based job view. All of a technician's assigned jobs are plotted as markers with automatic bounds fitting, so a day's work can be read geographically rather than as a list.
Geo-verified arrival. Distance between the technician's position and the property is computed on the device. Arrival within the expected proximity confirms automatically; beyond it, photographic evidence is captured and stored with coordinates and timestamp.
Structured inspection capture. A multi-section form covering inspection type, equipment condition across several components, treatment-quality readings, whether remedial work was carried out, written observations, multiple photographs and an on-screen technician signature.
Offline capture and deferred submission. The complete form, its photographs and its signature persist to an on-device relational store and can be edited and submitted later, with a dedicated queue screen listing everything not yet transmitted.
Automated report generation. Completed inspections render server-side into a formal PDF matching the operator's report layout, downloadable at any time from the back office.
Automatic multi-recipient distribution. Each report is emailed on submission to a recipient list derived from the job — the property owner's addresses, the operator's records, and the recipients configured for that service area.
Customer reminder messaging. Scheduled daily SMS reminders for next-day appointments plus on-demand sends from the mobile application, with number validation, opt-out wording, duplicate-send protection and delivery failure logging.
Notifications. In-app notification records with read state and deep linking into the relevant job, delivered to the technician's device as push notifications and handled in foreground, background and cold-start states on both platforms.
Multi-tenant white-labelling. Every operational record is scoped to an operator tenant, each with its own theme record driving the mobile application's appearance, its own technicians, service areas and distribution recipients — with platform administrators retaining cross-tenant visibility.
Administrative platform. Management of jobs, technicians and their certification records, service areas and their recipients, operator tenants, themes, branding and global settings, with report retrieval and submitted-form review.
Key Features
Offline-first inspection capture A device-local relational store mirrors the inspection record, arrival confirmations and photo attachments, so a full inspection can be captured, edited and revisited with no connectivity, then submitted when signal returns.
Visible deferred-submission queue Everything captured but not yet transmitted appears on a dedicated screen where it can be reviewed, edited and resubmitted — the queue is browsable rather than opaque, which is what makes technicians trust it.
Proximity-based arrival verification with photographic fallback Arrival is confirmed automatically when the device is near the property; outside that range the technician captures a photograph recorded with coordinates and a server timestamp. Every visit produces evidence, and no legitimate visit is blocked by imperfect geocoding.
Automated report generation and distribution Completed inspections render into a formal PDF and dispatch automatically to a recipient list derived per job, removing the manual assembly-and-send step entirely.
Bulk schedule ingestion from spreadsheets Operator scheduling spreadsheets import directly with format normalisation, entity matching, duplicate detection and automatic geocoding of every property address.
Map-based day planning Assigned jobs plotted with automatic bounds fitting, with on-device distance calculation between the technician and each property.
Automated appointment reminders with delivery logging Scheduled next-day customer messaging with number validation, opt-out handling, duplicate-send protection and persisted delivery failures — directly targeting the cost of wasted journeys.
Server-driven white-label theming Operator branding is a data record applied to the mobile application at launch, so adding a new operator brand requires configuration rather than a new build.
Evidence-complete inspection records Equipment condition, quality readings, remedial work, written observations, photographs, an on-screen technician signature, arrival evidence and a server-side timestamp, all attached to a single record.
Tenant-scoped access with cross-tenant administration Every operational query is scoped to the operator tenant, with a separate platform administration level able to see across all of them.
Technical Architecture
Mobile client. A cross-platform application composed through drawer, stack and tab navigators, with global state in a predictable store. Its defining component is a device-local relational database whose schema mirrors the server-side inspection record — not a serialised cache, but real tables supporting query, edit and delete, which is what makes the deferred queue usable. Device capabilities include mapping with marker clustering and bounds fitting, geolocation and distance computation, camera and gallery capture with cropping, on-screen signature capture, connectivity detection and push notification handling across foreground, background and cold-start states. Theming is applied from server-supplied values at launch.
API layer. A token-authenticated REST API over OAuth2 bearer tokens, with controllers partitioned into a mobile API tree and an administrative tree over separate base classes providing a uniform response envelope and consistent messaging. Tenant scoping is applied at query level, conditionally relaxed for platform administrators.
Administrative back office. A server-rendered administrative platform sharing the same application and data layer, behind a session guard with an explicit role gate preventing field technicians from reaching it.
Document and distribution layer. Server-side PDF rendering from a templated report layout, with recipient resolution per job across property owner addresses, operator record addresses and service-area recipients, dispatched over SMTP.
Scheduled processing. A daily console command resolving next-day appointments and dispatching customer reminders through the messaging provider, with failures persisted as structured records.
Media layer. Uploads resized and compressed, pushed to cloud object storage, with a per-record marker capturing where each file actually landed and a read path that branches on it — so an upload failure degrades to local disk rather than failing the write.
Data layer. A relational database covering jobs, inspections, arrival confirmations, notifications, messaging logs, users and certifications, operator tenants and their themes, service areas and distribution recipients, evolved through a multi-year migration history.
Delivery pipeline. Continuous integration on the default branch driving an automated release with database migration, cache management and application server restart.
Flow: Offline mobile capture (device-local store) → Deferred submission queue → Token-authenticated API → Tenant-scoped data layer → PDF report generation → Multi-recipient distribution; alongside Back office scheduling and spreadsheet ingestion → Address geocoding → Dispatch → Push notification to technician → Scheduled customer reminder messaging
Technology Stack
| Category | Technology |
|---|---|
| Backend language | PHP |
| Backend framework | Laravel |
| Database | MySQL with Doctrine DBAL, multi-year migration history |
| API authentication | Laravel Passport (OAuth2 bearer tokens) |
| Back office | Server-rendered Blade templates with a session guard |
| Query caching | Model-level caching layer |
| Document generation | Server-side PDF rendering from templated layouts |
| Spreadsheet ingestion | Excel/CSV import with format normalisation and entity matching |
| SMS messaging | Twilio, with dedicated phone number parsing and validation |
| Push notifications | Firebase Cloud Messaging via service-account authentication |
| Object storage | Google Cloud Storage with per-record fallback to local disk |
| Geocoding & mapping | Commercial geocoding API and mobile mapping SDK |
| Image processing | Server-side resizing and compression |
| Monitoring | Sentry-compatible error monitoring across both tiers; log inspection tooling |
| Scheduling | Laravel scheduled console commands |
| Deployment | Continuous integration driving automated releases with migration and cache management |
| Mobile framework | React Native with React 19 |
| Mobile state | Redux with thunk middleware |
| Mobile navigation | React Navigation (stack, drawer, material top tabs) |
| Mobile offline store | On-device SQLite mirroring the inspection record schema |
| Mobile device features | Mapping, geolocation and distance calculation, camera and gallery capture with cropping, signature capture, connectivity detection, permissions handling |
| Mobile push | Firebase messaging with platform-native notification handling |
Technical Challenges & Solutions
| Challenge | Our Approach |
|---|---|
| No usable connectivity at the point of work | An offline-first mobile client backed by a device-local relational store mirroring the server's inspection schema, so the complete form, photographs and signature can be captured, edited and revisited with no network, then submitted when signal returns — with a browsable queue so nothing is transmitted invisibly. |
| No defensible evidence that a technician attended a property | On-device distance comparison against the property's geocoded position. Within the expected proximity, arrival confirms automatically; outside it, photographic evidence is captured with coordinates and a server timestamp. Every visit produces an evidence record, and no legitimate visit is blocked by imperfect rural geocoding. |
| A long technical form completed outdoors on a phone | The form is sectioned into discrete structured inputs with photograph and signature capture inline, and every intermediate state persists locally — so an interruption, a phone call or a lost connection never costs the technician their work. |
| Reports needing to reach several parties per job, promptly | Server-side PDF generation on submission with the recipient list resolved from the job itself, covering the property owner, the operator's records and the recipients configured for that service area — removing manual assembly and dispatch. |
| The operator's schedule living in spreadsheets | Direct spreadsheet ingestion with normalisation across inconsistent date formats, technician and service-area matching, duplicate detection against existing work, and automatic geocoding of every imported address so mapping and arrival verification work from the moment of import. |
| Wasted journeys to inaccessible properties | A scheduled daily reminder process messaging next-day customers, plus on-demand sends from the field, with number validation, opt-out wording, duplicate-send protection and persisted delivery failures so non-delivery is answerable from data. |
| Serving multiple operator brands from one mobile application | Tenant branding as a server-side data record fetched at launch and applied globally in the client, with all operational data scoped to the tenant at query level — so a new operator brand is configuration rather than a separate build. |
| Media uploads failing on an unreliable path | Per-record storage markers recording where each file actually landed, with automatic fallback to local disk and a read path that branches accordingly, so a storage incident never fails a technician's submission. |
| Deadlines correct across storage and display | Timestamps stored in a single canonical zone with conversion applied at every boundary — creation, editing, spreadsheet import, reminder scheduling and display — so scheduled work and reminders land on the intended day. |
Security & Reliability
Token-based authentication. The mobile API authenticates with OAuth2 bearer tokens, issued at sign-in and revoked at sign-out, with a separate session guard for the administrative platform.
Role separation enforced at the entry point. Field technicians are explicitly excluded from the administrative platform by a role gate at authentication, rather than being merely hidden from its navigation.
Tenant scoping at query level. Jobs, inspections, technicians, service areas and distribution recipients are all scoped to the owning operator tenant, with cross-tenant visibility reserved for platform administration.
Credential handling. Passwords are hashed, and complexity requirements are enforced on change with clear guidance rather than a bare rejection.
Evidence integrity. Arrival confirmations carry coordinates and a server-assigned timestamp rather than a device-supplied one, and inspection submissions are dated server-side.
Auditable messaging. Failed customer messages are persisted with recipient, content, status and provider error, so delivery can be evidenced or explained after the fact.
Graceful degradation. Media upload failures fall back to local disk with a per-record marker rather than failing the write, so an object storage incident does not become a field incident.
No data loss at the edge. Because capture persists locally before any network interaction, a lost connection, a closed application or a dead battery does not lose an inspection.
Production monitoring. Error monitoring is integrated across both the backend and the mobile application, with server log inspection tooling available to administrators.
Scalability & Performance
Tenant-scoped queries. Operational reads are bounded by tenant, technician and status, so the working set stays small as the number of operators and the historical record both grow.
Cached configuration reads. Settings and branding values read on nearly every request are served from a model-level cache rather than the database.
Media offloaded from the application tier. Images are resized and compressed on upload, stored in cloud object storage and served directly from it, keeping large binary traffic away from the application servers — significant in a product where every job carries multiple photographs plus a signature.
Batch messaging on a schedule. Customer reminders run as a scheduled command against the next day's appointments rather than as synchronous sends, so provider latency never affects a user-facing request.
Reads served from the device. Offline job and form data is read from the device's local store, removing a class of network round trips entirely — the application is fast in the field partly because it often is not using the network at all.
Stateless API tier. Token authentication and externalised media storage keep application instances free of local state, so the tier scales horizontally.
Push over polling. Job assignment and reassignment reach technicians by push notification rather than client polling.
Computation at the edge. Distance between technician and property is computed on the device from data it already holds, rather than as a server round trip.
Business Outcomes
- Inspections are captured once, at the property, by the person who did the work — eliminating the re-keying step between the field and the office and the transcription errors that came with it.
- Connectivity stopped being a constraint on the work. Technicians complete full inspections at properties with no signal and submit when convenient, with a visible queue confirming nothing has been lost.
- Every visit now carries evidence. Arrival is verified against the property's location or backed by photographic proof, giving the operator a defensible record when a finding is later questioned.
- Reports reach their recipients automatically. The manual assembly-and-send step is gone; a completed inspection produces and distributes its own document.
- The operator's existing schedule was preserved. Spreadsheet ingestion meant adoption did not require re-entering a forward schedule by hand.
- Wasted journeys are actively reduced through automated next-day customer reminders, with delivery failures logged rather than assumed.
- Dispatch became a back-office action rather than a phone call, with assignment, bulk assignment and reassignment all reaching the technician as a push notification.
- The platform can carry more than one operator brand without a separate application, because branding and data are both tenant-scoped configuration.
Why it worked
Field service software fails in a specific and predictable way. It is designed in an office, where the network works, and it is used in a field, where it does not. The technician's experience of a bad field application is not a degraded one — it is a total one: half an hour of form entry lost, the day's work unrecorded, and a permanent, entirely rational return to paper. There is no recovering from that, because you only get one chance to be trusted with a day's work.
Our team builds for the field as the primary condition rather than the edge case. We put a real relational store on the device, mirroring the server's schema, so a captured inspection is a durable local record that can be edited and revisited, not a fragile pending request. We made the deferred queue something the technician can see and open, because a queue you cannot inspect is a queue you do not trust. And we chose evidence over enforcement on arrival verification — confirming proximity where it exists, requiring a photograph where it does not — because a system that blocks legitimate work at a badly geocoded rural address is a system that gets bypassed.
The same judgement runs through the rest of the delivery: meeting the operator's schedule in the spreadsheets where it already lived, making the report a document that arrives rather than a portal nobody logs into, and making media upload failures degrade quietly instead of surfacing as a submission error to someone standing in a field. Our teams work across Laravel and modern PHP, multi-tenant platform design, offline-first mobile architecture, geospatial and mapping integration, document generation and automated distribution, and messaging and notification infrastructure — with the operational judgement to know which failure modes are inconvenient and which ones end adoption.
Final Summary
An operator inspects treatment systems at properties across a wide rural service area on a recurring cycle, and every inspection has to produce a technical record that reaches the property owner and the operator's designated recipients. That process ran on printed job sheets and handwritten forms, re-keyed in an office, assembled and sent by hand — slow, inconsistent, and with no reliable evidence that any given visit had actually taken place.
Our team built a platform that moves the whole cycle onto one system. Work is scheduled and dispatched from a multi-tenant back office that ingests the operator's existing scheduling spreadsheets directly, geocoding each property so it can be mapped and verified. Technicians receive assignments by push notification and work from a mobile application built offline-first: a device-local relational store mirrors the inspection record, so a full technical form, its photographs and the technician's signature can be captured, edited and revisited at a property with no connectivity at all, then submitted from a visible queue when signal returns. Arrival is verified against the property's position where geocoding permits, and backed by timestamped photographic evidence where it does not — so every visit produces a record without any legitimate visit being blocked.
On submission the platform renders the inspection into a formal report and distributes it automatically to a recipient list derived from the job itself. Around that sit the operational details that decide whether a field product is adopted or abandoned: automated next-day customer reminders with logged delivery failures to cut wasted journeys, media uploads that degrade to local storage rather than failing a technician's submission, tenant-scoped data and server-driven branding so one application can carry more than one operator, and evidence attached to every record. The measure of a field service platform is not what it does when everything works — it is what it does at a property with no signal, in bad weather, with half an hour of work already entered. That is the case it was built for.